feat: environment variable filtering with filter_env, allowed_env, disallowed_env#44
feat: environment variable filtering with filter_env, allowed_env, disallowed_env#44ppsplus-bradh wants to merge 10 commits intoguess:mainfrom
Conversation
|
also this - i did a squash + merge so just have to remove the #42 commits |
Prevents leaking sensitive host environment (SSH keys, database URLs, cloud credentials) to the CLI subprocess. Filters by CLI-recognized prefixes (ANTHROPIC_, CLAUDE_CODE_, CLAUDE_, VERTEX_REGION_), an explicit allowlist of non-namespaced CLI vars, and essential system vars (PATH, HOME, etc.). User-provided :env bypasses the filter.
Add `allowed_env` option that accepts a list of environment variable names to pass through from the system environment to the CLI, beyond the built-in allowlist. Unlike `env` (key-value pairs), `allowed_env` takes only keys — values are read from System.get_env() at spawn time. This enables applications to forward specific env vars (e.g. DATABASE_URL, custom config) without hardcoding values in the `env` map, while still benefiting from the security filtering that excludes RELEASE_*, SSH keys, and other sensitive process-level vars. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete the env control surface with two new options:
- `filter_env` (boolean, default true) — when true, applies the
built-in allowlist (ANTHROPIC_*, CLAUDE_*, PATH, HOME, etc.).
When false, passes all system env vars through unfiltered.
- `disallowed_env` (list of strings) — keys to exclude from the
CLI environment. Works in both filtered and unfiltered modes.
Combined with the existing `allowed_env` and `env` options, this
gives users full control over what reaches the CLI:
# Filtered (default): built-in allowlist + extras
filter_env: true, allowed_env: ["DATABASE_URL"]
# Unfiltered: everything minus exclusions
filter_env: false, disallowed_env: ["RELEASE_COOKIE", "SECRET_KEY"]
# Explicit overrides always win regardless of mode
env: %{"FORCE_THIS" => "value"}
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
8b7b514 to
63a7c0d
Compare
|
Rebased on main. |
|
Very keen to see this merged. Nice work @ppsplus-bradh ! |
|
So So I think I'd rather maintain consistency with how the Python SDK behaves and pass all system env by default. You can use I'm working on a PR for this now. |
Awesome! Didn't realize we already inherently had that level of flexibility. |
|
@ppsplus-bradh thanks again for your help/work on these 🙏 |
Summary
Adds a complete environment variable control surface for CLI session spawning. Three new session options give users fine-grained control over which system env vars reach the CLI:
filter_envbooleantrueallowed_env[String.t()][]disallowed_env[String.t()][]Naming follows the SDK's existing convention (
allowed_tools/disallowed_tools).Depends on
spawn_executablerefactor) — this PR builds on top of thebuild_env/prepare_envchanges from that PR.How it works
Filtered mode (
filter_env: true, default):Unfiltered mode (
filter_env: false):Setting
filter_env: falsewith no other options reproduces the pre-filter behavior exactly — all system env vars pass through unchanged.Both paths then merge identically with SDK vars, user
:envoverrides, and API key.Examples
Changes
@cli_env_prefixes,@cli_env_allowlist,@system_env_allowlist),collect_system_env/3,filter_system_env/1,cli_env_var?/2filter_env,allowed_env,disallowed_envconvert_optionnil returns for new options