T09 · Insecure Skill Coding Practices
- Location
scripts/generate_image.py:70- Finding
API Key Exposure Through Command-Line Arguments
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill does perform its stated Qwen image-generation job, but it handles paid API credentials in ways that can expose them.
Install only if you intend to use Alibaba Cloud DashScope/Qwen Image and are comfortable sending prompts to that service. Prefer DASHSCOPE_API_KEY or a protected secret store over --api-key, avoid exposing keys in logs or command history, and avoid --no-verify-ssl unless you understand the download-integrity risk.
scripts/generate_image.py:70API Key Exposure Through Command-Line Arguments
scripts/generate_image.py:2Unpinned Runtime Dependency Resolution
scripts/generate_image.py:74Optional TLS Certificate Verification Bypass for Image Downloads
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
def get_api_key(provided_key: str | None) -> str | None:
"""Get API key from argument first, then environment."""
if provided_key:
return provided_key
return os.environ.get("DASHSCOPE_API_KEY")
The skill advertises and instructs use of capabilities that access environment variables, local configuration files, and the network, but it does not declare any explicit tool scope or permission boundaries. In an agent ecosystem, missing scope declarations can cause the skill to run with broader-than-expected access, reducing reviewability and increasing the chance of unintended credential access or outbound requests.
The activation text is broad enough that the skill may be selected for generic image requests rather than only when this specific provider is appropriate. Over-broad triggering is not direct code execution risk, but it can route user requests into a skill that performs network calls and credential-backed API usage unnecessarily, increasing exposure of secrets and external data sharing.
The documentation explicitly instructs the agent to retrieve API keys from local config files and environment variables without any warning, consent step, or limitation on exposing those values. In an agent setting, this creates a real secret-handling risk because the model may read sensitive credentials from the host and pass them into commands, logs, or responses, especially since examples also show the API key on the command line.
The script hard-codes a Chinese negative prompt as the default input sent to the image-generation service. This imposes a specific language/locale choice on every run unless the user explicitly overrides it, which is a natural-language policy violation under the language/locale rule.
The CLI help at L075-L077 says --no-verify-ssl disables SSL certificate verification, but the main DashScope API call at L125-L133 hardcodes verify=True. The option only affects the later image डाउनलोड at L172, so the documented behavior contradicts what the code actually does for the primary request.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
# Make API request
response = requests.post(
"https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
headers={
"Content-Type": "application/json",
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
try:
# Make API request
response = requests.post(
"https://dashscope.aliyuncs.com/api/v1/services/aigc/multimodal-generation/generation",
headers={
"Content-Type": "application/json",
The script takes an image URL returned by the external API and immediately fetches it with requests.get without validating the hostname or scheme. If the upstream service is compromised, misconfigured, or manipulated, this creates an SSRF-style sink that can trigger unintended outbound requests to attacker-controlled or internal endpoints from the environment running the skill.
output_path.parent.mkdir(parents=True, exist_ok=True)
print("Downloading image...")
img_response = requests.get(image_url, timeout=30, verify=not args.no_verify_ssl)
img_response.raise_for_status()
# Save the image
No suspicious patterns detected.