T09 · Insecure Skill Coding Practices
Error
- Location
- scripts/update-progress.sh:11
- Finding
- Arbitrary JavaScript Execution Through Unescaped Shell Variable Interpolation<![CDATA[ ## Vulnerability Details **File Location**: `scripts/update-progress.sh`, lines 11 and 25–40 **Vulnerability Type**: Shell-to-JavaScript injection **Risk Level**: High ### Vulnerable Code ```bash DATE="${2:-$(date +%Y-%m-%d)}" OPENCLAW_HOME="${OPENCLAW_HOME:-$HOME/.openclaw}" MEMORY_DIR="${OPENCLAW_HOME}/memory" TIPS_FILE="${MEMORY_DIR}/botlearn-tips.json" # Ensure memory directory exists mkdir -p "$MEMORY_DIR" # Day → URLs mapping for recording node - << NODESCRIPT const fs = require('fs'); const path = require('path'); const tipsFile = '$TIPS_FILE'; const day = parseInt('$DAY', 10); const date = '$DATE'; ``` ### Technical Analysis The script constructs a Node.js program using an unquoted heredoc. The user-supplied `DATE` argument and environment-derived `TIPS_FILE` value are inserted directly into single-quoted JavaScript string literals without escaping. Although `DAY` is constrained to a single digit from 1 through 7, `DATE` has no format validation, and `OPENCLAW_HOME` can contain arbitrary characters. A value containing a single quote can terminate the intended JavaScript string and append new JavaScript statements. For example, a crafted date argument shaped like the following can escape the string literal: ```text '; require('child_process').execSync('ATTACKER_COMMAND'); // ``` After interpolation, the generated program contains attacker-controlled JavaScript that is executed by Node.js. This is code injection rather than ordinary malformed input because the affected values are incorporated into source code, not passed as data. ### Attack Path 1. An attacker influences the second argument supplied to `scripts/update-progress.sh`, or controls the `OPENCLAW_HOME` environment variable. 2. The attacker includes a quote followed by valid JavaScript in that value. 3. The shell expands the value into the unquoted heredoc. 4. The injected quote terminates the intended JavaScript string literal. 5. The remaining attacker-controlled text be ...[truncated 1274 chars]
- Remediation
- <![CDATA[ ## Remediation Suggestions Do not construct JavaScript source code by interpolating shell variables. Use a quoted heredoc and pass all values as environment variables or positional arguments: ```bash if ! [[ "$DATE" =~ ^[0-9]{4}-[0-9]{2}-[0-9]{2}$ ]]; then echo '{"error":"Date must use YYYY-MM-DD format"}' >&2 exit 1 fi TIPS_FILE="$TIPS_FILE" DAY="$DAY" DATE="$DATE" node <<'NODESCRIPT' const fs = require('fs'); const path = require('path'); const tipsFile = process.env.TIPS_FILE; const day = Number.parseInt(process.env.DAY, 10); const date = process.env.DATE; if (!Number.isInteger(day) || day < 1 || day > 7) { throw new Error('Invalid day'); } if (!/^\d{4}-\d{2}-\d{2}$/.test(date)) { throw new Error('Invalid date'); } // Continue state processing using these values as data. NODESCRIPT ``` Additional hardening measures: 1. Validate that the date is both syntactically valid and represents a real calendar date. 2. Treat `OPENCLAW_HOME` as untrusted configuration and resolve it to an expected absolute directory. 3. Reject unexpected control characters in paths and arguments. 4. Avoid embedding any environment-derived value into generated source code. 5. Add regression tests containing quotes, backslashes, newlines, JavaScript syntax, and shell metacharacters. 6. Run the Skill with only the filesystem and network privileges required for reminder operation. ]]>
