T09 · Insecure Skill Coding Practices
Error
- Location
- private-secrets.sh:24
- Finding
- Arbitrary JavaScript and OS Command Execution Through Unescaped Secret Inputs## Vulnerability Details **File Location**: `private-secrets.sh`, lines 24-30 and 50-61 **Vulnerability Type**: JavaScript injection leading to arbitrary command execution **Risk Level**: High **Vulnerable code:** ```bash # Use node to update JSON node -e " const fs = require('fs'); const data = JSON.parse(fs.readFileSync('$SECRETS_FILE', 'utf8')); data['$NAME'] = '$VALUE'; fs.writeFileSync('$SECRETS_FILE', JSON.stringify(data, null, 2)); console.log('已添加: $NAME'); " ``` The `get` operation uses the same unsafe source-code construction: ```bash node -e " const fs = require('fs'); const data = JSON.parse(fs.readFileSync('$SECRETS_FILE', 'utf8')); if (data['$NAME']) { console.log(data['$NAME']); } else { console.log('未找到: $NAME'); process.exit(1); } " ``` ### Technical Analysis The script inserts the user-controlled `NAME` and `VALUE` arguments directly into JavaScript source passed to `node -e`. These values are not encoded, escaped, validated, or passed as data parameters. An attacker can include JavaScript quote delimiters and statements in a value, terminate the intended string literal, and inject additional JavaScript. Because Node.js exposes APIs such as `require('child_process')`, successful JavaScript injection can be escalated directly to operating-system command execution. Shell quoting around the `node -e` argument does not prevent this vulnerability. The shell first substitutes the variables, and Node.js subsequently parses the resulting text as executable JavaScript source. ### Attack Path 1. The attacker gains the ability to invoke the skill or influence a secret name or value supplied to its `add` command. 2. The attacker supplies a value containing a closing quote, an injected JavaScript statement, and a JavaScript comment marker. A conceptual payload can invoke `require('child_process').execSync(...)`. 3. The shell expands `$VALUE` inside ...[truncated 1369 chars]
- Remediation
- ## Remediation Suggestions - Never concatenate user input into source passed to `node -e`. - Move the JavaScript implementation into a standalone script and obtain the command, name, value, and file path through `process.argv`. - If an inline program must be retained, pass values after the program and read them from `process.argv`, for example: ```bash node - "$SECRETS_FILE" "$NAME" "$VALUE" <<'NODE' const fs = require('fs'); const [file, name, value] = process.argv.slice(2); const data = JSON.parse(fs.readFileSync(file, 'utf8')); data[name] = value; fs.writeFileSync(file, JSON.stringify(data, null, 2)); NODE ``` - Validate secret names against an explicit policy, such as a conservative length limit and an allowlist of letters, digits, underscores, periods, and hyphens. - Treat secret values strictly as opaque data; do not attempt to sanitize them for insertion into executable source. - Add regression tests using quotes, backslashes, newlines, comment markers, and JavaScript syntax in both names and values. - Return generic errors without including untrusted data in an executable or otherwise interpreted context.
