{"record":{"id":"d5a9d74488467b9d","repo":"JuliusBrussee/caveman","slug":"cannot-safely-launch-windows-command-shim-execu","errorCode":null,"errorMessage":"cannot safely launch Windows command shim: ${executable}","messagePattern":"cannot safely launch Windows command shim: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/agent/src/portable-process.ts","lineNumber":56,"sourceCode":"}\n\nexport function portableInvocation(\n  command: string,\n  args: readonly string[],\n  options: {\n    platform?: NodeJS.Platform;\n    env?: NodeJS.ProcessEnv;\n    execPath?: string;\n  } = {},\n): PortableInvocation {\n  const platform = options.platform ?? process.platform;\n  const env = options.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) {\n    throw new Error(`cannot safely launch Windows command shim: ${executable}`);\n  }\n  const relativeScript = parseWindowsNodeShim(readFileSync(executable, \"utf8\"));\n  if (!relativeScript) {\n    throw new Error(`cannot safely launch non-Node Windows command shim: ${executable}`);\n  }\n  const script = resolve(dirname(executable), ...relativeScript.split(/[\\\\/]+/));\n  if (!statSync(script).isFile()) throw new Error(`Windows command shim target is missing: ${script}`);\n  return { command: options.execPath ?? process.execPath, args: [script, ...args] };\n}\n\nexport function hostShellInvocation(\n  source: string,\n  platform: NodeJS.Platform = process.platform,\n  env: NodeJS.ProcessEnv = process.env,\n): PortableInvocation {\n  if (platform === \"win32\") {\n    return {\n      command: envValue(env, \"ComSpec\") ?? \"cmd.exe\",","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/JuliusBrussee/caveman/blob/27d5a3981a347890211bb1bf2439e5c821a63bc9/packages/agent/src/portable-process.ts#L38-L74","documentation":"On win32, portable invocation resolves .cmd/.bat shims (the wrappers npm creates for bin scripts) and inspects them before executing, because spawning a .cmd directly lets attackers inject arbitrary commands via the shim body. If the resolved file is not a regular file or exceeds 256 KiB, the library refuses to launch it with 'cannot safely launch Windows command shim'. This is a deliberate mitigation for CVE-2024-27980-class attacks.","triggerScenarios":"Running on Windows (or with options.platform forced to \"win32\") where the command resolves to a .cmd/.bat that is actually a directory, a symlink/pipe rather than a regular file, or a batch file larger than 256 KiB.","commonSituations":"A corrupted or hand-edited npm shim grew beyond the size cap, an antivirus quarantine left a stub, a symlinked global bin directory, or tests that fabricate oversized fake .cmd files to exercise this path.","solutions":["Reinstall the package that owns the shim so npm regenerates a sane, small wrapper: npm reinstall <pkg> (or delete node_modules/.bin/<name> and npm install)","Verify the target: it must be a regular file well under 256 KiB","Invoke the underlying node script (the shim's target) directly instead of the .cmd","If you control the shim, trim it — legitimate npm shims are a few hundred bytes"],"exampleFix":"// before\nconst inv = portableInvocation(\"my-tool\", []); // my-tool.cmd is 300 KiB or not a regular file\n\n// after\n// reinstall to restore the small generated shim\n// $ npm install my-tool --force\nconst inv = portableInvocation(\"my-tool\", []);","handlingStrategy":"try-catch","validationCode":"import { statSync } from \"node:fs\";\nfunction isLaunchableShim(executable: string): boolean {\n  if (!/\\.(?:cmd|bat)$/i.test(executable)) return true;\n  try {\n    const s = statSync(executable);\n    return s.isFile() && s.size <= 256 * 1024;\n  } catch {\n    return false;\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const inv = portableInvocation(cmd, args);\n} catch (e) {\n  if (e instanceof Error && e.message.startsWith(\"cannot safely launch Windows command shim\")) {\n    // reinstall the owning package or fall back to node <script.js> directly\n  }\n  throw e;\n}","preventionTips":["Prefer spawning node <script.js> directly over .cmd wrappers on Windows","Run npm install cleanly in CI rather than caching partial .bin directories","Treat oversized or non-file shims as tampering: investigate instead of bypassing"],"tags":["windows","security","process-spawn","command-injection","validation"],"backgroundTag":null,"analyzedSha":"27d5a3981a347890211bb1bf2439e5c821a63bc9","analyzedAt":"2026-08-15T09:26:11.751Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}