diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 0000000..2705cfd --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-14 - Replace execSync with execFileSync for Safe Execution +**Vulnerability:** The `execSync` function in `node:child_process` uses a shell by default, which can lead to command injection if input parameters are not properly sanitized. This was present in the `reconcile.ts` engine core and tests. +**Learning:** Even internal tooling or test environments require careful handling of shell executions to prevent unforeseen escalation vectors. `execFileSync` avoids invoking a shell by directly running the executable. +**Prevention:** Avoid string-based execution functions like `execSync`. Always use `execFileSync` (or `spawnSync`) with array-based arguments and specifically address the executable path, like `process.execPath`, to enforce execution safety. diff --git a/packages/core/src/loop.test.ts b/packages/core/src/loop.test.ts index f9d430f..7955c76 100644 --- a/packages/core/src/loop.test.ts +++ b/packages/core/src/loop.test.ts @@ -1,4 +1,4 @@ -import { execSync } from "node:child_process"; +import { execFileSync } from "node:child_process"; import fs from "node:fs"; import os from "node:os"; import path from "node:path"; @@ -173,10 +173,10 @@ describe("ledger benchmark e2e", () => { it("reconcile passes validator for correct ledger implementation", () => { const dir = setupWorkspace(); const mergeTarget = path.join(dir, "auth_service.js"); - execSync(`node "${fixturePath("mock-dev-ledger.mjs")}" "${path.join(dir, "developer_output.json")}"`, { + execFileSync(process.execPath, [fixturePath("mock-dev-ledger.mjs"), path.join(dir, "developer_output.json")], { stdio: "pipe", }); - execSync(`node "${fixturePath("mock-audit-pass.mjs")}" "${path.join(dir, "auditor_output.json")}"`, { + execFileSync(process.execPath, [fixturePath("mock-audit-pass.mjs"), path.join(dir, "auditor_output.json")], { 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 =