affaan-m/ECC · error · Error
Unsafe character in Windows linter argument
Error message
Unsafe character in Windows linter argument: ${JSON.stringify(token)} What it means
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.
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
Example fix
// before args.map(validateCmdToken) // after args.map(v => validateCmdToken(String(v).replace(/[\r\n]+/g, '').trim()))
Defensive patterns
Strategy: validation
Validate before calling
const UNSAFE = /["\0\r\n]/;
function safeToken(v) {
const t = String(v ?? '').replace(/[\r\n]+/g, '').trim();
if (UNSAFE.test(t)) throw new Error(`Unsafe linter token: ${JSON.stringify(t)}`);
return t;
}
args = args.map(safeToken); // run before invoking the hook path Type guard
const isSafeCmdToken = (v) => typeof v === 'string' && !["\0\r\n"].some(c => v.includes(c)) && !v.includes('"'); Try / catch
try {
tokens = args.map(validateCmdToken);
} catch (err) {
if (String(err.message).startsWith('Unsafe character in Windows linter argument:')) {
console.error(`Dropping unsafe linter token: ${err.message}`);
return null;
}
throw err;
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- File path contains unsafe shell characters
- Claude Code command contains characters that are unsafe for…
- Path traversal rejected
- Path traversal rejected
- result.stderr?.toString() || `Formatter exited with status
AI-assisted analysis of affaan-m/ECC@8321021c54 (2026-09-16).
Data as JSON: /api/errors/38cd9b077f89cdff.
Report an issue: GitHub.
Appendix: source
Thrown at scripts/hooks/pre-bash-commit-quality.js:269
for (const dir of getPathEnv().split(path.delimiter).filter(Boolean)) {
for (const candidate of getExecutableCandidates(path.join(dir, command))) {
if (fs.existsSync(candidate)) {
return candidate;
}
}
}
return null;
}
const LINTER_TIMEOUT_MS = 30000;
const UNSAFE_CMD_TOKEN = /["\0\r\n]/;
const CMD_TOKEN_ENV_PREFIX = 'ECC_LINTER_TOKEN_';
function validateCmdToken(value) {
const token = String(value);
if (UNSAFE_CMD_TOKEN.test(token)) {
throw new Error(`Unsafe character in Windows linter argument: ${JSON.stringify(token)}`);
}
return token;
}
function getLinterInvocation(command, args, platform = process.platform) {
const useCmd = platform === 'win32' && /\.(?:cmd|bat)$/i.test(command);
if (useCmd) {
const environment = { ...process.env };
for (const name of Object.keys(environment)) {
if (name.toUpperCase().startsWith(CMD_TOKEN_ENV_PREFIX)) {
delete environment[name];
}
}
// Keep untrusted values out of cmd.exe source. Percent expansion is
// non-recursive, so percent signs introduced by these environment values
// stay literal. Disabling delayed expansion likewise preserves exclamationView on GitHub (pinned to 8321021c54)