{"record":{"id":"f205988b394d34fe","repo":"JuliusBrussee/caveman","slug":"unsafe-windows-command-shim-executable","errorCode":null,"errorMessage":"unsafe Windows command shim: ${executable}","messagePattern":"unsafe Windows command shim: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"agents/delegate/portable-process.mjs","lineNumber":47,"sourceCode":"export function parseWindowsNodeShim(source) {\n  for (const line of source.split(/\\r?\\n/)) {\n    if (!/(?:\\bnode(?:\\.exe)?\\b|_prog)/i.test(line) || !/%\\*/.test(line)) continue;\n    // Shim-relative target (npm cmd-shim, pnpm/yarn-classic @zkochan forms), or\n    // a drive-absolute target (pnpm emits one when the global bin dir and the\n    // store sit on different drives — path.relative crosses drives as absolute).\n    const match = line.match(/\"%(?:dp0%|~dp0)\\\\([^\"\\r\\n]+\\.(?:cjs|mjs|js))\"\\s+%\\*/i)\n      || line.match(/\"([A-Za-z]:[\\\\/][^\"\\r\\n]+\\.(?:cjs|mjs|js))\"\\s+%\\*/i);\n    if (match) return match[1];\n  }\n  return null;\n}\n\nexport function portableInvocation(command, args, platform = process.platform, env = process.env) {\n  if (platform !== \"win32\") return { command, args: [...args] };\n  const executable = resolveWindowsCommand(command, env) ?? command;\n  if (!/\\.(?:cmd|bat)$/i.test(executable)) return { command: executable, args: [...args] };\n  const stat = statSync(executable);\n  if (!stat.isFile() || stat.size > 256 * 1024) throw new Error(`unsafe Windows command shim: ${executable}`);\n  const relativeScript = parseWindowsNodeShim(readFileSync(executable, \"utf8\"));\n  if (!relativeScript) throw new Error(`non-Node Windows command shim: ${executable}`);\n  const script = /^[A-Za-z]:[\\\\/]/.test(relativeScript)\n    ? relativeScript\n    : resolve(dirname(executable), ...relativeScript.split(/[\\\\/]+/));\n  if (!statSync(script).isFile()) throw new Error(`Windows command shim target missing: ${script}`);\n  return { command: process.execPath, args: [script, ...args] };\n}\n\nexport function delegateSpawnOptions(platform = process.platform) {\n  return {\n    detached: platform !== \"win32\",\n    windowsHide: true,\n  };\n}\n\nexport async function killProcessTree(\n  child,","sourceCodeStart":29,"sourceCodeEnd":65,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/3ee70a102609e550bd2e68004bf5990a9341c851/agents/delegate/portable-process.mjs#L29-L65","documentation":"On Windows, `portableInvocation` resolves .cmd/.bat shims and, instead of executing them through cmd.exe (a command-injection and arg-mangling hazard), inspects the file to re-spawn node directly. It throws this when the shim file is not a regular file or exceeds 256KB — i.e., it fails the safety checks required to trust the shim's contents.","triggerScenarios":"Spawning a Windows command whose resolved path ends in .cmd/.bat and either statSync reports it is not a regular file (directory, device, broken link) or its size is over 256 * 1024 bytes.","commonSituations":"A corrupted or hijacked npm shim (e.g. a .cmd replaced by a huge payload or symlinked to something odd) in node_modules/.bin; PATH resolution picking up an unexpected .cmd; antivirus or a bad install producing a non-regular file at the shim path.","solutions":["Inspect the reported shim path: verify it is a normal file and reasonably sized (dir /a, attrib)","Reinstall the package that owns the .cmd shim (npm rebuild or npm ci) to restore a clean shim","Remove the offending file from PATH if it is not a legitimate npm shim","Pin a non-shim invocation (call node script.js directly) to bypass the .cmd entirely"],"exampleFix":"// before (corrupted/oversized shim at C:\\proj\\node_modules\\.bin\\tsc.cmd)\nspawn(\"tsc\", [\"--build\"], opts);\n// after\nspawn(process.execPath, [\"C:/proj/node_modules/typescript/bin/tsc\", \"--build\"], opts);","handlingStrategy":"try-catch","validationCode":"import { statSync } from \"node:fs\";\nfunction shimLooksSafe(p) {\n  try { const s = statSync(p); return s.isFile() && s.size <= 256 * 1024; }\n  catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const inv = portableInvocation(command, args);\n  spawn(inv.command, inv.args, opts);\n} catch (e) {\n  if (String(e.message).startsWith(\"unsafe Windows command shim:\")) {\n    console.error(`Shim at ${e.message.split(\": \")[1]} is corrupt/oversized; reinstall the owning package`);\n  } else throw e;\n}","preventionTips":["Keep node_modules intact — avoid manual edits or symlinks in .bin","Verify shim sizes after suspicious installs (anything near 256KB is wrong)","Reinstall packages whose shims look tampered with","Prefer invoking node entry scripts directly on Windows when possible"],"tags":["windows","security","spawn"],"backgroundTag":"unsupported-operation","analyzedSha":"3ee70a102609e550bd2e68004bf5990a9341c851","analyzedAt":"2026-09-20T15:53:39.229Z","contentChangedAt":"2026-09-20T15:53:39.229Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}