T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/generate-changelog.js:27
- Finding
- Shell Command Injection Through the Changelog --since Argument<![CDATA[ ## Vulnerability Details **File Location**: `scripts/generate-changelog.js:27-35`, with attacker-controlled input assigned at `scripts/generate-changelog.js:318-319` **Vulnerability Type**: OS command injection **Risk Level**: High ### Vulnerable Code ```javascript getGitLog(sinceTag = null) { try { let command = 'git log --pretty=format:"%H|%an|%ad|%s" --date=short'; if (sinceTag) { command += ` ${sinceTag}..HEAD`; } const output = execSync(command, { encoding: 'utf8' }); ``` The value is obtained directly from the command line: ```javascript if (arg === '--since' && args[index + 1]) { options.sinceTag = args[index + 1]; } ``` ### Technical Analysis The `--since` value is concatenated into a shell command and passed to `execSync()` as a string. String-based `execSync()` invokes a command shell, so shell metacharacters in `sinceTag` are interpreted as commands rather than as part of a Git revision. No quoting, ref validation, or argument separation prevents an input containing command separators, substitutions, redirections, or similar syntax from changing the executed command. ### Attack Path 1. An attacker convinces a user or automation process to run the changelog generator with an attacker-controlled `--since` value. 2. The CLI parser stores the supplied text in `options.sinceTag`. 3. `getGitLog()` appends the text directly to the `git log` command. 4. `execSync()` passes the resulting string to a shell. 5. Shell syntax embedded in the value executes with the privileges of the Node.js process. For example, a value structurally resembling `valid-tag; attacker-command; #` would terminate the intended Git command and introduce an additional shell command. ### Impact Assessment Successful exploitation provides arbitrary local command execution under the account running the script. The attacker could read or modify repository files, access environment variables and locally readable credentials, alter gener ...[truncated 165 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Avoid invoking a shell. Pass every Git argument separately: ```javascript const { execFileSync } = require('child_process'); const args = [ 'log', '--pretty=format:%H|%an|%ad|%s', '--date=short' ]; if (sinceTag) { args.push(`${sinceTag}..HEAD`); } const output = execFileSync('git', args, { encoding: 'utf8', shell: false }); ``` Additionally: 1. Validate the supplied revision using a strict expected format. 2. Verify that it resolves with a separate non-shell Git call such as `git rev-parse --verify`. 3. Reject values beginning with `-` to prevent Git option injection. 4. Run the generator with the minimum filesystem and credential access required. 5. Add tests covering shell metacharacters, option-like values, malformed refs, and traversal attempts. ]]>
