diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..b732a04 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2024-10-26 - Command Injection via `execSync` Path Formatting +**Vulnerability:** Command injection vulnerability identified in `packages/core/src/reconcile.ts` where a shell string was constructed using `execSync(\`node --check "\${tempFile}"\`, { stdio: "pipe" });`. Since `tempFile` derives from `options.workspace`, any shell metacharacters (e.g. `"; ;"`) within the path could lead to arbitrary command execution by the Node.js shell. +**Learning:** `execSync` passes the entire command string to the system shell. When constructing commands that include dynamic or file paths, passing it as a string allows shell injection if the dynamic parts contain unescaped shell operators. +**Prevention:** Avoid `execSync` with template strings containing dynamic paths. Instead, use `execFileSync` (or `spawnSync`) which executes the binary directly without passing the command through a shell. The arguments are passed as an array to `execFileSync`, naturally preventing any shell injections: `execFileSync(process.execPath, ["--check", tempFile], { stdio: "pipe" });`. diff --git a/packages/core/src/reconcile.ts b/packages/core/src/reconcile.ts index b0285bc..7b04b7f 100644 --- a/packages/core/src/reconcile.ts +++ b/packages/core/src/reconcile.ts @@ -1,4 +1,4 @@ -import { execSync } from "node:child_process"; +import { execFileSync } from "node:child_process"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; @@ -139,7 +139,7 @@ export function reconcile(options: ReconcileOptions): ReconcileResult { fs.writeFileSync(tempFile, extractedCode, "utf8"); try { - execSync(`node --check "${tempFile}"`, { stdio: "pipe" }); + execFileSync(process.execPath, ["--check", tempFile], { stdio: "pipe" }); log.audit("Syntax check passed"); } catch (err) { const stderr =