T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/build_outline_deck_v2.py:642
- Finding
- JavaScript Code Injection Through Unsanitized Ticker Input<![CDATA[ ## Vulnerability Details **File Location**: `scripts/build_outline_deck_v2.py`, lines 642-668 **Vulnerability Type**: JavaScript code injection through unsafe source-template substitution **Risk Level**: High ### Vulnerable Code ```python output_pptx = f"{ts_code.replace('.', '_').lower()}_banker_deck.pptx" template = (template .replace("const SLIDE_COUNT = 20;", f"const SLIDE_COUNT = {len(slides)};") .replace("'presentation.pptx'", f"'{output_pptx}'") .replace("'2B2D42'", f"'{THEME['primary']}'") .replace("'8D99AE'", f"'{THEME['secondary']}'") .replace("'EF233C'", f"'{THEME['accent']}'") .replace("'EDF2F4'", f"'{THEME['light']}'") .replace("'FFFFFF'", f"'{THEME['bg']}'")) (slides_dir / "compile.js").write_text(template, encoding="utf-8") ``` The generated JavaScript is subsequently executed: ```python r = subprocess.run(["node", "compile.js"], cwd=str(slides_dir), capture_output=True, text=True, timeout=300) sys.stdout.write(r.stdout) sys.stderr.write(r.stderr) if r.returncode != 0: sys.exit(f"compile failed rc={r.returncode}") ``` ### Technical Analysis The `ts_code` command-line argument is incorporated into `output_pptx` and then inserted directly into a single-quoted JavaScript string through textual replacement. The value is not restricted to valid ticker characters and is not escaped as a JavaScript string literal. Although `subprocess.run()` uses an argument array and therefore does not introduce shell injection by itself, that protection does not prevent source-code injection. A quote in `ts_code` can terminate the intended JavaScript string, after which attacker-controlled JavaScript statements can be inserted into the generated `compile.js`. The generated file is executed with Node.js under the privileges of the user running the Skill. This turns control of the ticker argument into an arbitrary local code-execution primitive. A conceptual malicious ticker can be structured as follow ...[truncated 1815 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions 1. Enforce a strict allowlist for ticker values before using them: ```python if not re.fullmatch(r"[A-Za-z0-9._-]+", ts_code): sys.exit("invalid ts_code: only letters, digits, dots, underscores, and hyphens are allowed") ``` 2. Serialize all values inserted into JavaScript with `json.dumps()` rather than manually adding quotes: ```python output_literal = json.dumps(output_pptx, ensure_ascii=False) template = template.replace("'presentation.pptx'", output_literal) ``` 3. Avoid textual source-code substitution for runtime data. Prefer passing the output filename through a JSON configuration file, environment variable, or a fixed command-line argument read by trusted static JavaScript. 4. Validate the final output filename independently: - Reject path separators. - Reject control characters. - Resolve the destination and verify that it remains inside the intended deliverable directory. 5. Add regression tests using quotes, semicolons, newlines, backslashes, Unicode control characters, and path traversal sequences. Confirm that none can alter generated JavaScript syntax. 6. Run deck compilation in a restricted environment with minimal filesystem and network access to reduce impact if another generation flaw is introduced. ]]>
