{"record":{"id":"38cd9b077f89cdff","repo":"affaan-m/ECC","slug":"unsafe-character-in-windows-linter-argument-json-stringify","errorCode":null,"errorMessage":"Unsafe character in Windows linter argument: ${JSON.stringify(token)}","messagePattern":"Unsafe character in Windows linter argument: (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"scripts/hooks/pre-bash-commit-quality.js","lineNumber":269,"sourceCode":"  for (const dir of getPathEnv().split(path.delimiter).filter(Boolean)) {\n    for (const candidate of getExecutableCandidates(path.join(dir, command))) {\n      if (fs.existsSync(candidate)) {\n        return candidate;\n      }\n    }\n  }\n\n  return null;\n}\n\nconst LINTER_TIMEOUT_MS = 30000;\nconst UNSAFE_CMD_TOKEN = /[\"\\0\\r\\n]/;\nconst CMD_TOKEN_ENV_PREFIX = 'ECC_LINTER_TOKEN_';\n\nfunction validateCmdToken(value) {\n  const token = String(value);\n  if (UNSAFE_CMD_TOKEN.test(token)) {\n    throw new Error(`Unsafe character in Windows linter argument: ${JSON.stringify(token)}`);\n  }\n  return token;\n}\n\nfunction getLinterInvocation(command, args, platform = process.platform) {\n  const useCmd = platform === 'win32' && /\\.(?:cmd|bat)$/i.test(command);\n\n  if (useCmd) {\n    const environment = { ...process.env };\n    for (const name of Object.keys(environment)) {\n      if (name.toUpperCase().startsWith(CMD_TOKEN_ENV_PREFIX)) {\n        delete environment[name];\n      }\n    }\n\n    // Keep untrusted values out of cmd.exe source. Percent expansion is\n    // non-recursive, so percent signs introduced by these environment values\n    // stay literal. Disabling delayed expansion likewise preserves exclamation","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/affaan-m/ECC/blob/8321021c54d670126ce3b2969d5deb880b4b0c2a/scripts/hooks/pre-bash-commit-quality.js#L251-L287","documentation":"validateCmdToken in scripts/hooks/pre-bash-commit-quality.js sanitizes each argument token before it is embedded in a Windows cmd.exe linter invocation (ECC_LINTER_TOKEN_* env indirection). It rejects tokens containing double quotes, NUL, CR, or LF, because those characters can break out of cmd quoting or inject commands.","triggerScenarios":"Calling validateCmdToken with a value containing `\"`, a newline, carriage return, or NUL byte — e.g. a file path captured with a trailing newline or user input with an embedded quote.","commonSituations":"Paths or branch names with quotes on Windows, command output split on newlines and fed token-by-token, data read from files without trimming trailing CRLF.","solutions":["Strip/trim CR and LF from the value before validating (value.replace(/[\\r\\n]/g, ''))","Remove or escape embedded double quotes in the token source data","Fix the upstream producer so tokens never contain quotes or line breaks","On failure, inspect JSON.stringify(token) in the error to see the exact offending characters"],"exampleFix":"// before\nargs.map(validateCmdToken)\n// after\nargs.map(v => validateCmdToken(String(v).replace(/[\\r\\n]+/g, '').trim()))","handlingStrategy":"validation","validationCode":"const UNSAFE = /[\"\\0\\r\\n]/;\nfunction safeToken(v) {\n  const t = String(v ?? '').replace(/[\\r\\n]+/g, '').trim();\n  if (UNSAFE.test(t)) throw new Error(`Unsafe linter token: ${JSON.stringify(t)}`);\n  return t;\n}\nargs = args.map(safeToken); // run before invoking the hook path","typeGuard":"const isSafeCmdToken = (v) => typeof v === 'string' && ![\"\\0\\r\\n\"].some(c => v.includes(c)) && !v.includes('\"');","tryCatchPattern":"try {\n  tokens = args.map(validateCmdToken);\n} catch (err) {\n  if (String(err.message).startsWith('Unsafe character in Windows linter argument:')) {\n    console.error(`Dropping unsafe linter token: ${err.message}`);\n    return null;\n  }\n  throw err;\n}","preventionTips":["Trim CR/LF from any command output before splitting into tokens","Never pass raw user input as linter arguments on Windows cmd","Sanitize paths and identifiers at the source, before they reach the hook"],"tags":["security","windows","cmd-injection","hooks"],"backgroundTag":"shell-injection-guard","analyzedSha":"8321021c54d670126ce3b2969d5deb880b4b0c2a","analyzedAt":"2026-09-16T10:08:13.343Z","contentChangedAt":"2026-09-16T10:08:13.343Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}