When developing command-line interfaces with Click, developers must assume that all inputs, environment variables, and configuration sources are potentially untrusted and prone to manipulation or injection. The framework secures basic command routing and direct parameter parsing out of the box, but it relies on explicit developer configuration to enforce strict path validation, secret masking, and environment variable scoping. Any handling of file paths, shell execution, or sensitive configuration fallbacks must fail closed when encountering unexpected values or missing parameters.
Essential implementation rules
Initialize and Secure Shared Application Context State
Always invoke ctx.ensure_object(dict) inside top-level parent commands to guarantee that application state objects are properly initialized before setting or accessing keys across command hierarchies, preventing runtime exceptions during subcommand execution.
Enforce Strict File and Path Validation Boundaries
Avoid raw string parameters for file paths. Instead, use specialized types such as click.Path() with explicit flags like exists=True, dir_okay=False, resolve_path=True, and appropriate permissions to prevent traversal attacks, working-directory hijacking, and concurrent race conditions.
Isolate Environment Variable Scoping and Precedence
Restrict automatic environment variable lookups to application-specific namespaces by setting auto_envvar_prefix in context settings, explicitly declare allowed envvar parameters on options and arguments, and verify parameter sources using ctx.get_parameter_source() before executing sensitive operations or updating environment states.
Prevent Shell Execution and Argument Injection in Subprocesses
When invoking external utilities or editors via click.edit(), rely on Click’s built-in argument handling to pass filenames as discrete parameters without shell invocation. Use explicit delimiters or disable interspersed option parsing when handling leftover arguments via click.UNPROCESSED to prevent argument injection.
Constrain Input Values with Allow-Lists and Bounded Ranges
Restrict string inputs to explicit allowlists or Enum members using click.Choice(), and enforce explicit numerical bounds using click.IntRange or click.FloatRange to reject arbitrary or out-of-range values before they enter application logic.
Hide User Input When Prompting for Secrets
When collecting sensitive information such as passwords, tokens, or API keys using click.prompt(), always set hide_input=True so that the framework reads the input securely without visibly echoing the entered value to the terminal.
Explicitly Validate Custom Parameters and Handle Unset Defaults
Implement custom validation logic via click.ParamType subclasses or parameter callbacks, invoking self.fail() or raising click.BadParameter on validation failure. Ensure boolean flags, path options, and non-boolean flag options define explicit safe default values rather than relying on unvalidated fallbacks.
Export Explicit UTF-8 Locales in Deployment Environments
Ensure deployment environments, background jobs, and container runtimes explicitly set and export UTF-8 locale variables such as LANG=C.UTF-8 and LC_ALL=C.UTF-8 prior to executing Click CLI applications to prevent ASCII fallback errors and surrogate escape corruption.
click: All Security Cards
Approximately 3,700 tokens
On this card
Category: api contract misuse
Initialize Shared Context State Before Access
Use when
Building nested Click groups and subcommands that rely on ctx.obj for sharing application state across command hierarchies.
Secure rules
Rule 1: Call ctx.ensure_object() to guarantee that state objects are initialized before setting or accessing keys.
When passing shared application state across nested group and subcommand invocations via ctx.obj, the context object may be uninitialized if the script is invoked directly or programmatically without an explicit dictionary. Always invoke ctx.ensure_object() inside top-level parent commands to prevent unexpected runtime TypeError or AttributeError exceptions when subcommands access context attributes.
@click.group()@click.option('--debug/--no-debug', default=False)@click.pass_contextdef cli(ctx, debug): ctx.ensure_object(dict) ctx.obj['DEBUG'] = debug@cli.command()@click.pass_contextdef sync(ctx): debug_status = ctx.obj.get('DEBUG', False) click.echo(f"Debug is {'on' if debug_status else 'off'}")
Category: configuration source integrity
Secure Configuration Loading and Path Validation in Click
Use when
Loading configuration files, default maps, and path parameters in Click CLI applications to prevent untrusted input from altering security behavior.
Secure rules
Rule 1: Validate configuration file paths using click.Path and load settings only from trusted locations.
When accepting configuration paths via command options, configure click.Path with exists=True, dir_okay=False, and resolve_path=True to prevent path traversal and working-directory hijacking. Additionally, use click.get_app_dir() to locate standard OS-specific per-user configuration directories rather than loading configuration files from current working directory relative paths.
Secure Environment Variable Sources and Parameter Validation
Use when
Use when configuring Click commands, options, and arguments that consume parameters or flags from environment variables.
Secure rules
Rule 1: Configure distinct auto envvar prefixes on contexts to prevent variable collision
Set auto_envvar_prefix in context_settings when creating groups or commands to restrict automatic environment variable parameter lookups to application-specific namespaces.
Rule 4: Configure environment variable sources for arguments via envvar
Explicitly specify allowed environment variable names for CLI arguments using the envvar parameter in click.argument to restrict fallback exclusively to declared variable names.
Rule 6: Safely inspect os.environ for shell completion environment variables
When configuring shell completion or creating custom ShellComplete subclasses that read from environment variables, safely query os.environ using defaults.
Rule 7: Check parameter source before updating environment variables in option callbacks
Inspect the parameter source using ctx.get_parameter_source(param.name) and do not overwrite pre-existing environment variables if the source is ParameterSource.DEFAULT or ParameterSource.DEFAULT_MAP.
import osimport clickfrom click.core import ParameterSourcedef set_debug(ctx, param, value): source = ctx.get_parameter_source(param.name) if source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP): return None os.environ["APP_DEBUG"] = "1" if value else "0" return value@click.command()@click.option("--debug/--no-debug", default=False, is_eager=True, expose_value=False, callback=set_debug)def cli(): pass
Verify Parameter Sources and Precedence for Configuration Inputs
Use when
Developing command-line applications that accept security-sensitive options from multiple precedence levels such as prompts, command-line arguments, environment variables, and defaults.
Secure rules
Rule 1: Use parameter source introspection to verify the origin of sensitive inputs and guard against unexpected fallback values.
Call ctx.get_parameter_source(param_name) to inspect whether parameters originated from trusted sources like explicit command line flags, prompts, environment variables, or fallback defaults. Enforce strict checks to ensure sensitive settings or ports do not inadvertently rely on unsafe fallback configurations or override expectations.
import click@click.command()@click.option('--port', default=8080, envvar='APP_PORT')@click.pass_contextdef cli(ctx, port): source = ctx.get_parameter_source('port') if source >= click.ParameterSource.DEFAULT_MAP: click.echo('Warning: Using fallback default port setting.')
Category: dangerous execution
Prevent shell execution by avoiding untrusted shell interpretation in subprocess and pager utilities
Use when
When passing dynamic filenames, environment variables, or external command parameters to Click utilities like click.edit() or pager implementations that invoke external binaries.
Secure rules
Rule 1: Avoid shell interpretation and ensure subprocess execution uses direct argument tokenization instead of evaluating command strings through a system shell.
When launching external editors via click.edit() or handling external pager commands, Click tokenizes argument lists and passes parameters directly with shell=False. Ensure that custom subprocess or command execution workflows follow this pattern and never evaluate untrusted strings through shell interpreters.
Validate File Paths and Permissions Using Specialized Click Types
Use when
Developing command-line interfaces that accept file paths or file objects as arguments or options and require strict validation of existence, permissions, and file types.
Secure rules
Rule 1: Use dedicated parameter types instead of raw strings to enforce file and path validation boundaries.
Avoid defining file or directory path parameters as raw string arguments. Instead, use Click’s dedicated parameter types like click.Path() or click.File() to enforce strict path and file lifecycle validation.
Rule 2: Configure explicit validation flags on path parameters to check existence and permissions.
Use click.Path with explicit validation flags such as exists=True, readable=True, writable=True, executable=True, file_okay, and dir_okay to enforce input file boundaries and check permissions prior to application processing. Always render undecodable paths safely using click.format_filename.
Rule 3: Enable atomic writes when updating files that may be read concurrently.
Configure click.File with atomic=True when writing files that may be concurrently read by other users or processes. Retain lazy opening mode unless early truncation is explicitly desired.
Prevent argument injection when forwarding unprocessed arguments or filenames to external processes
Use when
When building command line interfaces with Click that forward unprocessed positional arguments or filenames to subprocesses or external editors.
Secure rules
Rule 1: Pass filenames to Click’s editor execution functions as discrete argument items without shell invocation.
When invoking external utilities using click.edit(), rely on Click’s built-in argument handling to pass filenames as discrete parameters rather than manually constructing shell execution strings. This ensures filenames containing spaces, quotes, or semicolons are treated strictly as literal file paths.
import clickdef edit_user_file(filepath: str) -> None: # click.edit passes filepath directly as a discrete argument to the editor process without shell invocation click.edit(filename=filepath)
Rule 2: Isolate external command invocation and separate unparsed option-like strings with explicit delimiters.
When capturing leftover arguments via nargs=-1 with click.UNPROCESSED, raw strings starting with dashes can be misinterpreted as options by downstream utilities. Prevent argument injection by disabling interspersed option parsing using allow_interspersed_args=False, or by explicitly prepending -- to separate command flags from positional user arguments.
import subprocessimport click@click.command(context_settings=dict(ignore_unknown_options=True))@click.argument("args", nargs=-1, type=click.UNPROCESSED)def cli(args): # Prepend '--' to separate command flags from positional user arguments when invoking external tools subprocess.run(["external-tool", "--", *args], check=True)
Category: input contract definition
Constrain input values and ranges using Choice and numeric ranges
Use when
Defining command-line parameters and options that require strict allowlists, explicit enumerations, or bounded numerical values.
Secure rules
Rule 1: Constrain string inputs to explicit allowlists or Enum members using click.Choice.
Use click.Choice to restrict user inputs to a strict allowlist of explicit string values or Enum members. This ensures that arbitrary and unvalidated strings are rejected before entering application logic.
Rule 2: Enforce strict numerical boundaries using IntRange and FloatRange.
Use click.IntRange or click.FloatRange to enforce explicit lower and upper bounds on numerical arguments, preventing out-of-range values or resource allocation spikes.
Secure Unsafe Parameter Defaults and Explicitly Handle Unset Configurations
Use when
Configuring Click command options, default values, environment variable bindings, and flag parameters to prevent unsafe configuration stomping and unvalidated fallbacks.
Secure rules
Rule 1: Sanitize empty environment variable fallbacks for path parameters
Rely on Click to normalize empty environment variable strings to None when defining options that accept file paths or system resources bound to environment variables via envvar. Explicitly handle None default values rather than assuming an empty environment variable provides a valid string path.
@click.command()@click.option("--config-path", type=click.Path(exists=True), envvar="CONFIG_PATH")def cli(config_path): if config_path is None: return # Process valid config path
Rule 2: Verify parameter source before overriding environment settings with option defaults
Check ctx.get_parameter_source(param.name) to verify whether a parameter value was explicitly passed on the command line or derived from defaults. If the value source is a default, avoid overwriting pre-existing environment variables or external security settings.
def set_debug(ctx, param, value): source = ctx.get_parameter_source(param.name) if source in (click.core.ParameterSource.DEFAULT, click.core.ParameterSource.DEFAULT_MAP): return None os.environ["APP_DEBUG"] = "1" if value else "0" return value@click.command()@click.option("--debug/--no-debug", default=False, is_eager=True, expose_value=False, callback=set_debug)def cli(): pass
Rule 3: Explicitly define boolean flag defaults when binding environment variables
Ensure boolean flag options bound to environment variables default safely to False or an explicit non-privileged state when environment variables are unset, empty, or whitespace-padded.
@click.command()@click.option("--enable-audit/--no-enable-audit", default=False, envvar="ENABLE_AUDIT")def cli(enable_audit): if enable_audit: pass
Rule 4: Specify explicit literal defaults for non-boolean flag options
Avoid using default=True on options configured with a non-boolean flag_value. Explicitly set default to the concrete value expected by the application to prevent unexpected parameter substitution.
Rule 5: Define explicit defaults or required flags for options
Explicitly specify safe default values using default or enforce mandatory parameter input using required=True to avoid relying on unsafe implicit None defaults in application logic.
Validate Command-Line Inputs Using Custom Parameter Types and Callbacks
Use when
Parsing, converting, and validating untrusted command-line inputs or arguments to ensure they conform to expected formats and ranges before processing.
Secure rules
Rule 1: Validate user input during CLI parameter parsing by defining custom click.ParamType subclasses or parameter callbacks.
Implement custom conversion logic using click.ParamType subclasses or parameter callbacks, and invoke self.fail() or raise click.BadParameter with sanitized error messages when input validation fails.
class PortNumber(click.ParamType): name = "port" def convert(self, value, param, ctx): if isinstance(value, int): val = value else: try: val = int(value) except ValueError: self.fail(f"{value!r} is not a valid integer.", param, ctx) if not (1 <= val <= 65535): self.fail(f"Port {val} is outside allowed range (1-65535).", param, ctx) return val
Category: runtime environment hardening
Configure Explicit UTF-8 Locales in Deployment Environments
Use when
When deploying Click applications to automated or headless runtime environments that default to ASCII.
Secure rules
Rule 1: Explicitly set and export UTF-8 locale variables prior to executing Click CLI applications.
Ensure deployment environments, init scripts, background jobs, and containers explicitly set and export a UTF-8 locale such as LANG=C.UTF-8 and LC_ALL=C.UTF-8 before running Click CLI applications. Automated or headless environments defaulting to ASCII prevent clean Unicode input parsing, cause surrogate escape corruption, or trigger execution aborts.
Hide user input when prompting for secrets and passwords
Use when
Prompting command-line interface users for sensitive information such as passwords, tokens, or API keys.
Secure rules
Rule 1: Hide input when prompting for passwords
When using click.prompt() to collect a password, set hide_input=True so Click reads the value through its hidden input prompt instead of visibly echoing the entered value.
Building nested Click groups and subcommands that rely on ctx.obj for sharing application state across command hierarchies.
Secure rules
Rule 1: Call ctx.ensure_object() to guarantee that state objects are initialized before setting or accessing keys.
When passing shared application state across nested group and subcommand invocations via ctx.obj, the context object may be uninitialized if the script is invoked directly or programmatically without an explicit dictionary. Always invoke ctx.ensure_object() inside top-level parent commands to prevent unexpected runtime TypeError or AttributeError exceptions when subcommands access context attributes.
@click.group()@click.option('--debug/--no-debug', default=False)@click.pass_contextdef cli(ctx, debug): ctx.ensure_object(dict) ctx.obj['DEBUG'] = debug@cli.command()@click.pass_contextdef sync(ctx): debug_status = ctx.obj.get('DEBUG', False) click.echo(f"Debug is {'on' if debug_status else 'off'}")
Secure Configuration Loading and Path Validation in Click
Approximately 1,186 tokens
Use when
Loading configuration files, default maps, and path parameters in Click CLI applications to prevent untrusted input from altering security behavior.
Secure rules
Rule 1: Validate configuration file paths using click.Path and load settings only from trusted locations.
When accepting configuration paths via command options, configure click.Path with exists=True, dir_okay=False, and resolve_path=True to prevent path traversal and working-directory hijacking. Additionally, use click.get_app_dir() to locate standard OS-specific per-user configuration directories rather than loading configuration files from current working directory relative paths.
Secure Environment Variable Sources and Parameter Validation
Use when
Use when configuring Click commands, options, and arguments that consume parameters or flags from environment variables.
Secure rules
Rule 1: Configure distinct auto envvar prefixes on contexts to prevent variable collision
Set auto_envvar_prefix in context_settings when creating groups or commands to restrict automatic environment variable parameter lookups to application-specific namespaces.
Rule 4: Configure environment variable sources for arguments via envvar
Explicitly specify allowed environment variable names for CLI arguments using the envvar parameter in click.argument to restrict fallback exclusively to declared variable names.
Rule 6: Safely inspect os.environ for shell completion environment variables
When configuring shell completion or creating custom ShellComplete subclasses that read from environment variables, safely query os.environ using defaults.
Rule 7: Check parameter source before updating environment variables in option callbacks
Inspect the parameter source using ctx.get_parameter_source(param.name) and do not overwrite pre-existing environment variables if the source is ParameterSource.DEFAULT or ParameterSource.DEFAULT_MAP.
import osimport clickfrom click.core import ParameterSourcedef set_debug(ctx, param, value): source = ctx.get_parameter_source(param.name) if source in (ParameterSource.DEFAULT, ParameterSource.DEFAULT_MAP): return None os.environ["APP_DEBUG"] = "1" if value else "0" return value@click.command()@click.option("--debug/--no-debug", default=False, is_eager=True, expose_value=False, callback=set_debug)def cli(): pass
Verify Parameter Sources and Precedence for Configuration Inputs
Use when
Developing command-line applications that accept security-sensitive options from multiple precedence levels such as prompts, command-line arguments, environment variables, and defaults.
Secure rules
Rule 1: Use parameter source introspection to verify the origin of sensitive inputs and guard against unexpected fallback values.
Call ctx.get_parameter_source(param_name) to inspect whether parameters originated from trusted sources like explicit command line flags, prompts, environment variables, or fallback defaults. Enforce strict checks to ensure sensitive settings or ports do not inadvertently rely on unsafe fallback configurations or override expectations.
import click@click.command()@click.option('--port', default=8080, envvar='APP_PORT')@click.pass_contextdef cli(ctx, port): source = ctx.get_parameter_source('port') if source >= click.ParameterSource.DEFAULT_MAP: click.echo('Warning: Using fallback default port setting.')
Prevent shell execution by avoiding untrusted shell interpretation in subprocess and pager utilities
Approximately 188 tokens
Use when
When passing dynamic filenames, environment variables, or external command parameters to Click utilities like click.edit() or pager implementations that invoke external binaries.
Secure rules
Rule 1: Avoid shell interpretation and ensure subprocess execution uses direct argument tokenization instead of evaluating command strings through a system shell.
When launching external editors via click.edit() or handling external pager commands, Click tokenizes argument lists and passes parameters directly with shell=False. Ensure that custom subprocess or command execution workflows follow this pattern and never evaluate untrusted strings through shell interpreters.
Validate File Paths and Permissions Using Specialized Click Types
Approximately 408 tokens
Use when
Developing command-line interfaces that accept file paths or file objects as arguments or options and require strict validation of existence, permissions, and file types.
Secure rules
Rule 1: Use dedicated parameter types instead of raw strings to enforce file and path validation boundaries.
Avoid defining file or directory path parameters as raw string arguments. Instead, use Click’s dedicated parameter types like click.Path() or click.File() to enforce strict path and file lifecycle validation.
Rule 2: Configure explicit validation flags on path parameters to check existence and permissions.
Use click.Path with explicit validation flags such as exists=True, readable=True, writable=True, executable=True, file_okay, and dir_okay to enforce input file boundaries and check permissions prior to application processing. Always render undecodable paths safely using click.format_filename.
Rule 3: Enable atomic writes when updating files that may be read concurrently.
Configure click.File with atomic=True when writing files that may be concurrently read by other users or processes. Retain lazy opening mode unless early truncation is explicitly desired.
Prevent argument injection when forwarding unprocessed arguments or filenames to external processes
Approximately 365 tokens
Use when
When building command line interfaces with Click that forward unprocessed positional arguments or filenames to subprocesses or external editors.
Secure rules
Rule 1: Pass filenames to Click’s editor execution functions as discrete argument items without shell invocation.
When invoking external utilities using click.edit(), rely on Click’s built-in argument handling to pass filenames as discrete parameters rather than manually constructing shell execution strings. This ensures filenames containing spaces, quotes, or semicolons are treated strictly as literal file paths.
import clickdef edit_user_file(filepath: str) -> None: # click.edit passes filepath directly as a discrete argument to the editor process without shell invocation click.edit(filename=filepath)
Rule 2: Isolate external command invocation and separate unparsed option-like strings with explicit delimiters.
When capturing leftover arguments via nargs=-1 with click.UNPROCESSED, raw strings starting with dashes can be misinterpreted as options by downstream utilities. Prevent argument injection by disabling interspersed option parsing using allow_interspersed_args=False, or by explicitly prepending -- to separate command flags from positional user arguments.
import subprocessimport click@click.command(context_settings=dict(ignore_unknown_options=True))@click.argument("args", nargs=-1, type=click.UNPROCESSED)def cli(args): # Prepend '--' to separate command flags from positional user arguments when invoking external tools subprocess.run(["external-tool", "--", *args], check=True)
Constrain input values and ranges using Choice and numeric ranges
Approximately 926 tokens
Use when
Defining command-line parameters and options that require strict allowlists, explicit enumerations, or bounded numerical values.
Secure rules
Rule 1: Constrain string inputs to explicit allowlists or Enum members using click.Choice.
Use click.Choice to restrict user inputs to a strict allowlist of explicit string values or Enum members. This ensures that arbitrary and unvalidated strings are rejected before entering application logic.
Rule 2: Enforce strict numerical boundaries using IntRange and FloatRange.
Use click.IntRange or click.FloatRange to enforce explicit lower and upper bounds on numerical arguments, preventing out-of-range values or resource allocation spikes.
Secure Unsafe Parameter Defaults and Explicitly Handle Unset Configurations
Use when
Configuring Click command options, default values, environment variable bindings, and flag parameters to prevent unsafe configuration stomping and unvalidated fallbacks.
Secure rules
Rule 1: Sanitize empty environment variable fallbacks for path parameters
Rely on Click to normalize empty environment variable strings to None when defining options that accept file paths or system resources bound to environment variables via envvar. Explicitly handle None default values rather than assuming an empty environment variable provides a valid string path.
@click.command()@click.option("--config-path", type=click.Path(exists=True), envvar="CONFIG_PATH")def cli(config_path): if config_path is None: return # Process valid config path
Rule 2: Verify parameter source before overriding environment settings with option defaults
Check ctx.get_parameter_source(param.name) to verify whether a parameter value was explicitly passed on the command line or derived from defaults. If the value source is a default, avoid overwriting pre-existing environment variables or external security settings.
def set_debug(ctx, param, value): source = ctx.get_parameter_source(param.name) if source in (click.core.ParameterSource.DEFAULT, click.core.ParameterSource.DEFAULT_MAP): return None os.environ["APP_DEBUG"] = "1" if value else "0" return value@click.command()@click.option("--debug/--no-debug", default=False, is_eager=True, expose_value=False, callback=set_debug)def cli(): pass
Rule 3: Explicitly define boolean flag defaults when binding environment variables
Ensure boolean flag options bound to environment variables default safely to False or an explicit non-privileged state when environment variables are unset, empty, or whitespace-padded.
@click.command()@click.option("--enable-audit/--no-enable-audit", default=False, envvar="ENABLE_AUDIT")def cli(enable_audit): if enable_audit: pass
Rule 4: Specify explicit literal defaults for non-boolean flag options
Avoid using default=True on options configured with a non-boolean flag_value. Explicitly set default to the concrete value expected by the application to prevent unexpected parameter substitution.
Rule 5: Define explicit defaults or required flags for options
Explicitly specify safe default values using default or enforce mandatory parameter input using required=True to avoid relying on unsafe implicit None defaults in application logic.
Validate Command-Line Inputs Using Custom Parameter Types and Callbacks
Approximately 260 tokens
Use when
Parsing, converting, and validating untrusted command-line inputs or arguments to ensure they conform to expected formats and ranges before processing.
Secure rules
Rule 1: Validate user input during CLI parameter parsing by defining custom click.ParamType subclasses or parameter callbacks.
Implement custom conversion logic using click.ParamType subclasses or parameter callbacks, and invoke self.fail() or raise click.BadParameter with sanitized error messages when input validation fails.
class PortNumber(click.ParamType): name = "port" def convert(self, value, param, ctx): if isinstance(value, int): val = value else: try: val = int(value) except ValueError: self.fail(f"{value!r} is not a valid integer.", param, ctx) if not (1 <= val <= 65535): self.fail(f"Port {val} is outside allowed range (1-65535).", param, ctx) return val
Configure Explicit UTF-8 Locales in Deployment Environments
Approximately 188 tokens
Use when
When deploying Click applications to automated or headless runtime environments that default to ASCII.
Secure rules
Rule 1: Explicitly set and export UTF-8 locale variables prior to executing Click CLI applications.
Ensure deployment environments, init scripts, background jobs, and containers explicitly set and export a UTF-8 locale such as LANG=C.UTF-8 and LC_ALL=C.UTF-8 before running Click CLI applications. Automated or headless environments defaulting to ASCII prevent clean Unicode input parsing, cause surrogate escape corruption, or trigger execution aborts.
Hide user input when prompting for secrets and passwords
Approximately 138 tokens
Use when
Prompting command-line interface users for sensitive information such as passwords, tokens, or API keys.
Secure rules
Rule 1: Hide input when prompting for passwords
When using click.prompt() to collect a password, set hide_input=True so Click reads the value through its hidden input prompt instead of visibly echoing the entered value.