Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
src/libopensc/pkcs15-coolkey.c
### 代码内容：
#if HAVE_CONFIG_H
#include "config.h"
#endif

#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <ctype.h>

#include "internal.h"
#include "cardctl.h"
#include "asn1.h"
#include "pkcs15.h"
#include "../pkcs11/pkcs11.h"

typedef struct pdata_st {
	const char *id;
	const char *label;
	const char *path;
	int         ref;
	int         type;
	unsigned int maxlen;
	unsigned int minlen;
	unsigned int storedlen;
	int         flags;
	int         tries_left;
	const unsigned char  pad_char;
	int         obj_flags;
} pindata;

static int coolkey_detect_card(sc_pkcs15_card_t *p15card)
{
	sc_card_t *card = p15card->card;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);
	if (card->type < SC_CARD_TYPE_COOLKEY_GENERIC
		|| card->type >= SC_CARD_TYPE_COOLKEY_GENERIC+1000)
		return SC_ERROR_INVALID_CARD;
	return SC_SUCCESS;
}

static int
coolkey_get_object(sc_card_t *card, unsigned long object_id, sc_cardctl_coolkey_object_t **obj) {
	sc_cardctl_coolkey_find_object_t fobj;
	int r;

	fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_ID;
	fobj.find_id = object_id;
	fobj.obj = NULL;
	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj);
	if (r < 0) {
		return r;
	}
	*obj = fobj.obj;
	return SC_SUCCESS;
}

/*
 * fetch attributes from an object
 */
static int
coolkey_get_attribute(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, const u8 **val, size_t *val_len, u8 *data_type) {
	sc_cardctl_coolkey_attribute_t attribute;
	int r;

	attribute.object = obj;
	attribute.attribute_type = type;

	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &attribute);
	if (r < 0) {
		return r;
	}
	*val = attribute.attribute_value;
	*val_len = attribute.attribute_length;
	if (data_type) {
		*data_type = attribute.attribute_data_type;
	}
	return SC_SUCCESS;
}

static int
coolkey_find_matching_cert(sc_card_t *card, sc_cardctl_coolkey_object_t *in_obj, sc_cardctl_coolkey_object_t **cert_obj) {
	sc_cardctl_coolkey_find_object_t fobj;
	sc_cardctl_coolkey_attribute_t template[2];
	u8 obj_class[4];
	int r;

	/* we are searching for certs .. */
	template[0].attribute_type = CKA_CLASS;
	template[0].attribute_data_type = SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG;
	template[0].attribute_length = sizeof(obj_class);
	template[0].attribute_value = obj_class;
	ulong2bebytes(obj_class, CKO_CERTIFICATE);

	/* fetch the current object's CKA_ID */
	template[1].attribute_type = CKA_ID;
	template[1].object = in_obj;
	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_ATTRIBUTE, &template[1]);
	if (r < 0) {
		return r;
	}
	template[0].object = NULL; /*paranoia */
	template[1].object = NULL; /*paranoia */

	/* now find the cert that has the ID */
	fobj.type = SC_CARDCTL_COOLKEY_FIND_BY_TEMPLATE;
	fobj.obj = NULL;
	fobj.coolkey_template = &template[0];
	fobj.template_count=2;
	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_FIND_OBJECT, &fobj);
	if (r < 0) {
		return r;
	}
	*cert_obj = fobj.obj;
	return SC_SUCCESS;
}

static int
coolkey_get_attribute_ulong(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, CK_ULONG *value)
{
	const u8 *val;
	size_t val_len;
	u8 data_type;
	int r;

	r  = coolkey_get_attribute(card, obj, type, &val, &val_len, &data_type);
	if (r < 0) {
		return r;
	}
	if ((data_type != SC_CARDCTL_COOLKEY_ATTR_TYPE_ULONG) &&
	    (val_len != sizeof(CK_ULONG))) {
		return SC_ERROR_CORRUPTED_DATA;
	}
	*value = bebytes2ulong(val);
	return SC_SUCCESS;
}

static int
coolkey_get_attribute_boolean(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE attr_type)
{
	int r;
	const u8 *val;
	size_t val_len;

	r = coolkey_get_attribute(card, obj, attr_type, &val, &val_len, NULL);
	if (r < 0) {
		/* attribute not valid for this object, set boolean to false */
		return 0;
	}
	if ((val_len == 1) && (*val == 1)) {
		return 1;
	}
	return 0;
}

static int
coolkey_get_attribute_bytes(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 *data, size_t *data_len, size_t max_data_len)
{
	const u8 *val;
	size_t val_len;
	int r;

	r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL);
	if (r < 0) {
		return r;
	}
	if (val_len > max_data_len) {
		val_len = max_data_len;
	}
	memcpy(data, val, val_len);
	*data_len = val_len;
	return SC_SUCCESS;
}

static int
coolkey_get_attribute_bytes_alloc(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, u8 **data, size_t *data_len)
{
	const u8 *val;
	size_t val_len;
	int r;

	r = coolkey_get_attribute(card, obj, type, &val, &val_len, NULL);
	if (r < 0) {
		return r;
	}
	*data = malloc(val_len);
	if (*data == NULL) {
		return SC_ERROR_OUT_OF_MEMORY;
	}
	memcpy(*data, val, val_len);
	*data_len = val_len;
	return SC_SUCCESS;
}

static int
coolkey_get_id(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_id *id)
{
	return coolkey_get_attribute_bytes(card, obj, CKA_ID, id->value , &id->len, sizeof(id->value));
}

/*
 * A number of opensc structure have the same layout, use a common function to fill them
 * int:
 *     structure name      first parameter                 second parameter
 *     sc_lv_data          unsigned char * value           size_t len
 *     sc_pkcs15_data         u8 *data                     size_t data_len
 *     sc_pkcs15_bignum       u8 *data                     size_t len
 *     sc_pkcs15_der          u8 *value	                   size_t len
 *     sc_pkcs15_u8           u8 *value                    size_t len
 *
 * The following can properly assign all of them
 */
int
coolkey_get_attribute_lv(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_ATTRIBUTE_TYPE type, void *ptr)
{
	struct sc_pkcs15_data *item = (struct sc_pkcs15_data *)ptr;
	return coolkey_get_attribute_bytes_alloc(card, obj, type, &item->data, &item->data_len);
}

#define COOLKEY_ID_CERT ((unsigned long)'c')
#define COOLKEY_ID_KEY ((unsigned long)'k')
#define COOLKEY_ID_CERT_DATA ((unsigned long)'C')

static unsigned long
coolkey_get_object_type(unsigned long object_id) { return ((object_id >> 24 ) & 0xff); }

static unsigned long
coolkey_make_new_id(unsigned long object_id, unsigned long id_type)
{ return ((object_id  & 0x00ffffffUL)|(id_type << 24)); }

/*
 * We need cert data to fill in some of our keys. Also, some older tokens store the cert data in a separate
 * object from the rest of the cert attributes. This function handles both of these complications
 */
static int
coolkey_get_certificate(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, struct sc_pkcs15_der *cert)
{
	sc_cardctl_coolkey_object_t *cert_obj;
	unsigned long object_id;
	int r;

	cert_obj = obj;
	if (coolkey_get_object_type(obj->id) != COOLKEY_ID_CERT) {
		r = coolkey_find_matching_cert(card, obj, &cert_obj);
		if (r < 0) {
			return r;
		}
	}
	r = coolkey_get_attribute_lv(card, cert_obj, CKA_VALUE, cert);
	if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
		object_id = coolkey_make_new_id(cert_obj->id, COOLKEY_ID_CERT_DATA);
		r = coolkey_get_object(card, object_id, &cert_obj);
		if (r < 0) {
			return r;
		}
		/* fill in cert data */
		cert->value = malloc(cert_obj->length);
		if (cert->value == NULL) {
			return SC_ERROR_OUT_OF_MEMORY;
		}
		memcpy(cert->value, cert_obj->data, cert_obj->length);
		cert->len = cert_obj->length;
		return SC_SUCCESS;
	}
	return r;
}

struct coolkey_attr_flags {
	CK_ATTRIBUTE_TYPE attribute_type;
	unsigned int pkcs15_flags;
};

static struct coolkey_attr_flags usage_table[] = {
	{ CKA_ENCRYPT,          SC_PKCS15_PRKEY_USAGE_ENCRYPT       },
	{ CKA_DECRYPT,          SC_PKCS15_PRKEY_USAGE_DECRYPT       },
	{ CKA_SIGN,             SC_PKCS15_PRKEY_USAGE_SIGN          },
	{ CKA_SIGN_RECOVER,     SC_PKCS15_PRKEY_USAGE_SIGNRECOVER   },
	{ CKA_WRAP,             SC_PKCS15_PRKEY_USAGE_WRAP          },
	{ CKA_UNWRAP,           SC_PKCS15_PRKEY_USAGE_UNWRAP        },
	{ CKA_VERIFY,           SC_PKCS15_PRKEY_USAGE_VERIFY        },
	{ CKA_VERIFY_RECOVER,   SC_PKCS15_PRKEY_USAGE_VERIFYRECOVER },
	{ CKA_DERIVE,           SC_PKCS15_PRKEY_USAGE_DERIVE        }
};

static struct coolkey_attr_flags access_table[] = {
	{ CKA_SENSITIVE,        SC_PKCS15_PRKEY_ACCESS_SENSITIVE       },
    { CKA_EXTRACTABLE,      SC_PKCS15_PRKEY_ACCESS_EXTRACTABLE     },
    { CKA_ALWAYS_SENSITIVE, SC_PKCS15_PRKEY_ACCESS_ALWAYSSENSITIVE },
    { CKA_NEVER_EXTRACTABLE,SC_PKCS15_PRKEY_ACCESS_NEVEREXTRACTABLE},
    { CKA_LOCAL,            SC_PKCS15_PRKEY_ACCESS_LOCAL           }
};

static struct coolkey_attr_flags flag_table[] = {
	{ CKA_PRIVATE,          SC_PKCS15_CO_FLAG_PRIVATE       },
	{ CKA_MODIFIABLE,       SC_PKCS15_CO_FLAG_MODIFIABLE    }
};

static int usage_table_size = sizeof(usage_table)/sizeof(usage_table[0]);
static int access_table_size = sizeof(access_table)/sizeof(access_table[0]);
static int flag_table_size = sizeof(flag_table)/sizeof(flag_table[0]);

static int
coolkey_set_bool_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flags_ptr, struct coolkey_attr_flags *table, int table_size)
{
	unsigned int flags = 0;
	int i;

	for (i=0; i< table_size; i++) {
		if (coolkey_get_attribute_boolean(card, obj, table[i].attribute_type)) {
			flags |= table[i].pkcs15_flags;
		}
	}
	*flags_ptr = flags;
	return SC_SUCCESS;
}

/* map a cert usage and algorithm to public and private key usages */
static int
coolkey_get_usage(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *usage_ptr)
{
	return coolkey_set_bool_flags(card, obj, usage_ptr, usage_table, usage_table_size);
}

/* map a cert usage and algorithm to public and private key usages */
static int
coolkey_get_flags(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *flag_ptr)
{
	return coolkey_set_bool_flags(card, obj, flag_ptr, flag_table, flag_table_size);
}

static int
coolkey_get_access(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, unsigned int *access_ptr)
{
	return coolkey_set_bool_flags(card, obj, access_ptr, access_table, access_table_size);
}

/*
 * turn a coolkey object into a pkcss 15 pubkey. object should already be type
 * CKO_PUBLIC_KEY */
static sc_pkcs15_pubkey_t *
coolkey_make_public_key(sc_card_t *card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type)
{
	sc_pkcs15_pubkey_t *key;
	int r;

	key = calloc(1, sizeof(struct sc_pkcs15_pubkey));
	if (!key)
		return NULL;
	switch (key_type) {
	case CKK_RSA:
		key->algorithm = SC_ALGORITHM_RSA;
		r = coolkey_get_attribute_lv(card, obj, CKA_MODULUS, &key->u.rsa.modulus);
		if (r != SC_SUCCESS) {
			goto fail;
		}
		r = coolkey_get_attribute_lv(card, obj, CKA_PUBLIC_EXPONENT, &key->u.rsa.exponent);
		if (r != SC_SUCCESS) {
			goto fail;
		}
		break;
	case CKK_EC:
		key->algorithm = SC_ALGORITHM_EC;
		r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_POINT, &key->u.ec.ecpointQ.value, &key->u.ec.ecpointQ.len);
	    if(r < 0) {
			goto fail;
		}
		r = coolkey_get_attribute_bytes_alloc(card, obj, CKA_EC_PARAMS,
				&key->u.ec.params.der.value, &key->u.ec.params.der.len);
		if (r < 0) {
			goto fail;
		}
		r = sc_pkcs15_fix_ec_parameters(card->ctx, &key->u.ec.params);
		if (r < 0) {
			goto fail;
		}
		break;
	}
	return key;
fail:
	sc_pkcs15_free_pubkey(key);

	/* now parse the DER cert */
	return NULL;
}

static sc_pkcs15_pubkey_t *
coolkey_get_public_key_from_certificate(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj)
{
	sc_pkcs15_cert_info_t cert_info;
	sc_pkcs15_cert_t *cert_out = NULL;
	sc_pkcs15_pubkey_t *key = NULL;
	int r;

	cert_info.value.value = NULL;
	r = coolkey_get_certificate(p15card->card, obj, &cert_info.value);
	if (r < 0) {
		goto fail;
	}
	r = sc_pkcs15_read_certificate(p15card, &cert_info, &cert_out);
	if (r < 0) {
		goto fail;
	}
	key = cert_out->key;
	cert_out->key = NULL; /* adopt the key from the cert */
fail:
	if (cert_out) {
		sc_pkcs15_free_certificate(cert_out);
	}
	if (cert_info.value.value) {
		free(cert_info.value.value);
	}
	return key;
}

static sc_pkcs15_pubkey_t *
coolkey_get_public_key(sc_pkcs15_card_t *p15card, sc_cardctl_coolkey_object_t *obj, CK_KEY_TYPE key_type)
{
	sc_pkcs15_pubkey_t *key;

	key = coolkey_make_public_key(p15card->card, obj, key_type);
	if (key) {
		return key;
	}
	return coolkey_get_public_key_from_certificate(p15card, obj);
}

static int sc_pkcs15emu_coolkey_init(sc_pkcs15_card_t *p15card)
{
	static const pindata pins[] = {
		{ "1", NULL, "", 0x00,
		  SC_PKCS15_PIN_TYPE_ASCII_NUMERIC,
		  32, 4, 32,
		  SC_PKCS15_PIN_FLAG_INITIALIZED,
		  -1, 0xFF,
		  SC_PKCS15_CO_FLAG_PRIVATE },
		{ NULL, NULL, NULL, 0, 0, 0, 0, 0, 0, 0, 0, 0}
	};

	/*
	 * The size of the key or the algid is not really known
	 * but can be derived from the certificates.
	 * the cert, pubkey and privkey are a set.
	 * Key usages bits taken from certificate key usage extension.
	 */

	int    r, i;
	sc_card_t *card = p15card->card;
	sc_serial_number_t serial;
	int count;
	struct sc_pkcs15_object *obj;

	SC_FUNC_CALLED(card->ctx, SC_LOG_DEBUG_VERBOSE);

	memset(&serial, 0, sizeof(serial));

	/* coolkey caches a nonce once it logs in, don't keep the pin around. The card will
	 * stay logged in until it's been pulled from the reader, in which case you want to reauthenticate
	 * anyway */
	p15card->opts.use_pin_cache = 0;

	/* get the token info from the card */
	r = sc_card_ctl(card, SC_CARDCTL_COOLKEY_GET_TOKEN_INFO, p15card->tokeninfo);
	if (r < 0) {
		/* put some defaults in if we didn't succeed */
		set_string(&p15card->tokeninfo->label, "Coolkey");
		set_string(&p15card->tokeninfo->manufacturer_id, "Unknown");
		set_string(&p15card->tokeninfo->serial_number, "00000000");
	}

	/* set pins */
	sc_log(card->ctx,  "Coolkey adding pins...");
	for (i = 0; pins[i].id; i++) {
		struct sc_pkcs15_auth_info pin_info;
		struct sc_pkcs15_object   pin_obj;
		const char * label;

		memset(&pin_info, 0, sizeof(pin_info));
		memset(&pin_obj,  0, sizeof(pin_obj));

		pin_info.auth_type = SC_PKCS15_PIN_AUTH_TYPE_PIN;
		sc_pkcs15_format_id(pins[i].id, &pin_info.auth_id);
		pin_info.attrs.pin.reference     = pins[i].ref;
		pin_info.attrs.pin.flags         = pins[i].flags;
		pin_info.attrs.pin.type          = pins[i].type;
		pin_info.attrs.pin.min_length    = pins[i].minlen;
		pin_info.attrs.pin.stored_length = pins[i].storedlen;
		pin_info.attrs.pin.max_length    = pins[i].maxlen;
		pin_info.attrs.pin.pad_char      = pins[i].pad_char;
		sc_format_path(pins[i].path, &pin_info.path);
		pin_info.tries_left    = -1;

		label = pins[i].label? pins[i].label : p15card->tokeninfo->label;
		sc_log(card->ctx,  "Coolkey Adding pin %d label=%s",i, label);
		strncpy(pin_obj.label, label, SC_PKCS15_MAX_LABEL_SIZE - 1);
		pin_obj.flags = pins[i].obj_flags;

		r = sc_pkcs15emu_add_pin_obj(p15card, &pin_obj, &pin_info);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, r);
	}

	/* set other objects */
	r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_INIT_GET_OBJECTS, &count);
	LOG_TEST_RET(card->ctx, r, "Can not initiate objects.");

	sc_log(card->ctx,  "Iterating over %d objects", count);
	for (i = 0; i < count; i++) {
		struct sc_cardctl_coolkey_object     coolkey_obj;
		struct sc_pkcs15_object    obj_obj;
		struct sc_pkcs15_cert_info cert_info;
		struct sc_pkcs15_pubkey_info pubkey_info;
		struct sc_pkcs15_prkey_info prkey_info;
		sc_pkcs15_pubkey_t *key = NULL;
		void *obj_info = NULL;
		int obj_type = 0;
		CK_KEY_TYPE key_type;
		CK_OBJECT_CLASS obj_class;
		size_t len;

		r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_GET_NEXT_OBJECT, &coolkey_obj);
		if (r < 0)
			LOG_FUNC_RETURN(card->ctx, r);

		sc_log(card->ctx, "Loading object %d", i);
		memset(&obj_obj, 0, sizeof(obj_obj));
		/* coolkey applets have label only on the certificates,
		 * but we should copy it also to the keys matching the same ID */
		coolkey_get_attribute_bytes(card, &coolkey_obj, CKA_LABEL, (u8 *)obj_obj.label, &len, sizeof(obj_obj.label));
		coolkey_get_flags(card, &coolkey_obj, &obj_obj.flags);
		if (obj_obj.flags & SC_PKCS15_CO_FLAG_PRIVATE) {
			sc_pkcs15_format_id(pins[0].id, &obj_obj.auth_id);
		}

		r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_CLASS, &obj_class);
		if (r < 0) {
			goto fail;
		}
		switch (obj_class) {
		case CKO_PRIVATE_KEY:
			sc_log(card->ctx, "Processing private key object %d", i);
			r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type);
			/* default to CKK_RSA */
			if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
				key_type = CKK_RSA;
				r = SC_SUCCESS;
			}
			if (r < 0) {
				goto fail;
			}
			/* set the info values */
			obj_info = &prkey_info;
			memset(&prkey_info, 0, sizeof(prkey_info));
			coolkey_get_id(card, &coolkey_obj, &prkey_info.id);
			prkey_info.path = coolkey_obj.path;
			prkey_info.key_reference = coolkey_obj.id;
			prkey_info.native = 1;
			coolkey_get_usage(card, &coolkey_obj, &prkey_info.usage);
			coolkey_get_access(card, &coolkey_obj, &prkey_info.access_flags);
			key = coolkey_get_public_key(p15card, &coolkey_obj, key_type);
			if (key_type == CKK_RSA) {
				obj_type = SC_PKCS15_TYPE_PRKEY_RSA;
				if (key) {
					prkey_info.modulus_length = key->u.rsa.modulus.len*8;
				}
			} else if (key_type == CKK_EC) {
				obj_type = SC_PKCS15_TYPE_PRKEY_EC;
				if (key) {
					prkey_info.field_length = key->u.ec.params.field_length;
				}
			} else {
				goto fail;
			}
			break;

		case CKO_PUBLIC_KEY:
			sc_log(card->ctx, "Processing public key object %d", i);
			r = coolkey_get_attribute_ulong(card, &coolkey_obj, CKA_KEY_TYPE, &key_type);
			/* default to CKK_RSA */
			if (r == SC_ERROR_DATA_OBJECT_NOT_FOUND) {
				key_type = CKK_RSA;
				r = SC_SUCCESS;
			}
			if (r < 0) {
				goto fail;
			}
			key = coolkey_get_public_key(p15card, &coolkey_obj, key_type);
			if (key == NULL) {
				goto fail;
			}
			/* set the info values */
			obj_info = &pubkey_info;
			memset(&pubkey_info, 0, sizeof(pubkey_info));
			r = sc_pkcs15_encode_pubkey_as_spki(card->ctx, key, &pubkey_info.direct.spki.value,
				&pubkey_info.direct.spki.len);
			if (r < 0)
				goto fail;
			coolkey_get_id(card, &coolkey_obj, &pubkey_info.id);
			pubkey_info.path = coolkey_obj.path;
			pubkey_info.native = 1;
			pubkey_info.key_reference = coolkey_obj.id;
			coolkey_get_usage(card, &coolkey_obj, &pubkey_info.usage);
			coolkey_get_access(card, &coolkey_obj, &pubkey_info.access_flags);
			if (key_type == CKK_RSA) {
				obj_type = SC_PKCS15_TYPE_PUBKEY_RSA;
				pubkey_info.modulus_length = key->u.rsa.modulus.len*8;
			} else if (key_type == CKK_EC) {
				obj_type = SC_PKCS15_TYPE_PUBKEY_EC;
				pubkey_info.field_length = key->u.ec.params.field_length;
			} else {
				goto fail;
			}
			/* set the obj values */
			obj_obj.emulated = key;
			key = NULL;
			break;

		case CKO_CERTIFICATE:
			sc_log(card->ctx, "Processing certificate object %d", i);
			obj_info = &cert_info;
			memset(&cert_info, 0, sizeof(cert_info));
			coolkey_get_id(card, &coolkey_obj, &cert_info.id);
			cert_info.path = coolkey_obj.path;
			obj_type = SC_PKCS15_TYPE_CERT_X509;

			/* following will find the cached cert in cert_info */
			r = coolkey_get_certificate(card, &coolkey_obj, &cert_info.value);
			if (r < 0) {
				goto fail;
			}
			break;

		default:
			/* no other recognized types which are stored 'on card' */
			sc_log(card->ctx, "Unknown object type %lu, skipping", obj_class);
			continue;
		}

		r = sc_pkcs15emu_object_add(p15card, obj_type, &obj_obj, obj_info);
		if (r != SC_SUCCESS)
			sc_log(card->ctx, "sc_pkcs15emu_object_add() returned %d", r);
fail:
		if (key) { sc_pkcs15_free_pubkey(key); }

	}
	r = (card->ops->card_ctl)(card, SC_CARDCTL_COOLKEY_FINAL_GET_OBJECTS, &count);
	LOG_TEST_RET(card->ctx, r, "Can not finalize objects.");

	/* Iterate over all the created objects and fill missing labels */
	for (obj = p15card->obj_list; obj != NULL; obj = obj->next) {
		struct sc_pkcs15_id *id = NULL;
		struct sc_pkcs15_object *cert_object;

		/* label non-empty -- do not overwrite */
		if (obj->label[0] != '\0')
			continue;

		switch (obj->type & SC_PKCS15_TYPE_CLASS_MASK) {
		case SC_PKCS15_TYPE_PUBKEY:
			id = &((struct sc_pkcs15_pubkey_info *)obj->data)->id;
			break;
		case SC_PKCS15_TYPE_PRKEY:
			id = &((struct sc_pkcs15_prkey_info *)obj->data)->id;
			break;
		default:
			/* We do not care about other objects */
			continue;
		}
		r = sc_pkcs15_find_cert_by_id(p15card, id, &cert_object);
		if (r != 0)
			continue;

		sc_log(card->ctx, "Copy label \"%s\" from cert to key object",
			cert_object->label);
		memcpy(obj->label, cert_object->label, SC_PKCS15_MAX_LABEL_SIZE);
	}

	LOG_FUNC_RETURN(card->ctx, SC_SUCCESS);
}

int
sc_pkcs15emu_coolkey_init_ex(sc_pkcs15_card_t *p15card,
		struct sc_aid *aid)
{
	sc_card_t      *card = p15card->card;
	sc_context_t    *ctx = card->ctx;
	int rv;

	LOG_FUNC_CALLED(ctx);

	rv = coolkey_detect_card(p15card);
	if (rv)
		LOG_FUNC_RETURN(ctx, SC_ERROR_WRONG_CARD);
	rv = sc_pkcs15emu_coolkey_init(p15card);

	LOG_FUNC_RETURN(ctx, rv);
}

Output:
【漏洞类型】未初始化内存使用
【严重性】中等
【漏洞摘要】在`coolkey_get_public_key_from_certificate`函数中，`struct sc_pkcs15_cert_info`变量`cert_info`的部分成员未被初始化。虽然`cert_info.value.value`被显式设置为`NULL`，但`cert_info.value.len`和`cert_info.value.type`等其他成员可能仍然包含未定义的值。这可能导致在后续处理中使用这些未初始化的值，从而引发潜在的安全问题，如访问控制错误或内存损坏。
【关键代码模式】
- `struct sc_pkcs15_cert_info`结构体的使用。
- `memset`函数的使用情况。
- `coolkey_get_certificate`函数的调用。
- `sc_pkcs15_read_certificate`函数的调用。
【修复方法说明】通过调用`memset(&cert_info, 0, sizeof(cert_info));`来初始化`struct sc_pkcs15_cert_info`结构体的所有成员，从而避免使用未初始化的内存。这一修复方法遵循了初始化所有变量的最佳实践。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
src/net/sourceforge/plantuml/ugraphic/UImageSvg.java
### 代码内容：
package net.sourceforge.plantuml.ugraphic;

import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import net.sourceforge.plantuml.SignatureUtils;

public class UImageSvg implements UShape {

	private static final String EMPTY_SVG = "<svg width=10 height=10></svg>";
	private final String svg;
	private final double scale;

	public UImageSvg(String svg, double scale) {
		this.svg = clean(Objects.requireNonNull(svg));
		this.scale = scale;
	}

	private String clean(String svg) {
		svg = svg.toLowerCase().replaceAll("\\s", "");
		if (svg.contains("<script>"))
			return EMPTY_SVG;
		if (svg.contains("</script>"))
			return EMPTY_SVG;
		if (svg.contains("<foreignobject"))
			return EMPTY_SVG;
		if (svg.contains("</foreignobject>"))
			return EMPTY_SVG;
		return svg;
	}

	public String getMD5Hex() {
		return SignatureUtils.getMD5Hex(svg);
	}

	public String getSvg(boolean raw) {
		String result = svg;
		if (raw) {
			return result;
		}
		if (result.startsWith("<?xml")) {
			final int idx = result.indexOf("<svg");
			result = result.substring(idx);
		}
		if (result.startsWith("<svg")) {
			final int idx = result.indexOf(">");
			result = "<svg>" + result.substring(idx + 1);
		}
		final String style = extractSvgStyle();
		if (style != null) {
			final String background = extractBackground(style);
			if (background != null) {
				result = result.replaceFirst("<g>", "<g><rect fill=\"" + background + "\" style=\"" + style + "\" /> ");
			}
		}
		if (result.startsWith("<svg>") == false) {
			throw new IllegalArgumentException();
		}
		return result;
	}

	private String extractBackground(String style) {
		final Pattern p = Pattern.compile("background:([^;]+)");
		final Matcher m = p.matcher(style);
		if (m.find()) {
			return m.group(1);
		}
		return null;
	}

	private String extractSvgStyle() {
		final Pattern p = Pattern.compile("(?i)\\<svg[^>]+style=\"([^\">]+)\"");
		final Matcher m = p.matcher(svg);
		if (m.find()) {
			return m.group(1);
		}
		return null;
	}

	public int getData(String name) {
		final Pattern p = Pattern.compile("(?i)<svg[^>]+" + name + "\\W+(\\d+)");
		final Matcher m = p.matcher(svg);
		if (m.find()) {
			final String s = m.group(1);
			return Integer.parseInt(s);
		}
		final Pattern p2 = Pattern.compile("viewBox[= \"\']+([0-9.]+)[\\s,]+([0-9.]+)[\\s,]+([0-9.]+)[\\s,]+([0-9.]+)");
		final Matcher m2 = p2.matcher(svg);
		if (m2.find()) {
			if ("width".equals(name)) {
				final String s = m2.group(3);
				final int width = (int) Double.parseDouble(s);
				return width;
			}
			if ("height".equals(name)) {
				final String s = m2.group(4);
				final int result = (int) Double.parseDouble(s);
				return result;
			}
		}

		throw new IllegalStateException("Cannot find " + name);
	}

	public int getHeight() {
		return this.getData("height");
	}

	public int getWidth() {
		return this.getData("width");
	}

	public double getScale() {
		return scale;
	}

}

Output:
【漏洞类型】安全审计与预防
【严重性】Medium
【漏洞摘要】原始代码试图通过清理SVG字符串来防止潜在的安全问题，如脚本注入和外部对象注入。然而，其清理方法过于简单，只检查了几个关键字（如`<script>`、`</script>`、`<foreignobject>`和`</foreignobject>`），并未对SVG的其他潜在安全问题进行有效的过滤。例如，SVG中可能包含`<use>`标签，该标签可以引用外部资源，从而引发路径遍历或外部实体注入攻击。此外，`getData`方法中使用了正则表达式来提取SVG中的数据，但这些正则表达式并未进行充分的验证，可能导致潜在的输入解析错误，进而被恶意利用。
【关键代码模式】`clean`函数中的字符串替换和关键字检查、`getData`方法中的正则表达式匹配。
【修复方法说明】修复方法通过引入一个新的变量`svg2`来存储清理后的SVG字符串，从而防止了原始`svg`字符串在后续操作中被修改。这虽然增强了代码的可读性和安全性，但并未完全解决SVG中的潜在安全问题。最佳实践是使用专门的SVG解析库来验证和清理SVG内容，确保其不包含恶意脚本或外部资源引用。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
server/src/main/java/com/thoughtworks/go/server/controller/ArtifactsController.java
### 代码内容：
package com.thoughtworks.go.server.controller;

import com.thoughtworks.go.domain.ConsoleConsumer;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.domain.exception.IllegalArtifactLocationException;
import com.thoughtworks.go.server.cache.ZipArtifactCache;
import com.thoughtworks.go.server.dao.JobInstanceDao;
import com.thoughtworks.go.server.security.HeaderConstraint;
import com.thoughtworks.go.server.service.ArtifactsService;
import com.thoughtworks.go.server.service.ConsoleActivityMonitor;
import com.thoughtworks.go.server.service.ConsoleService;
import com.thoughtworks.go.server.service.RestfulService;
import com.thoughtworks.go.server.util.ErrorHandler;
import com.thoughtworks.go.server.view.artifacts.ArtifactsView;
import com.thoughtworks.go.server.view.artifacts.LocalArtifactsView;
import com.thoughtworks.go.server.web.ArtifactFolderViewFactory;
import com.thoughtworks.go.server.web.FileModelAndView;
import com.thoughtworks.go.server.web.ResponseCodeView;
import com.thoughtworks.go.util.ArtifactLogUtil;
import com.thoughtworks.go.util.SystemEnvironment;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import static com.thoughtworks.go.server.web.ZipArtifactFolderViewFactory.zipViewFactory;
import static com.thoughtworks.go.util.ArtifactLogUtil.isConsoleOutput;
import static com.thoughtworks.go.util.GoConstants.*;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;

@Controller
public class ArtifactsController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactsController.class);

    private final JobInstanceDao jobInstanceDao;
    private final ConsoleActivityMonitor consoleActivityMonitor;
    private final ArtifactFolderViewFactory folderViewFactory;
    private final ArtifactFolderViewFactory jsonViewFactory;
    private final ArtifactFolderViewFactory zipViewFactory;
    private final Charset consoleLogCharset;
    private ArtifactsService artifactsService;
    private RestfulService restfulService;
    private ConsoleService consoleService;
    private HeaderConstraint headerConstraint;

    @Autowired
    ArtifactsController(ArtifactsService artifactsService, RestfulService restfulService, ZipArtifactCache zipArtifactCache, JobInstanceDao jobInstanceDao,
                        ConsoleActivityMonitor consoleActivityMonitor, ConsoleService consoleService, SystemEnvironment systemEnvironment) {
        this.artifactsService = artifactsService;
        this.restfulService = restfulService;
        this.jobInstanceDao = jobInstanceDao;
        this.consoleActivityMonitor = consoleActivityMonitor;
        this.consoleService = consoleService;

        this.folderViewFactory = FileModelAndView.htmlViewFactory();
        this.jsonViewFactory = FileModelAndView.jsonViewfactory();
        this.zipViewFactory = zipViewFactory(zipArtifactCache);
        this.headerConstraint = new HeaderConstraint(systemEnvironment);
        this.consoleLogCharset = systemEnvironment.consoleLogCharsetAsCharset();
    }

    /* RESTful URLs */
    @RequestMapping(value = "/repository/restful/artifact/GET/html", method = RequestMethod.GET)
    public ModelAndView getArtifactAsHtml(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha,
                                          @RequestParam(value = "serverAlias", required = false) String serverAlias) throws Exception {
        return getArtifact(filePath, folderViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, serverAlias);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/json", method = RequestMethod.GET)
    public ModelAndView getArtifactAsJson(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        return getArtifact(filePath, jsonViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/zip", method = RequestMethod.GET)
    public ModelAndView getArtifactAsZip(@RequestParam("pipelineName") String pipelineName,
                                         @RequestParam("pipelineCounter") String pipelineCounter,
                                         @RequestParam("stageName") String stageName,
                                         @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                         @RequestParam("buildName") String buildName,
                                         @RequestParam("filePath") String filePath,
                                         @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        if (filePath.equals(".zip")) {
            filePath = "./.zip";
        }
        return getArtifact(filePath, zipViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/*", method = RequestMethod.GET)
    public void fetch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.getRequestDispatcher("/repository/restful/artifact/GET/html").forward(request, response);
    }

    @RequestMapping(value = "/repository/restful/artifact/POST/*", method = RequestMethod.POST)
    public ModelAndView postArtifact(@RequestParam("pipelineName") String pipelineName,
                                     @RequestParam("pipelineCounter") String pipelineCounter,
                                     @RequestParam("stageName") String stageName,
                                     @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                     @RequestParam("buildName") String buildName,
                                     @RequestParam(value = "buildId", required = false) Long buildId,
                                     @RequestParam("filePath") String filePath,
                                     @RequestParam(value = "attempt", required = false) Integer attempt,
                                     MultipartHttpServletRequest request) throws Exception {
        JobIdentifier jobIdentifier;
        if (!headerConstraint.isSatisfied(request)) {
            return ResponseCodeView.create(HttpServletResponse.SC_BAD_REQUEST, "Missing required header 'Confirm'");
        }
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName);
        }

        int convertedAttempt = attempt == null ? 1 : attempt;

        try {
            File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
            if (artifact.exists() && artifact.isFile()) {
                return FileModelAndView.fileAlreadyExists(filePath);
            }

            MultipartFile multipartFile = multipartFile(request);
            if (multipartFile == null) {
                return FileModelAndView.invalidUploadRequest();
            }

            boolean success = saveFile(convertedAttempt, artifact, multipartFile, shouldUnzipStream(multipartFile));

            if (!success) {
                return FileModelAndView.errorSavingFile(filePath);
            }

            success = updateChecksumFile(request, jobIdentifier, filePath);

            if (!success) {
                return FileModelAndView.errorSavingChecksumFile(filePath);
            }

            return FileModelAndView.fileCreated(filePath);

        } catch (IllegalArtifactLocationException e) {
            return FileModelAndView.forbiddenUrl(filePath);
        }
    }

    private boolean updateChecksumFile(MultipartHttpServletRequest request, JobIdentifier jobIdentifier, String filePath) throws IOException, IllegalArtifactLocationException {
        MultipartFile checksumMultipartFile = getChecksumFile(request);
        if (checksumMultipartFile != null) {
            String checksumFilePath = String.format("%s/%s/%s", artifactsService.findArtifactRoot(jobIdentifier), ArtifactLogUtil.CRUISE_OUTPUT_FOLDER, ArtifactLogUtil.MD5_CHECKSUM_FILENAME);
            File checksumFile = artifactsService.getArtifactLocation(checksumFilePath);
            synchronized (checksumFilePath.intern()) {
                return artifactsService.saveOrAppendFile(checksumFile, checksumMultipartFile.getInputStream());
            }
        } else {
            LOGGER.warn("[Artifacts Upload] Checksum file not uploaded for artifact at path '{}'", filePath);
        }
        return true;
    }

    private boolean saveFile(int convertedAttempt, File artifact, MultipartFile multipartFile, boolean shouldUnzip) throws IOException {
        try (InputStream inputStream = multipartFile.getInputStream()) {
            return artifactsService.saveFile(artifact, inputStream, shouldUnzip, convertedAttempt);
        }
    }

    @RequestMapping(value = "/repository/restful/artifact/PUT/*", method = RequestMethod.PUT)
    public ModelAndView putArtifact(@RequestParam("pipelineName") String pipelineName,
                                    @RequestParam("pipelineCounter") String pipelineCounter,
                                    @RequestParam("stageName") String stageName,
                                    @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                    @RequestParam("buildName") String buildName,
                                    @RequestParam(value = "buildId", required = false) Long buildId,
                                    @RequestParam("filePath") String filePath,
                                    @RequestParam(value = "agentId", required = false) String agentId,
                                    HttpServletRequest request
    ) throws Exception {
        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        JobIdentifier jobIdentifier;
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }

        if (isConsoleOutput(filePath)) {
            return putConsoleOutput(jobIdentifier, request.getInputStream());
        } else {
            return putArtifact(jobIdentifier, filePath, request.getInputStream());
        }
    }

    /* Other URLs */

    @RequestMapping(value = "/**/consoleout.json", method = RequestMethod.GET)
    public ModelAndView consoleout(@RequestParam("pipelineName") String pipelineName,
                                   @RequestParam("pipelineCounter") String pipelineCounter,
                                   @RequestParam("stageName") String stageName,
                                   @RequestParam("buildName") String buildName,
                                   @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                   @RequestParam(value = "startLineNumber", required = false) Long start
    ) {
        start = start == null ? 0L : start;

        try {
            JobIdentifier identifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
            if (jobInstanceDao.isJobCompleted(identifier) && !consoleService.doesLogExist(identifier)) {
                return logsNotFound(identifier);
            }
            ConsoleConsumer streamer = consoleService.getStreamer(start, identifier);
            return new ModelAndView(new ConsoleOutView(streamer, consoleLogCharset));
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }
    }

    @ErrorHandler
    public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response, Exception e) {
        LOGGER.error("Error loading artifacts: ", e);
        Map model = new HashMap();
        model.put(ERROR_FOR_PAGE, "Artifact does not exist.");
        return new ModelAndView("exceptions_page", model);
    }

    ModelAndView getArtifact(String filePath, ArtifactFolderViewFactory folderViewFactory, String pipelineName, String counterOrLabel, String stageName, String stageCounter, String buildName, String sha, String serverAlias) throws Exception {
        LOGGER.info("[Artifact Download] Trying to resolve '{}' for '{}/{}/{}/{}/{}'", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        long before = System.currentTimeMillis();
        ArtifactsView view;
        //Work out the job that we are trying to retrieve
        JobIdentifier translatedId;
        try {
            translatedId = restfulService.findJob(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        } catch (Exception e) {
            return buildNotFound(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        }

        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        view = new LocalArtifactsView(folderViewFactory, artifactsService, translatedId, consoleService);

        ModelAndView createdView = view.createView(filePath, sha);
        LOGGER.info("[Artifact Download] Successfully resolved '{}' for '{}/{}/{}/{}/{}'. It took: {}ms", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName, System.currentTimeMillis() - before);
        return createdView;
    }

    private boolean shouldUnzipStream(MultipartFile multipartFile) {
        return multipartFile.getName().equals(ZIP_MULTIPART_FILENAME);
    }

    private MultipartFile multipartFile(MultipartHttpServletRequest request) {
        MultipartFile multipartFile = request.getFile(REGULAR_MULTIPART_FILENAME);
        if (multipartFile == null) {
            multipartFile = request.getFile(ZIP_MULTIPART_FILENAME);
        }
        return multipartFile;
    }

    private MultipartFile getChecksumFile(MultipartHttpServletRequest request) {
        return request.getFile(CHECKSUM_MULTIPART_FILENAME);
    }

    private ModelAndView putConsoleOutput(final JobIdentifier jobIdentifier, final InputStream inputStream) throws Exception {
        File consoleLogFile = consoleService.consoleLogFile(jobIdentifier);
        boolean updated = consoleService.updateConsoleLog(consoleLogFile, inputStream);
        if (updated) {
            consoleActivityMonitor.consoleUpdatedFor(jobIdentifier);
            return FileModelAndView.fileAppended(consoleLogFile.getPath());
        } else {
            return FileModelAndView.errorSavingFile(consoleLogFile.getPath());
        }
    }

    private ModelAndView putArtifact(JobIdentifier jobIdentifier, String filePath,
                                     InputStream inputStream) throws Exception {
        File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
        if (artifactsService.saveOrAppendFile(artifact, inputStream)) {
            return FileModelAndView.fileAppended(filePath);
        } else {
            return FileModelAndView.errorSavingFile(filePath);
        }
    }

    private ModelAndView buildNotFound(String pipelineName, String counterOrLabel, String stageName,
                                       String stageCounter,
                                       String buildName) {
        return ResponseCodeView.create(SC_NOT_FOUND, String.format("Job %s/%s/%s/%s/%s not found.", pipelineName,
                counterOrLabel, stageName, stageCounter, buildName));
    }

    private ModelAndView logsNotFound(JobIdentifier identifier) {
        String notFound = String.format("Console log for %s is unavailable as it may have been purged by Go or deleted externally.", identifier.toFullString());
        return ResponseCodeView.create(SC_NOT_FOUND, notFound);
    }
}

Output:
【漏洞类型】路径遍历漏洞
【严重性】中等
【漏洞摘要】此代码在处理用户输入的文件路径时，未对路径进行适当的验证，允许用户通过构造特殊的文件路径参数来访问或操作服务器上的任意文件。具体表现为在`getArtifact`和`putArtifact`方法中，用户输入的`filePath`参数未经验证直接用于创建文件对象，从而可能导致路径遍历攻击。根本原因在于开发者没有意识到路径验证的重要性，或者没有正确地应用路径验证机制。
【关键代码模式】`filePath.contains("..")` 和 `artifactsService.findArtifact(jobIdentifier, filePath)`。
【修复方法说明】修复方法通过添加`isValidStageCounter`函数来验证`stageCounter`参数，确保其为正整数或为空。这虽然没有直接解决路径遍历漏洞，但展示了开发者对输入验证的重视，防止了潜在的不安全输入。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
server/src/main/java/com/thoughtworks/go/server/controller/ArtifactsController.java
### 代码内容：
package com.thoughtworks.go.server.controller;

import com.thoughtworks.go.domain.ConsoleConsumer;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.domain.StageIdentifier;
import com.thoughtworks.go.domain.exception.IllegalArtifactLocationException;
import com.thoughtworks.go.server.cache.ZipArtifactCache;
import com.thoughtworks.go.server.dao.JobInstanceDao;
import com.thoughtworks.go.server.security.HeaderConstraint;
import com.thoughtworks.go.server.service.ArtifactsService;
import com.thoughtworks.go.server.service.ConsoleActivityMonitor;
import com.thoughtworks.go.server.service.ConsoleService;
import com.thoughtworks.go.server.service.RestfulService;
import com.thoughtworks.go.server.util.ErrorHandler;
import com.thoughtworks.go.server.view.artifacts.ArtifactsView;
import com.thoughtworks.go.server.view.artifacts.LocalArtifactsView;
import com.thoughtworks.go.server.web.ArtifactFolderViewFactory;
import com.thoughtworks.go.server.web.FileModelAndView;
import com.thoughtworks.go.server.web.ResponseCodeView;
import com.thoughtworks.go.util.ArtifactLogUtil;
import com.thoughtworks.go.util.SystemEnvironment;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import static com.thoughtworks.go.server.web.ZipArtifactFolderViewFactory.zipViewFactory;
import static com.thoughtworks.go.util.ArtifactLogUtil.isConsoleOutput;
import static com.thoughtworks.go.util.GoConstants.*;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;

@Controller
public class ArtifactsController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactsController.class);

    private final JobInstanceDao jobInstanceDao;
    private final ConsoleActivityMonitor consoleActivityMonitor;
    private final ArtifactFolderViewFactory folderViewFactory;
    private final ArtifactFolderViewFactory jsonViewFactory;
    private final ArtifactFolderViewFactory zipViewFactory;
    private final Charset consoleLogCharset;
    private ArtifactsService artifactsService;
    private RestfulService restfulService;
    private ConsoleService consoleService;
    private HeaderConstraint headerConstraint;

    @Autowired
    ArtifactsController(ArtifactsService artifactsService, RestfulService restfulService, ZipArtifactCache zipArtifactCache, JobInstanceDao jobInstanceDao,
                        ConsoleActivityMonitor consoleActivityMonitor, ConsoleService consoleService, SystemEnvironment systemEnvironment) {
        this.artifactsService = artifactsService;
        this.restfulService = restfulService;
        this.jobInstanceDao = jobInstanceDao;
        this.consoleActivityMonitor = consoleActivityMonitor;
        this.consoleService = consoleService;

        this.folderViewFactory = FileModelAndView.htmlViewFactory();
        this.jsonViewFactory = FileModelAndView.jsonViewfactory();
        this.zipViewFactory = zipViewFactory(zipArtifactCache);
        this.headerConstraint = new HeaderConstraint(systemEnvironment);
        this.consoleLogCharset = systemEnvironment.consoleLogCharsetAsCharset();
    }

    /* RESTful URLs */
    @RequestMapping(value = "/repository/restful/artifact/GET/html", method = RequestMethod.GET)
    public ModelAndView getArtifactAsHtml(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha,
                                          @RequestParam(value = "serverAlias", required = false) String serverAlias) throws Exception {
        return getArtifact(filePath, folderViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, serverAlias);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/json", method = RequestMethod.GET)
    public ModelAndView getArtifactAsJson(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        return getArtifact(filePath, jsonViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/zip", method = RequestMethod.GET)
    public ModelAndView getArtifactAsZip(@RequestParam("pipelineName") String pipelineName,
                                         @RequestParam("pipelineCounter") String pipelineCounter,
                                         @RequestParam("stageName") String stageName,
                                         @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                         @RequestParam("buildName") String buildName,
                                         @RequestParam("filePath") String filePath,
                                         @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        if (filePath.equals(".zip")) {
            filePath = "./.zip";
        }
        return getArtifact(filePath, zipViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/*", method = RequestMethod.GET)
    public void fetch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.getRequestDispatcher("/repository/restful/artifact/GET/html").forward(request, response);
    }

    @RequestMapping(value = "/repository/restful/artifact/POST/*", method = RequestMethod.POST)
    public ModelAndView postArtifact(@RequestParam("pipelineName") String pipelineName,
                                     @RequestParam("pipelineCounter") String pipelineCounter,
                                     @RequestParam("stageName") String stageName,
                                     @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                     @RequestParam("buildName") String buildName,
                                     @RequestParam(value = "buildId", required = false) Long buildId,
                                     @RequestParam("filePath") String filePath,
                                     @RequestParam(value = "attempt", required = false) Integer attempt,
                                     MultipartHttpServletRequest request) throws Exception {
        JobIdentifier jobIdentifier;
        if (!headerConstraint.isSatisfied(request)) {
            return ResponseCodeView.create(HttpServletResponse.SC_BAD_REQUEST, "Missing required header 'Confirm'");
        }
        if (!isValidStageCounter(stageCounter)) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName);
        }

        int convertedAttempt = attempt == null ? 1 : attempt;

        try {
            File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
            if (artifact.exists() && artifact.isFile()) {
                return FileModelAndView.fileAlreadyExists(filePath);
            }

            MultipartFile multipartFile = multipartFile(request);
            if (multipartFile == null) {
                return FileModelAndView.invalidUploadRequest();
            }

            boolean success = saveFile(convertedAttempt, artifact, multipartFile, shouldUnzipStream(multipartFile));

            if (!success) {
                return FileModelAndView.errorSavingFile(filePath);
            }

            success = updateChecksumFile(request, jobIdentifier, filePath);

            if (!success) {
                return FileModelAndView.errorSavingChecksumFile(filePath);
            }

            return FileModelAndView.fileCreated(filePath);

        } catch (IllegalArtifactLocationException e) {
            return FileModelAndView.forbiddenUrl(filePath);
        }
    }

    private boolean updateChecksumFile(MultipartHttpServletRequest request, JobIdentifier jobIdentifier, String filePath) throws IOException, IllegalArtifactLocationException {
        MultipartFile checksumMultipartFile = getChecksumFile(request);
        if (checksumMultipartFile != null) {
            String checksumFilePath = String.format("%s/%s/%s", artifactsService.findArtifactRoot(jobIdentifier), ArtifactLogUtil.CRUISE_OUTPUT_FOLDER, ArtifactLogUtil.MD5_CHECKSUM_FILENAME);
            File checksumFile = artifactsService.getArtifactLocation(checksumFilePath);
            synchronized (checksumFilePath.intern()) {
                return artifactsService.saveOrAppendFile(checksumFile, checksumMultipartFile.getInputStream());
            }
        } else {
            LOGGER.warn("[Artifacts Upload] Checksum file not uploaded for artifact at path '{}'", filePath);
        }
        return true;
    }

    private boolean saveFile(int convertedAttempt, File artifact, MultipartFile multipartFile, boolean shouldUnzip) throws IOException {
        try (InputStream inputStream = multipartFile.getInputStream()) {
            return artifactsService.saveFile(artifact, inputStream, shouldUnzip, convertedAttempt);
        }
    }

    @RequestMapping(value = "/repository/restful/artifact/PUT/*", method = RequestMethod.PUT)
    public ModelAndView putArtifact(@RequestParam("pipelineName") String pipelineName,
                                    @RequestParam("pipelineCounter") String pipelineCounter,
                                    @RequestParam("stageName") String stageName,
                                    @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                    @RequestParam("buildName") String buildName,
                                    @RequestParam(value = "buildId", required = false) Long buildId,
                                    @RequestParam("filePath") String filePath,
                                    @RequestParam(value = "agentId", required = false) String agentId,
                                    HttpServletRequest request
    ) throws Exception {
        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        if (!isValidStageCounter(stageCounter)) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }

        JobIdentifier jobIdentifier;
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }

        if (isConsoleOutput(filePath)) {
            return putConsoleOutput(jobIdentifier, request.getInputStream());
        } else {
            return putArtifact(jobIdentifier, filePath, request.getInputStream());
        }
    }

    /* Other URLs */

    @RequestMapping(value = "/**/consoleout.json", method = RequestMethod.GET)
    public ModelAndView consoleout(@RequestParam("pipelineName") String pipelineName,
                                   @RequestParam("pipelineCounter") String pipelineCounter,
                                   @RequestParam("stageName") String stageName,
                                   @RequestParam("buildName") String buildName,
                                   @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                   @RequestParam(value = "startLineNumber", required = false) Long start
    ) {
        start = start == null ? 0L : start;

        try {
            JobIdentifier identifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
            if (jobInstanceDao.isJobCompleted(identifier) && !consoleService.doesLogExist(identifier)) {
                return logsNotFound(identifier);
            }
            ConsoleConsumer streamer = consoleService.getStreamer(start, identifier);
            return new ModelAndView(new ConsoleOutView(streamer, consoleLogCharset));
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }
    }

    @ErrorHandler
    public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response, Exception e) {
        LOGGER.error("Error loading artifacts: ", e);
        Map model = new HashMap();
        model.put(ERROR_FOR_PAGE, "Artifact does not exist.");
        return new ModelAndView("exceptions_page", model);
    }

    ModelAndView getArtifact(String filePath, ArtifactFolderViewFactory folderViewFactory, String pipelineName, String counterOrLabel, String stageName, String stageCounter, String buildName, String sha, String serverAlias) throws Exception {
        LOGGER.info("[Artifact Download] Trying to resolve '{}' for '{}/{}/{}/{}/{}'", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        long before = System.currentTimeMillis();
        ArtifactsView view;
        //Work out the job that we are trying to retrieve
        JobIdentifier translatedId;
        try {
            translatedId = restfulService.findJob(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        } catch (Exception e) {
            return buildNotFound(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        }

        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        view = new LocalArtifactsView(folderViewFactory, artifactsService, translatedId, consoleService);

        ModelAndView createdView = view.createView(filePath, sha);
        LOGGER.info("[Artifact Download] Successfully resolved '{}' for '{}/{}/{}/{}/{}'. It took: {}ms", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName, System.currentTimeMillis() - before);
        return createdView;
    }

    private boolean shouldUnzipStream(MultipartFile multipartFile) {
        return multipartFile.getName().equals(ZIP_MULTIPART_FILENAME);
    }

    private MultipartFile multipartFile(MultipartHttpServletRequest request) {
        MultipartFile multipartFile = request.getFile(REGULAR_MULTIPART_FILENAME);
        if (multipartFile == null) {
            multipartFile = request.getFile(ZIP_MULTIPART_FILENAME);
        }
        return multipartFile;
    }

    private MultipartFile getChecksumFile(MultipartHttpServletRequest request) {
        return request.getFile(CHECKSUM_MULTIPART_FILENAME);
    }

    private ModelAndView putConsoleOutput(final JobIdentifier jobIdentifier, final InputStream inputStream) throws Exception {
        File consoleLogFile = consoleService.consoleLogFile(jobIdentifier);
        boolean updated = consoleService.updateConsoleLog(consoleLogFile, inputStream);
        if (updated) {
            consoleActivityMonitor.consoleUpdatedFor(jobIdentifier);
            return FileModelAndView.fileAppended(consoleLogFile.getPath());
        } else {
            return FileModelAndView.errorSavingFile(consoleLogFile.getPath());
        }
    }

    private ModelAndView putArtifact(JobIdentifier jobIdentifier, String filePath,
                                     InputStream inputStream) throws Exception {
        File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
        if (artifactsService.saveOrAppendFile(artifact, inputStream)) {
            return FileModelAndView.fileAppended(filePath);
        } else {
            return FileModelAndView.errorSavingFile(filePath);
        }
    }

    private ModelAndView buildNotFound(String pipelineName, String counterOrLabel, String stageName,
                                       String stageCounter,
                                       String buildName) {
        return ResponseCodeView.create(SC_NOT_FOUND, String.format("Job %s/%s/%s/%s/%s not found.", pipelineName,
                counterOrLabel, stageName, stageCounter, buildName));
    }

    private ModelAndView logsNotFound(JobIdentifier identifier) {
        String notFound = String.format("Console log for %s is unavailable as it may have been purged by Go or deleted externally.", identifier.toFullString());
        return ResponseCodeView.create(SC_NOT_FOUND, notFound);
    }

    private boolean isValidStageCounter(String stageCounter) {
        if (StringUtils.isEmpty(stageCounter) || StageIdentifier.LATEST.equalsIgnoreCase(stageCounter)) {
            return true;
        }

        try {
            int value = Integer.parseInt(stageCounter);
            return value > 0;
        } catch (Exception e) {
            return false;
        }
    }
}

Output:
【漏洞类型】路径遍历漏洞
【严重性】High
【漏洞摘要】此代码存在路径遍历漏洞，具体表现为在`getArtifact`和`putArtifact`方法中未验证用户输入的`filePath`参数。攻击者可以通过构造特殊的`filePath`值（如`../`）来访问或操作文件系统中的任意文件。
【关键代码模式】
- `getArtifact`方法中的`filePath`参数未验证。
- `putArtifact`方法中的`filePath`参数未验证。
- `isValidStageCounter`方法用于验证`stageCounter`参数，但在`getArtifact`方法中未调用。
【修复方法说明】修复方法在`getArtifact`和`putArtifact`方法中添加了对`stageCounter`参数的验证，以防止路径遍历攻击。


Instruction:
作为 Java 安全专家，检测此代码中的安全漏洞：
## 内容1
### 文件路径：
server/src/main/java/com/thoughtworks/go/server/controller/ArtifactsController.java
### 代码内容：
package com.thoughtworks.go.server.controller;

import com.thoughtworks.go.domain.ConsoleConsumer;
import com.thoughtworks.go.domain.JobIdentifier;
import com.thoughtworks.go.domain.StageIdentifier;
import com.thoughtworks.go.domain.exception.IllegalArtifactLocationException;
import com.thoughtworks.go.server.cache.ZipArtifactCache;
import com.thoughtworks.go.server.dao.JobInstanceDao;
import com.thoughtworks.go.server.security.HeaderConstraint;
import com.thoughtworks.go.server.service.ArtifactsService;
import com.thoughtworks.go.server.service.ConsoleActivityMonitor;
import com.thoughtworks.go.server.service.ConsoleService;
import com.thoughtworks.go.server.service.RestfulService;
import com.thoughtworks.go.server.util.ErrorHandler;
import com.thoughtworks.go.server.view.artifacts.ArtifactsView;
import com.thoughtworks.go.server.view.artifacts.LocalArtifactsView;
import com.thoughtworks.go.server.web.ArtifactFolderViewFactory;
import com.thoughtworks.go.server.web.FileModelAndView;
import com.thoughtworks.go.server.web.ResponseCodeView;
import com.thoughtworks.go.util.ArtifactLogUtil;
import com.thoughtworks.go.util.SystemEnvironment;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.Map;

import static com.thoughtworks.go.server.web.ZipArtifactFolderViewFactory.zipViewFactory;
import static com.thoughtworks.go.util.ArtifactLogUtil.isConsoleOutput;
import static com.thoughtworks.go.util.GoConstants.*;
import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND;

@Controller
public class ArtifactsController {
    private static final Logger LOGGER = LoggerFactory.getLogger(ArtifactsController.class);

    private final JobInstanceDao jobInstanceDao;
    private final ConsoleActivityMonitor consoleActivityMonitor;
    private final ArtifactFolderViewFactory folderViewFactory;
    private final ArtifactFolderViewFactory jsonViewFactory;
    private final ArtifactFolderViewFactory zipViewFactory;
    private final Charset consoleLogCharset;
    private ArtifactsService artifactsService;
    private RestfulService restfulService;
    private ConsoleService consoleService;
    private HeaderConstraint headerConstraint;

    @Autowired
    ArtifactsController(ArtifactsService artifactsService, RestfulService restfulService, ZipArtifactCache zipArtifactCache, JobInstanceDao jobInstanceDao,
                        ConsoleActivityMonitor consoleActivityMonitor, ConsoleService consoleService, SystemEnvironment systemEnvironment) {
        this.artifactsService = artifactsService;
        this.restfulService = restfulService;
        this.jobInstanceDao = jobInstanceDao;
        this.consoleActivityMonitor = consoleActivityMonitor;
        this.consoleService = consoleService;

        this.folderViewFactory = FileModelAndView.htmlViewFactory();
        this.jsonViewFactory = FileModelAndView.jsonViewfactory();
        this.zipViewFactory = zipViewFactory(zipArtifactCache);
        this.headerConstraint = new HeaderConstraint(systemEnvironment);
        this.consoleLogCharset = systemEnvironment.consoleLogCharsetAsCharset();
    }

    /* RESTful URLs */
    @RequestMapping(value = "/repository/restful/artifact/GET/html", method = RequestMethod.GET)
    public ModelAndView getArtifactAsHtml(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha,
                                          @RequestParam(value = "serverAlias", required = false) String serverAlias) throws Exception {
        return getArtifact(filePath, folderViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, serverAlias);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/json", method = RequestMethod.GET)
    public ModelAndView getArtifactAsJson(@RequestParam("pipelineName") String pipelineName,
                                          @RequestParam("pipelineCounter") String pipelineCounter,
                                          @RequestParam("stageName") String stageName,
                                          @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                          @RequestParam("buildName") String buildName,
                                          @RequestParam("filePath") String filePath,
                                          @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        return getArtifact(filePath, jsonViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/zip", method = RequestMethod.GET)
    public ModelAndView getArtifactAsZip(@RequestParam("pipelineName") String pipelineName,
                                         @RequestParam("pipelineCounter") String pipelineCounter,
                                         @RequestParam("stageName") String stageName,
                                         @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                         @RequestParam("buildName") String buildName,
                                         @RequestParam("filePath") String filePath,
                                         @RequestParam(value = "sha1", required = false) String sha
    ) throws Exception {
        if (filePath.equals(".zip")) {
            filePath = "./.zip";
        }
        return getArtifact(filePath, zipViewFactory, pipelineName, pipelineCounter, stageName, stageCounter, buildName, sha, null);
    }

    @RequestMapping(value = "/repository/restful/artifact/GET/*", method = RequestMethod.GET)
    public void fetch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        request.getRequestDispatcher("/repository/restful/artifact/GET/html").forward(request, response);
    }

    @RequestMapping(value = "/repository/restful/artifact/POST/*", method = RequestMethod.POST)
    public ModelAndView postArtifact(@RequestParam("pipelineName") String pipelineName,
                                     @RequestParam("pipelineCounter") String pipelineCounter,
                                     @RequestParam("stageName") String stageName,
                                     @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                     @RequestParam("buildName") String buildName,
                                     @RequestParam(value = "buildId", required = false) Long buildId,
                                     @RequestParam("filePath") String filePath,
                                     @RequestParam(value = "attempt", required = false) Integer attempt,
                                     MultipartHttpServletRequest request) throws Exception {
        JobIdentifier jobIdentifier;
        if (!headerConstraint.isSatisfied(request)) {
            return ResponseCodeView.create(HttpServletResponse.SC_BAD_REQUEST, "Missing required header 'Confirm'");
        }
        if (!isValidStageCounter(stageCounter)) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter,
                    buildName);
        }

        int convertedAttempt = attempt == null ? 1 : attempt;

        try {
            File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
            if (artifact.exists() && artifact.isFile()) {
                return FileModelAndView.fileAlreadyExists(filePath);
            }

            MultipartFile multipartFile = multipartFile(request);
            if (multipartFile == null) {
                return FileModelAndView.invalidUploadRequest();
            }

            boolean success = saveFile(convertedAttempt, artifact, multipartFile, shouldUnzipStream(multipartFile));

            if (!success) {
                return FileModelAndView.errorSavingFile(filePath);
            }

            success = updateChecksumFile(request, jobIdentifier, filePath);

            if (!success) {
                return FileModelAndView.errorSavingChecksumFile(filePath);
            }

            return FileModelAndView.fileCreated(filePath);

        } catch (IllegalArtifactLocationException e) {
            return FileModelAndView.forbiddenUrl(filePath);
        }
    }

    private boolean updateChecksumFile(MultipartHttpServletRequest request, JobIdentifier jobIdentifier, String filePath) throws IOException, IllegalArtifactLocationException {
        MultipartFile checksumMultipartFile = getChecksumFile(request);
        if (checksumMultipartFile != null) {
            String checksumFilePath = String.format("%s/%s/%s", artifactsService.findArtifactRoot(jobIdentifier), ArtifactLogUtil.CRUISE_OUTPUT_FOLDER, ArtifactLogUtil.MD5_CHECKSUM_FILENAME);
            File checksumFile = artifactsService.getArtifactLocation(checksumFilePath);
            synchronized (checksumFilePath.intern()) {
                return artifactsService.saveOrAppendFile(checksumFile, checksumMultipartFile.getInputStream());
            }
        } else {
            LOGGER.warn("[Artifacts Upload] Checksum file not uploaded for artifact at path '{}'", filePath);
        }
        return true;
    }

    private boolean saveFile(int convertedAttempt, File artifact, MultipartFile multipartFile, boolean shouldUnzip) throws IOException {
        try (InputStream inputStream = multipartFile.getInputStream()) {
            return artifactsService.saveFile(artifact, inputStream, shouldUnzip, convertedAttempt);
        }
    }

    @RequestMapping(value = "/repository/restful/artifact/PUT/*", method = RequestMethod.PUT)
    public ModelAndView putArtifact(@RequestParam("pipelineName") String pipelineName,
                                    @RequestParam("pipelineCounter") String pipelineCounter,
                                    @RequestParam("stageName") String stageName,
                                    @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                    @RequestParam("buildName") String buildName,
                                    @RequestParam(value = "buildId", required = false) Long buildId,
                                    @RequestParam("filePath") String filePath,
                                    @RequestParam(value = "agentId", required = false) String agentId,
                                    HttpServletRequest request
    ) throws Exception {
        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        if (!isValidStageCounter(stageCounter)) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }

        JobIdentifier jobIdentifier;
        try {
            jobIdentifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName, buildId);
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }

        if (isConsoleOutput(filePath)) {
            return putConsoleOutput(jobIdentifier, request.getInputStream());
        } else {
            return putArtifact(jobIdentifier, filePath, request.getInputStream());
        }
    }

    /* Other URLs */

    @RequestMapping(value = "/**/consoleout.json", method = RequestMethod.GET)
    public ModelAndView consoleout(@RequestParam("pipelineName") String pipelineName,
                                   @RequestParam("pipelineCounter") String pipelineCounter,
                                   @RequestParam("stageName") String stageName,
                                   @RequestParam("buildName") String buildName,
                                   @RequestParam(value = "stageCounter", required = false) String stageCounter,
                                   @RequestParam(value = "startLineNumber", required = false) Long start
    ) {
        start = start == null ? 0L : start;

        try {
            JobIdentifier identifier = restfulService.findJob(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
            if (jobInstanceDao.isJobCompleted(identifier) && !consoleService.doesLogExist(identifier)) {
                return logsNotFound(identifier);
            }
            ConsoleConsumer streamer = consoleService.getStreamer(start, identifier);
            return new ModelAndView(new ConsoleOutView(streamer, consoleLogCharset));
        } catch (Exception e) {
            return buildNotFound(pipelineName, pipelineCounter, stageName, stageCounter, buildName);
        }
    }

    @ErrorHandler
    public ModelAndView handleError(HttpServletRequest request, HttpServletResponse response, Exception e) {
        LOGGER.error("Error loading artifacts: ", e);
        Map model = new HashMap();
        model.put(ERROR_FOR_PAGE, "Artifact does not exist.");
        return new ModelAndView("exceptions_page", model);
    }

    ModelAndView getArtifact(String filePath, ArtifactFolderViewFactory folderViewFactory, String pipelineName, String counterOrLabel, String stageName, String stageCounter, String buildName, String sha, String serverAlias) throws Exception {
        LOGGER.info("[Artifact Download] Trying to resolve '{}' for '{}/{}/{}/{}/{}'", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        long before = System.currentTimeMillis();
        ArtifactsView view;
        //Work out the job that we are trying to retrieve
        JobIdentifier translatedId;
        try {
            translatedId = restfulService.findJob(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        } catch (Exception e) {
            return buildNotFound(pipelineName, counterOrLabel, stageName, stageCounter, buildName);
        }

        if (filePath.contains("..")) {
            return FileModelAndView.forbiddenUrl(filePath);
        }

        view = new LocalArtifactsView(folderViewFactory, artifactsService, translatedId, consoleService);

        ModelAndView createdView = view.createView(filePath, sha);
        LOGGER.info("[Artifact Download] Successfully resolved '{}' for '{}/{}/{}/{}/{}'. It took: {}ms", filePath, pipelineName, counterOrLabel, stageName, stageCounter, buildName, System.currentTimeMillis() - before);
        return createdView;
    }

    private boolean shouldUnzipStream(MultipartFile multipartFile) {
        return multipartFile.getName().equals(ZIP_MULTIPART_FILENAME);
    }

    private MultipartFile multipartFile(MultipartHttpServletRequest request) {
        MultipartFile multipartFile = request.getFile(REGULAR_MULTIPART_FILENAME);
        if (multipartFile == null) {
            multipartFile = request.getFile(ZIP_MULTIPART_FILENAME);
        }
        return multipartFile;
    }

    private MultipartFile getChecksumFile(MultipartHttpServletRequest request) {
        return request.getFile(CHECKSUM_MULTIPART_FILENAME);
    }

    private ModelAndView putConsoleOutput(final JobIdentifier jobIdentifier, final InputStream inputStream) throws Exception {
        File consoleLogFile = consoleService.consoleLogFile(jobIdentifier);
        boolean updated = consoleService.updateConsoleLog(consoleLogFile, inputStream);
        if (updated) {
            consoleActivityMonitor.consoleUpdatedFor(jobIdentifier);
            return FileModelAndView.fileAppended(consoleLogFile.getPath());
        } else {
            return FileModelAndView.errorSavingFile(consoleLogFile.getPath());
        }
    }

    private ModelAndView putArtifact(JobIdentifier jobIdentifier, String filePath,
                                     InputStream inputStream) throws Exception {
        File artifact = artifactsService.findArtifact(jobIdentifier, filePath);
        if (artifactsService.saveOrAppendFile(artifact, inputStream)) {
            return FileModelAndView.fileAppended(filePath);
        } else {
            return FileModelAndView.errorSavingFile(filePath);
        }
    }

    private ModelAndView buildNotFound(String pipelineName, String counterOrLabel, String stageName,
                                       String stageCounter,
                                       String buildName) {
        return ResponseCodeView.create(SC_NOT_FOUND, String.format("Job %s/%s/%s/%s/%s not found.", pipelineName,
                counterOrLabel, stageName, stageCounter, buildName));
    }

    private ModelAndView logsNotFound(JobIdentifier identifier) {
        String notFound = String.format("Console log for %s is unavailable as it may have been purged by Go or deleted externally.", identifier.toFullString());
        return ResponseCodeView.create(SC_NOT_FOUND, notFound);
    }

    private boolean isValidStageCounter(String stageCounter) {
        if (StringUtils.isEmpty(stageCounter) || StageIdentifier.LATEST.equalsIgnoreCase(stageCounter)) {
            return true;
        }

        try {
            int value = Integer.parseInt(stageCounter);
            return value > 0;
        } catch (Exception e) {
            return false;
        }
    }
}

Output:
【漏洞类型】路径遍历漏洞
【严重性】High
【漏洞摘要】此代码在处理用户输入的文件路径时，没有进行充分验证。特别是`getArtifact`和`consoleout`方法中，用户可以输入包含".."的路径，这可能导致路径遍历攻击，允许用户访问或修改服务器上的任意文件。具体问题行包括`if (filePath.contains(".."))`以及`view = new LocalArtifactsView(folderViewFactory, artifactsService, translatedId, consoleService);`。
【关键代码模式】
- `getArtifact`方法中，依赖于用户输入的`filePath`参数来定位和操作文件。
- `consoleout`方法中，同样依赖于用户输入的`stageCounter`参数来定位构建任务，而没有对`filePath`参数进行检查。
【修复方法说明】修复方法在`getArtifact`和`consoleout`方法中增加了对`stageCounter`参数的验证，确保它是一个有效的数字或特定值（如"Latest"）。这防止了路径遍历攻击，因为无效的`stageCounter`会导致构建任务未找到，从而返回404错误。适用的最佳实践包括输入验证和最小权限原则。