T09 · Insecure Skill Coding Practices
- Location
scripts/core/generate.py:14- Finding
Stability API Credential Disclosure Through an Unrestricted Custom API Host
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
This image-generation skill is mostly purpose-aligned, but it needs review because it can send API credentials to an arbitrary configured host and can automatically delete unrelated image files in a chosen output directory.
Install only if you are comfortable sending prompts to Stability AI or a configured API_HOST, and avoid setting API_HOST to anything untrusted or non-HTTPS. Use a dedicated empty output directory because the cleanup routine can delete older PNG, JPG, JPEG, WebP, and same-basename JSON files in that directory. Treat generated JSON metadata as potentially sensitive because it stores prompts and settings.
scripts/core/generate.py:14Stability API Credential Disclosure Through an Unrestricted Custom API Host
scripts/core/generate.py:313Automatic Cleanup Can Delete Unrelated User Images and Metadata
requirements.txt:1Unpinned Third-Party Dependencies Create Supply-Chain and Reproducibility Risk
The request target is derived from API_HOST, which comes from the environment and is not validated before the code sends the Stability API bearer token and user prompt to it. If an attacker can influence environment variables or the .env file, they can redirect requests to an attacker-controlled host and exfiltrate the API key and prompt data.
print(f"Style: {style_preset}")
try:
response = requests.post(url, headers=headers, json=body, timeout=120)
if response.status_code == 401:
print("Error: Invalid API key. Check your STABILITY_API_KEY.")
The V2 request also uses a URL built from the environment-controlled API_HOST while attaching the Authorization bearer token. A modified environment can silently redirect traffic and leak secrets and user content to an attacker-controlled service.
print(f"Style: {style_preset}")
try:
response = requests.post(url, headers=headers, files=files, data=data, timeout=120)
if response.status_code == 401:
print("Error: Invalid API key. Check your STABILITY_API_KEY.")
Code accesses credential files (SSH keys, AWS credentials, etc.). This could indicate credential theft attempts.
if not API_KEY:
print("Error: STABILITY_API_KEY environment variable not set.")
print("Tip: Check your .env file or export the variable.")
sys.exit(1)
ASPECT_RATIO_MAP = {
The skill documents behavior that clearly implies network access, environment-variable access, and local file writes, but it declares no explicit tool scope or permission boundaries. That makes the skill easier to invoke with broader-than-necessary capabilities and weakens reviewability, increasing the chance of unintended data access or misuse if integrated into an agent runtime.
The trigger phrases and description are broad enough to match many ordinary user requests like 'generate image' or 'draw this,' which can cause the skill to activate in contexts the user did not specifically intend. Over-broad routing increases the risk of accidental prompt transmission to an external API and unintended local artifact creation, especially in multi-skill agent environments.
The skill states that prompts, negative prompts, parameters, and timestamps are stored in local JSON metadata files, but it does not prominently warn users beforehand. Prompts can contain sensitive or proprietary information, so silent persistence creates a privacy and data-retention risk if those files are later accessed, synced, or exfiltrated.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
print(f"Style: {style_preset}")
try:
response = requests.post(url, headers=headers, json=body, timeout=120)
if response.status_code == 401:
print("Error: Invalid API key. Check your STABILITY_API_KEY.")
The skill persists prompt metadata and then performs lifecycle management of files, which exceeds a narrowly understood 'generate image' action. Because prompts may contain sensitive user data, storing them in JSON sidecar files increases privacy risk, especially without explicit user consent or retention controls.
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
print(f"Style: {style_preset}")
try:
response = requests.post(url, headers=headers, files=files, data=data, timeout=120)
if response.status_code == 401:
print("Error: Invalid API key. Check your STABILITY_API_KEY.")
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
img = Image.open(io.BytesIO(image_data))
img.save(filepath, "WEBP", quality=95)
else:
with open(filepath, "wb") as f:
f.write(image_data)
return filepath
Automatic cleanup deletes files in the output directory based solely on extension and age ordering, which is broader than simple image generation. If the output directory contains other important images, this feature can remove user data unexpectedly and cause integrity/availability loss.
The script deletes older files automatically without prior warning or confirmation. In an agent-skill context, this is risky because users may reasonably expect generation only, not destructive file operations in the selected output directory.
The dependency 'requests' is unpinned, so builds are not reproducible and may silently pull in a vulnerable or breaking release over time. In a network-facing skill that likely makes outbound API calls, this increases supply-chain risk and makes it impossible to verify whether known advisories affect the deployed version.
requests
python-dotenv
pillow
The manifest does not pin a version of 'requests', and the package has multiple known advisories across its release history. That means the deployment could resolve to an affected version without visibility, which is particularly relevant for a skill expected to perform HTTP requests to an external API.
The dependency 'python-dotenv' is unpinned, which allows arbitrary future releases to be installed without review. Because this package influences environment-variable loading, an unsafe or compromised version could affect secret handling or local file interactions in ways that are hard to audit.
requests
python-dotenv
pillow
Because 'python-dotenv' is not version-pinned, there is no way to determine from this manifest whether the installed release includes known vulnerabilities. While the package is not the core image-processing component, it can influence configuration and secret-loading behavior, so leaving its version unspecified weakens deployment assurance.
The dependency 'pillow' is unpinned, creating supply-chain and reproducibility risk for image-processing code. In an image-generation skill, image libraries are especially security-relevant because malformed image inputs or vulnerable decoders have historically led to denial of service and, in some versions, code-execution issues.
requests
python-dotenv
pillow
The manifest leaves 'pillow' unpinned even though the library has a long history of security advisories, including memory-safety and resource-consumption issues. In the context of an image-generation skill, this is more dangerous than average because image handling is a primary function, making a vulnerable Pillow version more likely to be exercised.
No suspicious patterns detected.