T09 · Insecure Skill Coding Practices
- Location
scripts/docx-plan.js:19- Finding
Shell Command Injection Through an Unvalidated Creator Name
- Content
View full analysis
Vulnerability Details
File Location:
scripts/docx-plan.js, lines 19–105
Vulnerability Type: Shell command injection in generated commands
Risk Level: MediumVulnerable Code
js const args = process.argv.slice(2); const tocFix = args.includes('--toc-fix'); const modeIdx = args.indexOf('--mode'); const mode = modeIdx !== -1 ? args[modeIdx + 1] : 'single'; const name = args.filter((a, i) => { if (a.startsWith('--')) return false; if (modeIdx !== -1 && i === modeIdx + 1) return false; return true; })[0]; // ... const isComparison = mode === 'comparison'; const mdFile = isComparison ? `选题公式学习-综合版.md` : `${name}-结构化总结报告.md`; const docxFile = isComparison ? `选题公式学习-综合版.docx` : `${name}-结构化总结报告-商业版.docx`; console.log(` === xhsfenxi Word generation plan: ${name || 'comparison'} === Mode: ${isComparison ? 'comparison / 综合版' : 'single creator'} ───────────────────────────────────────────── STEP 1 — Confirm Markdown is complete ───────────────────────────────────────────── Make sure this file is finalized: ${mdFile} ───────────────────────────────────────────── STEP 2 — Run build_docx6.py (recommended) ───────────────────────────────────────────── Primary script (most stable): python3 ${ARCHIVE_BASE}/build_docx6.py ${mdFile} ${docxFile} If build_docx6.py fails, fall back to earlier versions: build_docx5.py → build_docx4.py → build_docx3.py `);Technical Analysis
The script accepts a creator name from command-line arguments and interpolates it directly into Markdown and DOCX filenames. Those filenames are then embedded in a shell command without validation or shell-safe quoting.
The script does not itself execute the generated command. However, the surrounding Skill documentation describes the output as the exact Word build command and instructs the operator or agent to run it. Consequently, shell metacharacters in an attacker-controlled creator name remain active if the printed command is copied into ...[truncated 2344 chars]
- Remediation
View remediation
Remediation Suggestions
-
Do not construct executable shell commands from untrusted text. If the package must launch the DOCX builder, use
child_process.spawn()orexecFile()with an argument array andshell: false:js const { spawn } = require('node:child_process'); const child = spawn('python3', [ resolvedBuilderPath, mdFile, docxFile ], { shell: false, stdio: 'inherit' }); -
Validate and normalize creator names before using them in filenames. Apply a strict allowlist and reject or replace shell metacharacters, path separators, control characters, traversal sequences, and leading option markers. For example:
js function safeFilenameComponent(value) { const normalized = value.normalize('NFKC').trim(); const safe = normalized.replace(/[^\p{L}\p{N}._ -]/gu, '_'); if (!safe || safe === '.' || safe === '..') { throw new Error('Invalid creator name'); } return safe.slice(0, 100); } -
Use the sanitized value only for filenames. Preserve the original creator name solely as display text where appropriate.
-
If commands must be displayed rather than executed, quote every dynamic argument with a robust, tested POSIX shell-quoting function. Clearly label the output as requiring review and avoid describing dynamically generated commands as automatically safe.
-
Resolve and verify the builder path. Avoid relying on a hard-coded script directory outside the package. Bundle the required scripts, accept an explicitly configured trusted path, or verify the external script against a trusted cryptographic hash before execution.
-
Add regression tests covering semicolons, command substitutions, pipes, redirections, newlines, quotes, path traversal, Unicode normalization, and creator names beginning with hyphens.
-
