CrowdControl.CLI (crowd_control v0.1.1)

Copy Markdown View Source

Builds CLI commands for Claude Code and Open Code executables.

Sanitizes path-like options and environment variable keys/values to prevent shell injection and confused-deputy file reads.

Summary

Functions

Builds the command and arguments for launching a CLI subprocess.

Builds the environment variable map from options.

Sanitizes a filesystem path. Rejects non-binaries, null bytes, and C0 control characters ([\x00-\x1f]). Returns the expanded absolute path.

Types

opt()

@type opt() ::
  {:executable, String.t()}
  | {:model, String.t()}
  | {:system_prompt, String.t()}
  | {:allowed_tools, [String.t()]}
  | {:permission_mode, String.t()}
  | {:max_budget_usd, number()}
  | {:session_id, String.t()}
  | {:resume, String.t()}
  | {:continue, boolean()}
  | {:add_dir, String.t() | [String.t()]}
  | {:include_partial_messages, boolean()}
  | {:no_session_persistence, boolean()}
  | {:settings, String.t()}
  | {:settings_file, String.t()}
  | {:settings_json, String.t() | map()}
  | {:setting_sources, [String.t()]}
  | {:mcp_config, String.t() | [String.t()]}
  | {:strict_mcp_config, boolean()}
  | {:agents, String.t() | map()}
  | {:plugin_dir, String.t()}
  | {:bare, boolean()}
  | {:extra_args, [String.t()]}
  | {:env, %{optional(String.t()) => String.t()}}
  | {:api_key, String.t()}
  | {:api_url, String.t()}

opts()

@type opts() :: [opt()]

Functions

build_command(opts \\ [])

@spec build_command(opts()) ::
  {String.t(), [String.t()], %{optional(String.t()) => String.t()}}

Builds the command and arguments for launching a CLI subprocess.

Returns {executable, args, env} where executable is a string path or name, args is a list of string arguments, and env is a map of environment variables to set in the child process.

Raises ArgumentError if any path-like option or env key/value fails validation (null bytes, control chars, malformed env keys).

Options

  • :executable - CLI binary name or path (default: "claude")
  • :model - model to use (e.g. "sonnet", "opus")
  • :system_prompt - custom system prompt
  • :allowed_tools - list of allowed tool names
  • :permission_mode - permission mode string
  • :max_budget_usd - spending ceiling as a number
  • :session_id - session ID for new sessions
  • :resume - session ID to resume
  • :continue - true to continue the most recent session
  • :add_dir - additional project directory (string or list of strings, sanitized + expanded)
  • :include_partial_messages - true to include streaming deltas
  • :no_session_persistence - true to skip saving to disk
  • :settings_file - path to a settings JSON file (sanitized + expanded)
  • :settings_json - inline settings JSON (string or map)
  • :settings - deprecated; use :settings_file or :settings_json. Heuristic: a string starting with { is treated as inline JSON, otherwise as a file path
  • :setting_sources - list of setting sources (e.g. ["user", "project", "local"])
  • :mcp_config - path(s) to MCP config JSON files (sanitized + expanded)
  • :strict_mcp_config - true to only use MCP servers from :mcp_config
  • :agents - JSON string or map defining custom agents
  • :plugin_dir - path to a plugin directory (sanitized + expanded)
  • :bare - true for minimal mode
  • :extra_args - list of additional string arguments
  • :env - map of environment variables (keys must match ~r/\A[A-Za-z_][A-Za-z0-9_]*\z/, values must be binaries with no C0 control characters, i.e. [\x00-\x1f])
  • :api_key - shorthand for setting ANTHROPIC_API_KEY
  • :api_url - shorthand for setting ANTHROPIC_BASE_URL

build_env(opts)

@spec build_env(opts()) :: %{optional(String.t()) => String.t()}

Builds the environment variable map from options.

Merges :api_key and :api_url shorthands with the :env map. Explicit :env entries take precedence over shorthands.

Validates every key against ~r/\A[A-Za-z_][A-Za-z0-9_]*\z/ and ensures values are binaries with no C0 control characters ([\x00-\x1f]), raising ArgumentError on violation to prevent shell injection through the env-file mechanism. (DEL \x7f and Unicode separators are left to shell_escape/1, which renders every surviving byte inert.)

sanitize_path!(path)

@spec sanitize_path!(term()) :: String.t()

Sanitizes a filesystem path. Rejects non-binaries, null bytes, and C0 control characters ([\x00-\x1f]). Returns the expanded absolute path.

Raises ArgumentError on invalid input.