{"record":{"id":"11f0dc5509ba3c9c","repo":"JuliusBrussee/caveman","slug":"non-node-windows-command-shim-executable","errorCode":null,"errorMessage":"non-Node Windows command shim: ${executable}","messagePattern":"non-Node Windows command shim: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agents/delegate/portable-process.mjs","lineNumber":41,"sourceCode":"      if (existsSync(candidate)) return candidate;\n    }\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  let relativeScript = null;\n  for (const line of readFileSync(executable, \"utf8\").split(/\\r?\\n/)) {\n    if (!/(?:\\bnode(?:\\.exe)?\\b|_prog)/i.test(line) || !/%\\*/.test(line)) continue;\n    const match = line.match(/\"%(?:dp0%|~dp0)\\\\([^\"\\r\\n]+\\.(?:cjs|mjs|js))\"\\s+%\\*/i);\n    if (match) { relativeScript = match[1]; break; }\n  }\n  if (!relativeScript) throw new Error(`non-Node Windows command shim: ${executable}`);\n  const script = 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,\n  platform = process.platform,\n  taskkill = spawnSync,\n  kill = process.kill,\n  graceMs = 1500,","sourceCodeStart":23,"sourceCodeEnd":59,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/agents/delegate/portable-process.mjs#L23-L59","documentation":"portableInvocation() refuses a Windows .cmd/.bat shim whose source contains no recognizable Node-launch pattern (no node/node.exe/_prog line forwarding %* to a %~dp0 script). Like its caveman-shrink twin, this is the CVE-2024-27980 mitigation: arbitrary batch files are never spawned directly; only proven Node shims are unwrapped to a direct node invocation.","triggerScenarios":"Delegate spawn targets a .cmd/.bat that wraps a non-Node binary (a native exe wrapper), or a batch file in a format npm does not generate (pnpm or yarn custom shims, hand-written launchers).","commonSituations":"Delegate configured to launch a CLI that ships a native binary wrapped in .bat; package-manager-specific shim formats; older npm templates that predate the _prog pattern.","solutions":["Configure the delegate command to invoke node directly with the package's JS entrypoint as the argument","If a native binary is intended, point the command at the .exe itself, not at a .bat wrapper","Regenerate the shim with a current npm so it uses the standard pattern"],"exampleFix":"# before\n{ \"command\": \"some-cli.cmd\", \"args\": [] }\n\n# after (it is a Node CLI)\n{ \"command\": \"node\", \"args\": [\"C:\\\\...\\\\some-cli\\\\bin\\\\cli.js\"] }\n# after (it is native)\n{ \"command\": \"C:\\\\...\\\\some-cli.exe\", \"args\": [] }","handlingStrategy":"fallback","validationCode":"// Prefer direct-node invocation up front on Windows — avoids the shim path entirely\nconst useDirect = process.platform === \"win32\";","typeGuard":null,"tryCatchPattern":"try { spawn(inv.command, inv.args); }\ncatch (e) {\n  if (/non-Node Windows command shim/.test(String(e?.message))) {\n    spawn(realExecutableExeOrNodeEntry, args); // it is native or non-standard — bypass the shim\n  } else throw e;\n}","preventionTips":["Configure delegate commands as the real executable (.exe) or node plus the JS entrypoint, never a .bat wrapper","Standardize on npm-generated shims when a shim must be used"],"tags":["windows","spawn","security","delegate"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}