T09 · Insecure Skill Coding Practices
- Location
SKILL.md:56- Finding
Command Injection Through Unquoted Ad-Set Identifier
- Content
View full analysis
Vulnerability Details
File Location:
SKILL.md, line 56
Vulnerability Type: Shell command injection through unquoted, untrusted input
Risk Level: HighVulnerable Code Snippet:
text 2. **Build the trend, not a snapshot.** Read frequency, CTR, and CVR as a slope from baseline to now. Snapshot to the ledger so the delta is computed, not eyeballed: `python3 "${CLAUDE_PLUGIN_ROOT}/scripts/connectors/ledger.py" record <ad-set> --source paid --data '{"frequency": ..., "ctr": ..., "cvr": ..., "reach": ...}'`, then `ledger.py trend <ad-set> --source paid --field ctr` (repeat for `frequency`, `cvr`, `reach`).Technical Analysis
The documented shell commands place the
<ad-set>value into a command without quoting or otherwise establishing an argument boundary. The ad-set identifier can originate from user-supplied data or an advertising-platform export. The Skill itself identifies campaign names and ad labels as untrusted input, but the command construction does not safely preserve that trust boundary.If an Agent follows these instructions by directly substituting an ad-set name into a shell command, shell metacharacters such as command separators, command substitutions, redirections, or pipelines may be interpreted as executable syntax rather than as part of the identifier. A value beginning with an option prefix could also cause argument injection against
ledger.py.The inline JSON argument presents a related construction hazard if placeholder values are inserted through raw string replacement instead of being serialized by a JSON library. Quotes or shell syntax in substituted values could terminate the intended argument and modify the command.
Attack Path
- An attacker creates or supplies an advertising export containing a crafted campaign or ad-set name with shell syntax.
- The user asks the Agent to analyze the export using this Skill.
- The Agent treats the crafted name ...[truncated 1085 chars]
- Remediation
View remediation
Remediation Suggestions
- Do not assemble commands through textual interpolation. Invoke
ledger.pyusing a process API that accepts an argument array, with the ad-set identifier passed as exactly one argument. - Avoid invoking an intermediate shell. For example, use the semantic equivalent of
["python3", ledger_path, "record", ad_set, "--source", "paid", "--data", serialized_data]. - Serialize metric data with a standard JSON library rather than constructing JSON through string concatenation or placeholder replacement.
- Validate ad-set identifiers before use. Reject control characters and unexpected null bytes, and impose a reasonable maximum length.
- Insert
--before positional identifiers where supported so values beginning with-cannot be interpreted as command-line options. - If a shell is unavoidable, apply robust platform-specific argument quoting to every dynamic value. Shell escaping should be defense in depth rather than the preferred solution.
- Update the Skill instructions to explicitly prohibit copying campaign names, labels, or exported values into executable shell text.
- Add tests covering identifiers containing spaces, quotes, semicolons, command substitutions, redirections, newlines, and leading option prefixes.
- Do not assemble commands through textual interpolation. Invoke
