T09 · Insecure Skill Coding Practices
Error
- Location
- introspection-debugger.js:167
- Finding
- Shell Command Injection Through Automatic Dependency Installation<![CDATA[ ## Vulnerability Details **File Location**: `introspection-debugger.js:167-179` and `introspection-debugger.js:440-447` **Vulnerability Type**: OS command injection **Risk Level**: Critical ### Vulnerable Code ```javascript installDependency: async (error, context) => { const moduleName = this.extractModuleName(error.message); if (moduleName) { try { await this.execAsync(`npm install ${moduleName}`, { cwd: this.workspace }); return { action: 'installed_dependency', module: moduleName }; } catch (e) { return { action: 'install_failed', module: moduleName, reason: e.message }; } } return null; }, ``` The constructed command is passed to a shell: ```javascript execAsync(cmd, options = {}) { return new Promise((resolve, reject) => { exec(cmd, options, (error, stdout, stderr) => { if (error) reject(error); else resolve(stdout); }); }); } ``` ### Technical Analysis The module name is extracted from an error message and interpolated directly into a shell command. The extraction logic does not enforce valid npm package syntax and does not reject shell metacharacters. Because `child_process.exec()` invokes a shell, control operators, command substitutions, redirections, or other shell syntax embedded in the extracted value can alter the intended command. The automatic error-capture design makes this particularly dangerous because a crafted error can trigger command execution without a separate authorization step. ### Attack Path 1. An attacker causes the application to capture an error whose message matches `MODULE_NOT_FOUND` or `Cannot find module`. 2. The crafted message contains shell syntax in the text parsed by `extractModuleName()`. 3. Root-cause analysis selects the `installDependency` repair method. 4. The extracted value is interpolated into `npm install ${moduleName}`. 5. `exec()` passes the resulting string to the operating-system shell. 6. The injected command executes with the p ...[truncated 418 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions - Do not pass values derived from exception messages to a shell. - Replace `exec()` with argument-based execution such as: ```javascript spawn('npm', ['install', validatedModuleName], { cwd: this.workspace, shell: false }); ``` - Strictly validate package names against supported npm naming rules, including separately validated scoped packages. - Reject whitespace, shell metacharacters, URL specifications, local paths, Git references, and package-manager options. - Require explicit user authorization before any dependency installation. - Prefer installing only dependencies already declared and pinned in a reviewed lockfile. ]]>
