When a CLI silently ignores unknown flags (via .allowUnknownOption() or equivalent), user typos produce no error — --foramt json is swallowed, the command runs with the wrong output format, and the user thinks it succeeded. CWE-20 (Improper Input Validation) covers this precisely: inputs that fall outside the accepted set must be rejected, not ignored. The same principle applies to typed arguments: a --count abc that coerces to NaN and propagates through downstream logic produces silent corruption that can be harder to diagnose than a stack trace. Strict input rejection at the boundary turns typos into immediate feedback instead of silent misconfigurations.
Medium because silent acceptance of invalid inputs produces wrong results without any error signal, which is harder to diagnose than a crash.
Never set .allowUnknownOption() globally. Validate typed arguments at parse time and fail with a specific message naming the offending value:
program
.command('resize <width>')
.option('--format <type>', 'Output format', (val) => {
const formats = ['png', 'jpg', 'webp']
if (!formats.includes(val)) {
throw new InvalidArgumentError(`Must be one of: ${formats.join(', ')}`)
}
return val
})
.action((width, opts) => {
const w = parseInt(width, 10)
if (isNaN(w) || w <= 0) {
console.error(`Error: width must be a positive number, got '${width}'`)
process.exit(2)
}
})
For enum values in click, use click.Choice to get automatic validation and "did you mean?" hints:
@cli.command()
@click.option('--format', type=click.Choice(['png', 'jpg', 'webp']), required=True)
def resize(format):
pass # click rejects invalid choices automatically
ID: cli-quality.error-handling.invalid-input
Severity: medium
What to look for: Enumerate every input validation point. For each, check what happens when users provide invalid flags, unknown options, or wrong argument types. Verify that unknown flags produce a specific error (most frameworks do this by default — check that allowUnknownOption() or equivalent is NOT set unless intentional). Check that type-coerced arguments (numbers, enums) are validated. Look for "did you mean?" suggestions for typos.
Pass criteria: Unknown flags produce a specific error message naming the unrecognized flag. Invalid argument values (wrong type, out of range, invalid enum) produce specific errors. The CLI does not silently ignore unknown flags — 100% of invalid inputs must produce a descriptive error rather than a stack trace. Report: "X validation points found, all Y reject invalid input gracefully."
Fail criteria: Unknown flags are silently ignored (leading to confusion), or invalid values cause an unhandled exception instead of a helpful error, or .allowUnknownOption() is set globally without a clear reason.
Skip (N/A) when: All checks skip when no CLI entry point is detected.
Detail on fail: "program.allowUnknownOption() is set — any flag like --asdf is silently ignored. User typos won't produce errors" or "--count flag accepts any string — passing '--count abc' causes NaN to propagate through the program instead of validating the input as a number"
Remediation: Strict input validation prevents silent misconfigurations:
// commander — validate custom types
program
.command('resize <width>')
.option('--format <type>', 'Output format', (val) => {
const formats = ['png', 'jpg', 'webp']
if (!formats.includes(val)) {
throw new InvalidArgumentError(`Must be one of: ${formats.join(', ')}`)
}
return val
})
.action((width, opts) => {
const w = parseInt(width, 10)
if (isNaN(w) || w <= 0) {
console.error(`Error: width must be a positive number, got '${width}'`)
process.exit(2)
}
})
Do NOT set .allowUnknownOption() globally — it masks user typos.