{"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":null,"httpStatus":null,"severity":"error","filePath":"agents/delegate/portable-process.mjs","lineNumber":34,"sourceCode":"    ? [command]\n    : pathExt.split(\";\").map((extension) =>\n      `${command}${extension.startsWith(\".\") ? extension : `.${extension}`}`);\n  for (const directory of (envValue(env, \"PATH\") ?? \"\").split(\";\")) {\n    if (!directory) continue;\n    for (const name of names) {\n      const candidate = join(directory, name);\n      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}","sourceCodeStart":16,"sourceCodeEnd":52,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/agents/delegate/portable-process.mjs#L16-L52","documentation":"portableInvocation() in agents/delegate/portable-process.mjs rejects a .cmd/.bat shim on Windows when statSync shows it is not a regular file or exceeds 256 KiB. A shim that large or non-file does not match any legitimate npm-generated launcher, so the safe-spawn machinery treats it as untrusted input rather than executing it.","triggerScenarios":"The resolved .cmd/.bat is a directory, a symlink or pipe, or a batch file over 256 KiB — a corrupted download, a data file misnamed .bat, or an intentionally bloated script placed on PATH.","commonSituations":"Corrupted npm cache producing garbage shims; PATH pollution where another program's oversized .bat shadows the intended command; malicious or hand-rolled batch scripts; filesystem damage after disk-full events.","solutions":["Check the printed path: file type, size, and content — if you did not write it, treat it as suspect","Clear the package manager cache and reinstall the package that owns the shim (npm cache clean plus fresh install)","If the shim is legitimate but oversized, replace it with a direct node invocation on the real JS entrypoint"],"exampleFix":"# before\nwhere npx   # points at a 2MB .bat\n\n# after\ndel \"C:\\\\path\\\\to\\\\suspect.bat\" && npm install   # or bypass:\nnode .\\\\node_modules\\\\pkg\\\\bin\\\\cli.js","handlingStrategy":"type-guard","validationCode":"const fs = require(\"node:fs\");\nfunction isSafeShim(p) {\n  try {\n    const st = fs.statSync(p);\n    return st.isFile() && st.size <= 256 * 1024;\n  } catch { return false; }\n}","typeGuard":"function isTrustableCmdShim(executablePath) {\n  if (!/\\.(?:cmd|bat)$/i.test(executablePath)) return false;\n  let st;\n  try { st = fs.statSync(executablePath); } catch { return false; }\n  return st.isFile() && st.size <= 256 * 1024;\n}","tryCatchPattern":"if (!isTrustableCmdShim(cmd)) { spawn(process.execPath, [jsEntry, ...args]); return; }\ntry { spawn(...getSpawnInvocation(cmd, args)); }\ncatch (e) { /* reinstall the owning package */ }","preventionTips":["Inspect any .cmd or .bat you did not generate before it lands on PATH ahead of your tools","Keep the npm cache clean; corrupted caches produce oversized garbage shims","Treat an oversized shim as a security signal, not an inconvenience"],"tags":["windows","spawn","security","filesystem"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}