T09 · Insecure Skill Coding Practices
Error
- Location
- SKILL.md:60
- Finding
- Shell Command Injection Through Unescaped Web-Sourced Metadata## Vulnerability Details **File Location**: `SKILL.md`, lines 60-72 **Vulnerability Type**: Shell command injection caused by direct interpolation of untrusted content **Risk Level**: High ### Vulnerable Code ```bash python3 /Users/psy/.openclaw/workspace/skills/instagram-photo-text-overlay/scripts/overlay.py \ --input /tmp/ig-reddit-quote/{slug}-best.jpg \ --output /tmp/ig-reddit-quote/slide-{N}.jpg \ --title "{ATTRACTION_NAME}" \ --quote "{REDDIT_QUOTE}" \ --author "{SUBREDDIT}" \ --style quote --watermark "tabiji.ai" ``` The values used for `{ATTRACTION_NAME}`, `{REDDIT_QUOTE}`, and `{SUBREDDIT}` originate from a remotely fetched popular-picks page and its Reddit-derived content. The skill directs the agent to insert these values into a shell command without specifying escaping, validation, or argument-safe process invocation. ### Technical Analysis Shell double quotes do not neutralize all shell syntax. Command substitution constructs such as `$(command)` and backticks are still evaluated inside double-quoted arguments. An embedded double quote can also terminate the intended argument and introduce shell operators, redirections, or additional commands. Consequently, a malicious or compromised source page could supply an attraction name, quote, or subreddit value containing shell metacharacters. If the agent constructs and executes the documented command literally, the shell interprets the injected syntax rather than passing it exclusively as text to `overlay.py`. For example, a remotely supplied quote containing a command-substitution expression could cause that expression to run while the shell evaluates the `--quote` argument. A value containing a closing quote followed by a shell operator could similarly escape the argument and append an independent command. The filename placeholder `{slug}` may present an additional injection surface if generated from untrusted attraction names without a strict sl ...[truncated 2000 chars]
- Remediation
- ## Remediation Suggestions - Do not generate a shell command by interpolating manifest or web-sourced strings. - Invoke the overlay program through an argument-array API, such as Python `subprocess.run([...], check=True, shell=False)`, so every value is passed as a literal argument. - Treat every value obtained from the popular-picks page, Reddit content, search results, and the manifest as untrusted. - Validate attraction names, subreddit names, and generated slugs against explicit length and character constraints. - Generate slugs locally with a strict allowlist such as lowercase ASCII letters, digits, and hyphens. Reject path separators, traversal sequences, control characters, and shell metacharacters. - Canonicalize input and output paths and verify that they remain under `/tmp/ig-reddit-quote/`. - Apply length limits to titles and quotes before rendering to prevent resource exhaustion and malformed command lines. - If shell execution is unavoidable, use a proven escaping function for every dynamic argument; argument-array execution without a shell remains the preferred solution. - Run the media-rendering stage with minimal filesystem and network permissions and without publishing or repository credentials. - Separate rendering from publishing so processing untrusted text cannot directly access Instagram, repository, or deployment credentials. - Document that manifest fields are data only and must never be evaluated as shell syntax, templates, code, or agent instructions. A safe implementation should resemble: ```python subprocess.run( [ "python3", "/Users/psy/.openclaw/workspace/skills/instagram-photo-text-overlay/scripts/overlay.py", "--input", validated_input_path, "--output", validated_output_path, "--title", attraction_name, "--quote", reddit_quote, "--author", subreddit, "--style", "quote", "--watermark", "tabiji.ai", ], ...[truncated 43 chars]
