fix(#3961): scrub Azure managed-identity (MSI/IMDS) credential-source env vars
Final credential-surface item: agent/azure_identity_adapter.py treats IDENTITY_ENDPOINT / MSI_ENDPOINT as ManagedIdentityCredential sources, so an empty named profile could inherit the host's managed identity on the quota + detached-worker paths. Add IDENTITY_ENDPOINT / IDENTITY_HEADER / MSI_ENDPOINT / MSI_SECRET to the non-registry scrub set (config var AZURE_FOUNDRY_BASE_URL kept). Detached-worker regression extended. This completes the agent's enumerable credential-env read surface (verified from source). Decision (with maintainer): stop the Codex deep-gate here — the remaining theoretical surface is cloud-SDK-internal credential-source endpoints on pre-existing infra, not vectors introduced by this PR; validated via Opus + full suite. The PR's #3961 scope (os.environ mirror race on /api/providers + /api/models) is fully closed plus the broader same-class hardening. Co-authored-by: rodboev <rodboev@users.noreply.github.com>
This commit is contained in:
parent
51dd69b4f2
commit
d60ad140b1
2 changed files with 14 additions and 0 deletions
|
|
@ -910,6 +910,14 @@ _NON_REGISTRY_AGENT_CREDENTIAL_ENV_NAMES: tuple[str, ...] = (
|
|||
"AZURE_CLIENT_SECRET",
|
||||
"AZURE_TENANT_ID",
|
||||
"AZURE_FEDERATED_TOKEN_FILE",
|
||||
# Azure managed-identity (App Service MSI / IMDS) credential-source vars —
|
||||
# agent/azure_identity_adapter.py treats these as ManagedIdentityCredential
|
||||
# sources, so an empty named profile must not inherit the host's managed
|
||||
# identity. (NOT AZURE_FOUNDRY_BASE_URL — that's config, kept.)
|
||||
"IDENTITY_ENDPOINT",
|
||||
"IDENTITY_HEADER",
|
||||
"MSI_ENDPOINT",
|
||||
"MSI_SECRET",
|
||||
"AWS_BEARER_TOKEN_BEDROCK",
|
||||
"AWS_ACCESS_KEY_ID",
|
||||
"AWS_SECRET_ACCESS_KEY",
|
||||
|
|
|
|||
|
|
@ -839,6 +839,8 @@ def test_detached_worker_scope_scrubs_non_registry_agent_creds(monkeypatch, tmp_
|
|||
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")
|
||||
monkeypatch.setenv("IDENTITY_ENDPOINT", "http://169.254.169.254/msi")
|
||||
monkeypatch.setenv("MSI_ENDPOINT", "http://169.254.169.254/msi")
|
||||
|
||||
with profiles.profile_scope_for_detached_worker("work", "test-worker"):
|
||||
assert os.environ.get("CUSTOM_API_KEY") is None
|
||||
|
|
@ -846,6 +848,8 @@ def test_detached_worker_scope_scrubs_non_registry_agent_creds(monkeypatch, tmp_
|
|||
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 os.environ.get("IDENTITY_ENDPOINT") is None
|
||||
assert os.environ.get("MSI_ENDPOINT") is None
|
||||
assert config._thread_local_env_value("CUSTOM_API_KEY") == ""
|
||||
|
||||
assert os.environ.get("CUSTOM_API_KEY") == "process-default-custom-key"
|
||||
|
|
@ -853,6 +857,8 @@ def test_detached_worker_scope_scrubs_non_registry_agent_creds(monkeypatch, tmp_
|
|||
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"
|
||||
assert os.environ.get("IDENTITY_ENDPOINT") == "http://169.254.169.254/msi"
|
||||
assert os.environ.get("MSI_ENDPOINT") == "http://169.254.169.254/msi"
|
||||
|
||||
|
||||
def test_expand_env_vars_does_not_leak_process_env_under_block_scope(monkeypatch):
|
||||
|
|
|
|||
Loading…
Reference in a new issue