The security posture for commander.js-based applications assumes that all user input, command-line arguments, and environment variables are untrusted sources requiring explicit validation and authorization. The library provides parsing and routing capabilities by default, but it does not protect against unvalidated inputs, unauthorized command execution, or secret leakage in help output. Developers must treat custom action handlers, environment bindings, and standalone executable paths as security-sensitive surfaces and ensure that parsing failures or unauthorized actions fail closed.
Essential implementation rules
Enforce explicit authorization in action handlers
Do not rely on .hideHelp() or hidden: true settings to secure administrative or internal subcommands and options. Implement robust runtime authorization checks directly inside the command’s .action() callback to ensure unauthorized users cannot invoke sensitive logic.
Hide internal options and subcommands to prevent leakage
Mark administrative options and subcommands with hidden: true or .hideHelp() to prevent internal capabilities from leaking through error typo suggestions generated by showSuggestionAfterError().
Validate environment variables and external configuration sources
Treat environment variables as untrusted inputs by combining .env() bindings with explicit validation mechanisms like .choices(), custom .argParser() functions, or runtime checks on .getOptionValueSource('name'). Ensure mutual exclusion constraints are enforced via .conflicts().
Configure explicit executable paths for stand-alone subcommands
When defining stand-alone executable subcommands, explicitly set executableFile or call .executableDir() on the parent command to prevent unintended binary or file resolution.
Constrain option inputs with strict choices and mandatory flags
Limit input spaces by calling .choices() on options, declaring required parameters using .makeOptionMandatory(), and defining option dependencies or mutual exclusions with .implies() and .conflicts().
Throw InvalidArgumentError in custom parsers
Throw an instance of InvalidArgumentError or an error with code commander.invalidArgument within custom argument or option parsers when validation fails so that Commander cleanly stops execution instead of passing malformed inputs into application logic.
Sanitize untrusted input in custom output hooks
Sanitize and remove terminal escape sequences or control characters from user-supplied arguments or error messages before rendering them through custom output hooks like writeErr or outputError to prevent terminal injection.
Mask sensitive default values in help output
Prevent accidental exposure of secrets by providing human-readable replacement descriptions using defaultValueDescription or the second argument of .default() whenever options handle sensitive values or tokens.
Override default exit behavior to fail closed
Use .exitOverride() to replace automatic process termination with a thrown CommanderError, and wrap program.parse() in a try-catch block to handle errors safely and fail closed in long-running or embedded process environments.
commander-js: All Security Cards
Approximately 2,484 tokens
On this card
Category: access control
Enforce explicit authorization checks inside action handlers instead of relying on hidden flags
Use when
Developing administrative or internal CLI subcommands and options where sensitive actions must be restricted regardless of whether help documentation suppresses their display.
Secure rules
Rule 1: Guard sensitive commands and options inside the action handler, not with hideHelp or hidden settings
Option.hideHelp() and the hidden: true flag only remove items from generated help text—they do not block users from invoking those commands or options. For operations that should be restricted (administrative, destructive, tenant-scoped, etc.), implement explicit authorization logic inside the command’s .action() callback.
import { Command, Option } from 'commander';const program = new Command();program .command('internal-reset', { hidden: true }) // hidden from help, still executable .description('Dangerous maintenance task') .addOption(new Option('--force-purge').hideHelp()) // also hidden from help .action((options) => { // Robust runtime check – do not rely on help hiding if (process.env.NODE_ENV === 'production' && !isAuthorizedAdmin()) { throw new Error('Unauthorized attempt to execute internal-reset'); } // …secure reset logic… console.log('System state purged.'); });program.parse();
Category: boundary control
Hide internal options and subcommands to prevent boundary leaks
Use when
Defining sensitive, internal, or administrative CLI subcommands and options that should not cross trust boundaries or leak into typo suggestions.
Secure rules
Rule 1: Mark administrative options and subcommands as hidden to prevent leakage through error suggestions.
When creating sensitive CLI entry points, explicitly set { hidden: true } on subcommands or invoke .hideHelp() on option instances like Option. This ensures that unauthorized users cannot discover internal capabilities via typo error suggestions generated by showSuggestionAfterError().
import { Command, Option } from 'commander';const program = new Command();program.showSuggestionAfterError();program.command('debug-dump', { hidden: true });const internalFlag = new Option('--internal-key <key>').hideHelp();program.addOption(internalFlag);
Category: configuration source integrity
Secure Environment Variable Configuration and Validation in Commander
Use when
When binding CLI options to environment variables using .env() or handling ambient process configuration values.
Secure rules
Rule 1: Validate all environment variable inputs using custom option parsers and choice restrictions.
Treat environment variable inputs as untrusted external sources. Combine .env() definitions with explicit validation mechanisms like .choices() or custom .argParser() functions to validate values before application logic consumes them.
import { Command, Option, InvalidArgumentError } from 'commander';const program = new Command();program.addOption( new Option('-p, --port <number>', 'port number') .env('PORT') .argParser((val) => { const port = parseInt(val, 10); if (isNaN(port) || port < 1024 || port > 65535) { throw new InvalidArgumentError('Port must be an integer between 1024 and 65535.'); } return port; }));
Rule 2: Validate environment-derived option values using .env() and .getOptionValueSource()
Declare options that may be supplied from the environment with new Option(...).env(VAR).
After parsing, call command.getOptionValueSource(name) and apply stricter checks when the source is 'env'.
import { Command, Option } from 'commander';const program = new Command();program .addOption( new Option('--endpoint <url>', 'service endpoint').env('SERVICE_ENDPOINT') ) .action((opts, cmd) => { if (cmd.getOptionValueSource('endpoint') === 'env') { const url = new URL(opts.endpoint); if (url.protocol !== 'https:') { cmd.error('SERVICE_ENDPOINT must start with https://'); } } });program.parse();
Rule 3: Enforce option conflict rules on environment variable configurations.
Define mutual exclusion constraints with .conflicts() for options that must not operate together, ensuring contradictory options set via environment variables are correctly rejected.
import { Command, Option } from 'commander';const program = new Command();program .addOption(new Option('-s, --silent', 'Disable logging').env('SILENT')) .addOption( new Option('-j, --json', 'Output JSON') .env('JSON') .conflicts(['silent']) );program.parse();
Category: dangerous execution
Configure Explicit Executable Names for Stand-Alone Subcommands
Use when
Defining stand-alone executable subcommands with .command(name, description, opts) where process spawning or binary name resolution occurs.
Secure rules
Rule 1: Explicitly set executableFile or .executableDir() for stand-alone subcommands to ensure Commander launches the intended executable
When a subcommand is declared with a description string, Commander treats it as a stand-alone executable and looks for a file named program-subcommand in the entry-script directory. To avoid unintentional file resolution, give Commander an explicit target by either:
Passing the executableFile option to .command() to name the exact file to run.
Calling .executableDir() on the parent command to restrict the search to a trusted directory.
import { Command } from 'commander';const program = new Command() .name('pm') // base name for generated filenames .executableDir('./bin'); // trusted directory containing executablesprogram .command('update', 'update packages', { executableFile: 'pm-update' // exact executable inside ./bin });program.parse();
Category: input contract definition
Restrict Option Input Using Strict Choices and Mandatory Flags
Use when
Defining command-line options and arguments that require strict input validation boundaries and enumerated value allowlisting.
Secure rules
Rule 1: Constrain options and command arguments to expected input spaces by defining strict string choices and required flags.
Enforce strict input validation boundaries by calling .choices() to limit inputs to a predefined array of allowed values. Additionally, use .conflicts() and .implies() to declare mutually exclusive options and option dependencies, and declare required flags using .makeOptionMandatory() or argument required syntax.
import { Command, Option } from 'commander';const program = new Command();const envOption = new Option('-e, --environment <env>', 'target environment') .choices(['development', 'staging', 'production']) .makeOptionMandatory();program.addOption(envOption);program .addOption( new Option('-d, --drink <size>', 'drink cup size').choices([ 'small', 'medium', 'large' ]) ) .addOption( new Option('--disable-server', 'disables the server').conflicts('port') );program.parse();
Category: input interpretation safety
Throw InvalidArgumentError in Custom Parsers for Input Validation
Use when
When writing custom argument or option parser functions in Commander to parse untrusted user input.
Secure rules
Rule 1: Throw an InvalidArgumentError when custom parser validation fails.
When writing custom argument or option parser functions, throw an instance of InvalidArgumentError or an error with code ‘commander.invalidArgument’ whenever validation fails. Commander’s internal parsing catches this specific error to cleanly stop execution and trigger standard error handling rather than passing unvalidated values into application logic.
import { Command, InvalidArgumentError } from 'commander';const program = new Command();program .option('-p, --port <number>', 'port number', (value) => { const parsed = parseInt(value, 10); if (isNaN(parsed) || parsed < 1 || parsed > 65535) { throw new InvalidArgumentError('Port must be an integer between 1 and 65535.'); } return parsed; });
Category: output encoding
Sanitize Untrusted User Inputs Rendered in Custom CLI Output
Use when
When configuring custom output hooks via program.configureOutput() to handle error messages or command output containing untrusted user input before rendering to stdout or stderr streams.
Secure rules
Rule 1: Sanitize or encode untrusted user-supplied input contained within error messages or command output before applying styling or printing to terminal streams.
Ensure that any command-line arguments or option values supplied by users are properly sanitized to remove terminal escape sequences or control characters, preventing terminal injection attacks when echoed back through custom output hooks like writeErr or outputError.
Prevent accidental exposure of secrets in CLI help output
Use when
Defining options or arguments that handle sensitive values or tokens to ensure they are not leaked in plaintext through automated help documentation.
Secure rules
Rule 1: Mask sensitive default values in help output using description overrides
When configuring options or arguments with default values containing sensitive data, provide a human-readable replacement description using defaultValueDescription or the second argument of .default() to mask the secret in help output.
import { Command } from 'commander';const program = new Command();program .addOption( program .createOption('--token <token>', 'API access token') .default(process.env.API_TOKEN || 'fallback-secret', '[REDACTED]') );
Category: security control integrity
Override default exit behavior to fail closed in long-running processes
Use when
Embedding Commander inside long-running process environments, web services, or test runners where process.exit() should not be invoked on validation failure or help output.
Secure rules
Rule 1: Use exitOverride() to replace automatic process.exit() with a thrown CommanderError, and make sure the callback (or default handler) throws
Calling .exitOverride() tells Commander to invoke an override callback instead of exiting. Because Commander will still call process.exit() if that callback returns, the callback (or the default override which throws) must throw a CommanderError. Wrap program.parse() in a try … catch so your application can handle the error safely and decide the final exit status.
import { Command, CommanderError } from 'commander';const program = new Command();// Default override throws CommanderError for all exits.program.exitOverride();try { program.parse(process.argv);} catch (err) { if (err instanceof CommanderError) { // Help/version exits are not fatal. if (err.code === 'commander.helpDisplayed' || err.code === 'commander.version') { process.exitCode = 0; } else { console.error('CLI error:', err.message); process.exitCode = err.exitCode; // fail closed with the reported exit code } } else { throw err; // propagate unexpected errors }}
Enforce explicit authorization checks inside action handlers instead of relying on hidden flags
Approximately 325 tokens
Use when
Developing administrative or internal CLI subcommands and options where sensitive actions must be restricted regardless of whether help documentation suppresses their display.
Secure rules
Rule 1: Guard sensitive commands and options inside the action handler, not with hideHelp or hidden settings
Option.hideHelp() and the hidden: true flag only remove items from generated help text—they do not block users from invoking those commands or options. For operations that should be restricted (administrative, destructive, tenant-scoped, etc.), implement explicit authorization logic inside the command’s .action() callback.
import { Command, Option } from 'commander';const program = new Command();program .command('internal-reset', { hidden: true }) // hidden from help, still executable .description('Dangerous maintenance task') .addOption(new Option('--force-purge').hideHelp()) // also hidden from help .action((options) => { // Robust runtime check – do not rely on help hiding if (process.env.NODE_ENV === 'production' && !isAuthorizedAdmin()) { throw new Error('Unauthorized attempt to execute internal-reset'); } // …secure reset logic… console.log('System state purged.'); });program.parse();
Hide internal options and subcommands to prevent boundary leaks
Approximately 215 tokens
Use when
Defining sensitive, internal, or administrative CLI subcommands and options that should not cross trust boundaries or leak into typo suggestions.
Secure rules
Rule 1: Mark administrative options and subcommands as hidden to prevent leakage through error suggestions.
When creating sensitive CLI entry points, explicitly set { hidden: true } on subcommands or invoke .hideHelp() on option instances like Option. This ensures that unauthorized users cannot discover internal capabilities via typo error suggestions generated by showSuggestionAfterError().
import { Command, Option } from 'commander';const program = new Command();program.showSuggestionAfterError();program.command('debug-dump', { hidden: true });const internalFlag = new Option('--internal-key <key>').hideHelp();program.addOption(internalFlag);
Secure Environment Variable Configuration and Validation in Commander
Approximately 561 tokens
Use when
When binding CLI options to environment variables using .env() or handling ambient process configuration values.
Secure rules
Rule 1: Validate all environment variable inputs using custom option parsers and choice restrictions.
Treat environment variable inputs as untrusted external sources. Combine .env() definitions with explicit validation mechanisms like .choices() or custom .argParser() functions to validate values before application logic consumes them.
import { Command, Option, InvalidArgumentError } from 'commander';const program = new Command();program.addOption( new Option('-p, --port <number>', 'port number') .env('PORT') .argParser((val) => { const port = parseInt(val, 10); if (isNaN(port) || port < 1024 || port > 65535) { throw new InvalidArgumentError('Port must be an integer between 1024 and 65535.'); } return port; }));
Rule 2: Validate environment-derived option values using .env() and .getOptionValueSource()
Declare options that may be supplied from the environment with new Option(...).env(VAR).
After parsing, call command.getOptionValueSource(name) and apply stricter checks when the source is 'env'.
import { Command, Option } from 'commander';const program = new Command();program .addOption( new Option('--endpoint <url>', 'service endpoint').env('SERVICE_ENDPOINT') ) .action((opts, cmd) => { if (cmd.getOptionValueSource('endpoint') === 'env') { const url = new URL(opts.endpoint); if (url.protocol !== 'https:') { cmd.error('SERVICE_ENDPOINT must start with https://'); } } });program.parse();
Rule 3: Enforce option conflict rules on environment variable configurations.
Define mutual exclusion constraints with .conflicts() for options that must not operate together, ensuring contradictory options set via environment variables are correctly rejected.
import { Command, Option } from 'commander';const program = new Command();program .addOption(new Option('-s, --silent', 'Disable logging').env('SILENT')) .addOption( new Option('-j, --json', 'Output JSON') .env('JSON') .conflicts(['silent']) );program.parse();
Configure Explicit Executable Names for Stand-Alone Subcommands
Approximately 289 tokens
Use when
Defining stand-alone executable subcommands with .command(name, description, opts) where process spawning or binary name resolution occurs.
Secure rules
Rule 1: Explicitly set executableFile or .executableDir() for stand-alone subcommands to ensure Commander launches the intended executable
When a subcommand is declared with a description string, Commander treats it as a stand-alone executable and looks for a file named program-subcommand in the entry-script directory. To avoid unintentional file resolution, give Commander an explicit target by either:
Passing the executableFile option to .command() to name the exact file to run.
Calling .executableDir() on the parent command to restrict the search to a trusted directory.
import { Command } from 'commander';const program = new Command() .name('pm') // base name for generated filenames .executableDir('./bin'); // trusted directory containing executablesprogram .command('update', 'update packages', { executableFile: 'pm-update' // exact executable inside ./bin });program.parse();
Restrict Option Input Using Strict Choices and Mandatory Flags
Approximately 293 tokens
Use when
Defining command-line options and arguments that require strict input validation boundaries and enumerated value allowlisting.
Secure rules
Rule 1: Constrain options and command arguments to expected input spaces by defining strict string choices and required flags.
Enforce strict input validation boundaries by calling .choices() to limit inputs to a predefined array of allowed values. Additionally, use .conflicts() and .implies() to declare mutually exclusive options and option dependencies, and declare required flags using .makeOptionMandatory() or argument required syntax.
import { Command, Option } from 'commander';const program = new Command();const envOption = new Option('-e, --environment <env>', 'target environment') .choices(['development', 'staging', 'production']) .makeOptionMandatory();program.addOption(envOption);program .addOption( new Option('-d, --drink <size>', 'drink cup size').choices([ 'small', 'medium', 'large' ]) ) .addOption( new Option('--disable-server', 'disables the server').conflicts('port') );program.parse();
Throw InvalidArgumentError in Custom Parsers for Input Validation
Approximately 256 tokens
Use when
When writing custom argument or option parser functions in Commander to parse untrusted user input.
Secure rules
Rule 1: Throw an InvalidArgumentError when custom parser validation fails.
When writing custom argument or option parser functions, throw an instance of InvalidArgumentError or an error with code ‘commander.invalidArgument’ whenever validation fails. Commander’s internal parsing catches this specific error to cleanly stop execution and trigger standard error handling rather than passing unvalidated values into application logic.
import { Command, InvalidArgumentError } from 'commander';const program = new Command();program .option('-p, --port <number>', 'port number', (value) => { const parsed = parseInt(value, 10); if (isNaN(parsed) || parsed < 1 || parsed > 65535) { throw new InvalidArgumentError('Port must be an integer between 1 and 65535.'); } return parsed; });
Sanitize Untrusted User Inputs Rendered in Custom CLI Output
Approximately 233 tokens
Use when
When configuring custom output hooks via program.configureOutput() to handle error messages or command output containing untrusted user input before rendering to stdout or stderr streams.
Secure rules
Rule 1: Sanitize or encode untrusted user-supplied input contained within error messages or command output before applying styling or printing to terminal streams.
Ensure that any command-line arguments or option values supplied by users are properly sanitized to remove terminal escape sequences or control characters, preventing terminal injection attacks when echoed back through custom output hooks like writeErr or outputError.
Prevent accidental exposure of secrets in CLI help output
Approximately 197 tokens
Use when
Defining options or arguments that handle sensitive values or tokens to ensure they are not leaked in plaintext through automated help documentation.
Secure rules
Rule 1: Mask sensitive default values in help output using description overrides
When configuring options or arguments with default values containing sensitive data, provide a human-readable replacement description using defaultValueDescription or the second argument of .default() to mask the secret in help output.
import { Command } from 'commander';const program = new Command();program .addOption( program .createOption('--token <token>', 'API access token') .default(process.env.API_TOKEN || 'fallback-secret', '[REDACTED]') );
Override default exit behavior to fail closed in long-running processes
Approximately 346 tokens
Use when
Embedding Commander inside long-running process environments, web services, or test runners where process.exit() should not be invoked on validation failure or help output.
Secure rules
Rule 1: Use exitOverride() to replace automatic process.exit() with a thrown CommanderError, and make sure the callback (or default handler) throws
Calling .exitOverride() tells Commander to invoke an override callback instead of exiting. Because Commander will still call process.exit() if that callback returns, the callback (or the default override which throws) must throw a CommanderError. Wrap program.parse() in a try … catch so your application can handle the error safely and decide the final exit status.
import { Command, CommanderError } from 'commander';const program = new Command();// Default override throws CommanderError for all exits.program.exitOverride();try { program.parse(process.argv);} catch (err) { if (err instanceof CommanderError) { // Help/version exits are not fatal. if (err.code === 'commander.helpDisplayed' || err.code === 'commander.version') { process.exitCode = 0; } else { console.error('CLI error:', err.message); process.exitCode = err.exitCode; // fail closed with the reported exit code } } else { throw err; // propagate unexpected errors }}