{"record":{"id":"a54faa4fe72e1071","repo":"affaan-m/ECC","slug":"file-path-contains-unsafe-shell-characters","errorCode":null,"errorMessage":"File path contains unsafe shell characters","messagePattern":"File path contains unsafe shell characters","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"scripts/hooks/post-edit-format.js","lineNumber":60,"sourceCode":"    if (filePath && /\\.(ts|tsx|js|jsx)$/.test(filePath)) {\n      try {\n        const resolvedFilePath = path.resolve(filePath);\n        const projectRoot = findProjectRoot(path.dirname(resolvedFilePath));\n        const formatter = detectFormatter(projectRoot);\n        if (!formatter) return rawInput;\n\n        const resolved = resolveFormatterBin(projectRoot, formatter);\n        if (!resolved) return rawInput;\n\n        // Biome: `check --write` = format + lint in one pass\n        // Prettier: `--write` = format only\n        const args = formatter === 'biome' ? [...resolved.prefix, 'check', '--write', resolvedFilePath] : [...resolved.prefix, '--write', resolvedFilePath];\n\n        if (process.platform === 'win32' && resolved.bin.endsWith('.cmd')) {\n          // Windows: .cmd files require shell to execute. Guard against\n          // command injection by rejecting paths with shell metacharacters.\n          if (UNSAFE_PATH_CHARS.test(resolvedFilePath)) {\n            throw new Error('File path contains unsafe shell characters');\n          }\n          const result = spawnSync(resolved.bin, args, {\n            cwd: projectRoot,\n            shell: true,\n            stdio: 'pipe',\n            timeout: 15000\n          });\n          if (result.error) throw result.error;\n          if (typeof result.status === 'number' && result.status !== 0) {\n            throw new Error(result.stderr?.toString() || `Formatter exited with status ${result.status}`);\n          }\n        } else {\n          execFileSync(resolved.bin, args, {\n            cwd: projectRoot,\n            stdio: ['pipe', 'pipe', 'pipe'],\n            timeout: 15000\n          });\n        }","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/affaan-m/ECC/blob/01e15490f04e29cfefe3896951f43db46994d8ee/scripts/hooks/post-edit-format.js#L42-L78","documentation":"On Windows, when the resolved formatter binary ends in .cmd the hook must spawn it with shell:true. cmd.exe interprets characters like & | < > ^ % ! ; ( ) backtick and $ as operators or variable expanders, so the hook rejects any edited file path containing them before spawning. This is a command-injection guard; it is caught and silently skipped (formatting is non-blocking).","triggerScenarios":"Editing a JS/TS file whose absolute path contains a metacharacter, e.g. C:\\dev\\repo (copy)\\src\\file.ts, D:\\work\\a&b\\file.js, or a path with %VAR%-style segments.","commonSituations":"Project directories named with parentheses or ampersands on Windows, CI agent workspace paths containing parens, or paths produced by archive extractors that append ' (1)'.","solutions":["Rename or move the project directory so the absolute path contains no & | < > ^ % ! ; ( ) backtick or $ characters","Run the formatter manually outside the hook on that file","Invoke the non-.cmd formatter entry (node_modules/.bin/biome or prettier directly) so shell:true is not used"],"exampleFix":"// before\nC:\\work\\repo (1)\\src\\file.ts\n// after\nC:\\work\\repo-1\\src\\file.ts","handlingStrategy":"validation","validationCode":"const UNSAFE = /[&|<>^%!;`()$]/;\nfunction assertSafeShellPath(filePath) {\n  if (process.platform === 'win32' && UNSAFE.test(filePath)) {\n    throw new Error(`Path unsafe for cmd.exe: ${filePath}`);\n  }\n}","typeGuard":"function isSafeForCmdShell(filePath) {\n  return typeof filePath === 'string' && !/[&|<>^%!;`()$]/.test(filePath);\n}","tryCatchPattern":"try { spawnSync(bin, args, { shell: true }); }\ncatch (err) {\n  if (/unsafe shell characters/.test(err.message)) {\n    console.warn('Skipping format: project path contains shell metacharacters.');\n    return;\n  }\n  throw err;\n}","preventionTips":["Avoid parentheses, ampersands, and % in Windows project paths","Prefer the non-.cmd formatter entry so shell:true is not required","In CI, use workspace paths without spaces or special characters"],"tags":["security","command-injection","windows","hooks","formatting"],"backgroundTag":null,"analyzedSha":"01e15490f04e29cfefe3896951f43db46994d8ee","analyzedAt":"2026-08-13T00:31:08.655Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}