T09 · Insecure Skill Coding Practices
- Location
scripts/generate_image.py:11- Finding
Hardcoded Cloudflare API Credentials
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a real Cloudflare image generator, but it exposes a Cloudflare token and lets crafted prompts reach shell execution, so it needs review before installation.
Review before installing. The publisher should remove and rotate the exposed Cloudflare token, load credentials from user-controlled secret storage, replace shell-based curl with a native HTTPS client or shell=False argument list, avoid fixed temp files, and document exactly where images are saved and what data is sent to Cloudflare.
scripts/generate_image.py:11Hardcoded Cloudflare API Credentials
scripts/generate_image.py:22Arbitrary Shell Command Injection Through the Image Prompt
scripts/generate_image.py:22Predictable Shared Temporary File Enables Symlink Clobbering and Response Races
scripts/generate_image.py:22Bearer Token Disclosed Through Spawned Process Command Lines
The skill claims to only generate images, but the content also exposes a hardcoded Cloudflare API token and account ID, writes files locally, and references direct script execution. This mismatch is dangerous because reviewers or automated policy systems may trust the benign description while overlooking sensitive credential exposure and broader execution behavior.
The markdown directly includes a live-looking API token and account identifier without any masking or secure-handling guidance. Exposed credentials can be immediately reused by anyone with access to the skill, enabling unauthorized API calls, abuse of the Cloudflare account, cost incurrence, and possible downstream compromise of connected resources.
An image-generation skill only needs to make an HTTP request, but this implementation adds general shell execution capability through curl and shell=True. In this context, that is unnecessarily dangerous because it expands the skill from simple network use into arbitrary command execution if the prompt or command construction is abused.
A live API bearer token is hardcoded directly in the script and used for outbound requests, which exposes a secret to anyone with code access and makes accidental leakage or misuse highly likely. If reused elsewhere or committed to a repository, an attacker could abuse the Cloudflare account, incur cost, or access associated services.
The script constructs and executes a shell command from user input without adequate safeguards or disclosure, creating a direct path to command injection. In a skill context, prompts are expected to be attacker-controlled, so treating them as safe command-string content is especially dangerous.
This is a concrete tool-parameter abuse issue: attacker-controlled prompt data is funneled into a shell-executed command, allowing the tool boundary to be crossed from 'generate an image' into 'run arbitrary OS commands'. In agent skills, this context makes the issue more dangerous because natural-language inputs are routinely untrusted and can be adversarially crafted.
-H "Content-Type: application/json" \
-d '{json.dumps({"prompt": prompt})}' > /tmp/cf_response.json'''
subprocess.run(cmd, shell=True)
with open('/tmp/cf_response.json', 'r') as f:
data = json.load(f)
The skill declares no explicit tool scope even though its documented usage clearly implies shell execution and file access. In an agent environment, missing scope declarations weaken sandboxing and review controls, making it easier for the skill to use capabilities beyond what operators expect.
The skill writes API responses to a fixed temporary file and saves generated images to a local workspace path by default, creating local persistence not clearly implied by the stated behavior. This increases exposure because sensitive prompts, generated content, or API responses may remain on disk, and the fixed /tmp/cf_response.json path is susceptible to collisions or tampering in shared environments.
The function derives a default filesystem path and writes the generated image there, but the code provides no prior disclosure that it will create a file in the workspace by default. The save location is only printed after completion, which does not warn the user before the write occurs.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
output_path = f"/home/ubuntu/.openclaw/workspace/{prompt.replace(' ', '_')[:30]}.png"
# Call Cloudflare API
cmd = f'''curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/run/{MODEL}" \
-H "Authorization: Bearer {TOKEN}" \
-H "Content-Type: application/json" \
-d '{json.dumps({"prompt": prompt})}' > /tmp/cf_response.json'''
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
output_path = f"/home/ubuntu/.openclaw/workspace/{prompt.replace(' ', '_')[:30]}.png"
# Call Cloudflare API
cmd = f'''curl -s -X POST "https://api.cloudflare.com/client/v4/accounts/{ACCOUNT_ID}/ai/run/{MODEL}" \
-H "Authorization: Bearer {TOKEN}" \
-H "Content-Type: application/json" \
-d '{json.dumps({"prompt": prompt})}' > /tmp/cf_response.json'''
The script builds a shell command containing user-controlled input (prompt) and executes it with shell=True, which creates a command-injection path. Even though json.dumps escapes JSON syntax, it does not make shell interpolation safe; a crafted prompt containing shell-significant characters can break quoting and execute arbitrary commands on the host.
-H "Content-Type: application/json" \
-d '{json.dumps({"prompt": prompt})}' > /tmp/cf_response.json'''
subprocess.run(cmd, shell=True)
with open('/tmp/cf_response.json', 'r') as f:
data = json.load(f)
No suspicious patterns detected.