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

  1. Strip/trim CR and LF from the value before validating (value.replace(/[\r\n]/g, ''))
  2. Remove or escape embedded double quotes in the token source data
  3. Fix the upstream producer so tokens never contain quotes or line breaks
  4. 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

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


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 exclamation

View on GitHub (pinned to 8321021c54)