diff --git a/api/profiles.py b/api/profiles.py index 3783316f..09e611d6 100644 --- a/api/profiles.py +++ b/api/profiles.py @@ -890,19 +890,33 @@ def filter_runtime_env_for_gateway_parity(env: dict[str, str]) -> dict[str, str] # would miss them). Fail-closed list — verified against the installed agent: # CUSTOM_API_KEY hermes_cli/models.py (generic custom provider key) # AZURE_ANTHROPIC_KEY hermes_cli/runtime_provider.py (Azure-hosted Anthropic) +# AZURE_FOUNDRY_API_KEY hermes_cli/runtime_provider.py (Azure Foundry key) +# AZURE_* identity family agent/azure_identity_adapter.py (service-principal / +# workload-identity model auth) # AWS_BEARER_TOKEN_BEDROCK hermes_cli/model_switch.py (Bedrock bearer token) -# AWS_* agent/bedrock_adapter.py (boto3 credential chain) +# AWS_* credential chain agent/bedrock_adapter.py + model_switch._has_aws_creds +# (boto3 access keys, session token, profile, +# container/web-identity credential providers) +# NOTE: region/base-url config vars (AWS_REGION, AWS_DEFAULT_REGION, +# AZURE_FOUNDRY_BASE_URL) are deliberately NOT included — they're configuration, +# not credentials, and the child probe may legitimately need them. # Stripping these in a profile-scoped read prevents an empty named profile from # inheriting the server-process credential (#3961 residual cross-profile leak). _NON_REGISTRY_AGENT_CREDENTIAL_ENV_NAMES: tuple[str, ...] = ( "CUSTOM_API_KEY", "AZURE_ANTHROPIC_KEY", + "AZURE_FOUNDRY_API_KEY", + "AZURE_CLIENT_ID", + "AZURE_CLIENT_SECRET", + "AZURE_TENANT_ID", + "AZURE_FEDERATED_TOKEN_FILE", "AWS_BEARER_TOKEN_BEDROCK", "AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY", "AWS_SESSION_TOKEN", "AWS_PROFILE", "AWS_CONTAINER_CREDENTIALS_RELATIVE_URI", + "AWS_CONTAINER_CREDENTIALS_FULL_URI", "AWS_WEB_IDENTITY_TOKEN_FILE", ) diff --git a/tests/test_issue3957_profile_providers_models.py b/tests/test_issue3957_profile_providers_models.py index 8ba3c68b..cbc3be27 100644 --- a/tests/test_issue3957_profile_providers_models.py +++ b/tests/test_issue3957_profile_providers_models.py @@ -809,6 +809,7 @@ def test_account_usage_subprocess_env_strips_non_registry_agent_creds(monkeypatc monkeypatch.setenv("CUSTOM_API_KEY", "process-default-custom-key") monkeypatch.setenv("AWS_PROFILE", "process-default-aws-profile") monkeypatch.setenv("AWS_BEARER_TOKEN_BEDROCK", "process-default-bedrock-token") + monkeypatch.setenv("AWS_CONTAINER_CREDENTIALS_FULL_URI", "http://169.254.170.2/creds") profiles.set_request_profile("work") try: @@ -821,6 +822,7 @@ def test_account_usage_subprocess_env_strips_non_registry_agent_creds(monkeypatc assert "CUSTOM_API_KEY" not in env assert "AWS_PROFILE" not in env assert "AWS_BEARER_TOKEN_BEDROCK" not in env + assert "AWS_CONTAINER_CREDENTIALS_FULL_URI" not in env def test_detached_worker_scope_scrubs_non_registry_agent_creds(monkeypatch, tmp_path): @@ -835,14 +837,20 @@ def test_detached_worker_scope_scrubs_non_registry_agent_creds(monkeypatch, tmp_ monkeypatch.setenv("CUSTOM_API_KEY", "process-default-custom-key") monkeypatch.setenv("AWS_PROFILE", "process-default-aws-profile") monkeypatch.setenv("AWS_SECRET_ACCESS_KEY", "process-default-aws-secret") + monkeypatch.setenv("AZURE_CLIENT_SECRET", "process-default-azure-secret") + monkeypatch.setenv("AZURE_FOUNDRY_API_KEY", "process-default-foundry-key") with profiles.profile_scope_for_detached_worker("work", "test-worker"): assert os.environ.get("CUSTOM_API_KEY") is None assert os.environ.get("AWS_PROFILE") is None assert os.environ.get("AWS_SECRET_ACCESS_KEY") is None + assert os.environ.get("AZURE_CLIENT_SECRET") is None + assert os.environ.get("AZURE_FOUNDRY_API_KEY") is None assert config._thread_local_env_value("CUSTOM_API_KEY") == "" assert os.environ.get("CUSTOM_API_KEY") == "process-default-custom-key" assert os.environ.get("AWS_PROFILE") == "process-default-aws-profile" assert os.environ.get("AWS_SECRET_ACCESS_KEY") == "process-default-aws-secret" + assert os.environ.get("AZURE_CLIENT_SECRET") == "process-default-azure-secret" + assert os.environ.get("AZURE_FOUNDRY_API_KEY") == "process-default-foundry-key"