diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..ed0e8c6 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-09 - Avoid execSync for paths controlled by workspace inputs +**Vulnerability:** Command injection vulnerability via `execSync` when executing dynamically generated workspace files. The `tempFile` was generated dynamically in `.shieldedshell/state/temp_check.js` which depended on `options.workspace`, allowing command injection. +**Learning:** `execSync` runs in a shell by default or executes a string as a command, opening the possibility for command injection even for paths that look safe but are controlled by external inputs or environment locations. +**Prevention:** Use `spawnSync` from `node:child_process` with an array-based argument list and `process.execPath` to launch node, removing the shell execution risk. diff --git a/packages/core/src/reconcile.ts b/packages/core/src/reconcile.ts index b0285bc..1c00261 100644 --- a/packages/core/src/reconcile.ts +++ b/packages/core/src/reconcile.ts @@ -1,4 +1,4 @@ -import { execSync } from "node:child_process"; +import { spawnSync } from "node:child_process"; import crypto from "node:crypto"; import fs from "node:fs"; import path from "node:path"; @@ -138,20 +138,14 @@ export function reconcile(options: ReconcileOptions): ReconcileResult { fs.mkdirSync(path.dirname(tempFile), { recursive: true }); fs.writeFileSync(tempFile, extractedCode, "utf8"); - try { - execSync(`node --check "${tempFile}"`, { stdio: "pipe" }); - log.audit("Syntax check passed"); - } catch (err) { - const stderr = - err && typeof err === "object" && "stderr" in err - ? String((err as { stderr?: Buffer }).stderr ?? "") - : err instanceof Error - ? err.message - : String(err); + const check = spawnSync(process.execPath, ["--check", tempFile], { stdio: "pipe", encoding: "utf8" }); + if (check.status !== 0) { + const stderr = check.stderr || (check.error ? check.error.message : "Unknown syntax error"); consolidateSharedContext(sharedContext, `Syntax Error:\n${stderr}`); if (fs.existsSync(tempFile)) fs.unlinkSync(tempFile); return { success: false, reason: "Syntax check failed" }; } + log.audit("Syntax check passed"); if (options.benchmark) { const staticResult = runBenchmarkStaticVerification(options.workspace, options.benchmark);