T09 · Insecure Skill Coding Practices
- Location
scripts/generate.py:15- Finding
Unrestricted ComfyUI Host Allows Arbitrary Network Requests
- Content
View full analysis
- Remediation
View remediation
Security audit
Security checks for vulnerabilities and agentic risk
The skill is a real ComfyUI image generator, but its arbitrary HTTP host override and automatic sending of metadata-bearing JPGs warrant review before installation.
Install only if you are comfortable with the agent running this script against a trusted ComfyUI endpoint. Keep --host on localhost unless you explicitly trust the remote server and network path, and treat generated JPGs as potentially containing the prompt and workflow metadata before sharing them.
scripts/generate.py:15Unrestricted ComfyUI Host Allows Arbitrary Network Requests
scripts/generate.py:37Unbounded HTTP Requests and Completion Polling Permit Denial of Service
The skill invokes a local HTTP API on localhost:8188 and therefore has network capability, but the manifest does not declare any tool scope or allowed-tools restriction. Undeclared network access weakens policy enforcement and reviewability, making it easier for a skill to perform unintended requests or be repointed to non-local hosts via the documented --host override.
The skill explicitly says generated JPGs should always be sent immediately to the user, while the changelog and workflow notes state the output embeds prompt/info metadata. That creates a privacy leak path where prompts, parameters, or other embedded generation details may be disclosed to recipients without user awareness or consent.
Data is being sent to an external URL. This could be legitimate telemetry or data exfiltration. Manual review is recommended.
# Submit prompt
url = f'http://{args.host}/prompt'
resp = requests.post(url, json={'prompt': workflow})
resp.raise_for_status()
prompt_id = resp.json()['prompt_id']
print(f'Submitted prompt_id: {prompt_id} (seed: {seed})')
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
history_url = f'http://{args.host}/history/{prompt_id}'
while True:
time.sleep(1)
hist_resp = requests.get(history_url)
hist_resp.raise_for_status()
history = hist_resp.json()
if prompt_id in history and history[prompt_id]['outputs']:
Data from a source is assigned to a variable that is later passed to a sink, creating a variable-mediated taint flow.
# Download image
subfolder_path = img_info.get('subfolder', '')
view_url = f'http://{args.host}/view?filename={filename}&type=output&subfolder={subfolder_path}'
img_resp = requests.get(view_url)
img_resp.raise_for_status()
out_path = args.output or f'./gen-{seed}.jpg'