Require Secrets Manager key for Anthropic client

Remove the silent fallback to a plaintext ANTHROPIC_API_KEY env var
in both email processors; require ANTHROPIC_API_KEY_SECRET_ARN and
raise if absent so a misconfigured deploy fails loudly instead of
using an unmanaged key.

Adapted from f175323 on security/sweep-2026-06-17. The From-header
sender-domain allowlist from that commit is intentionally dropped:
the From header is spoofable (INFRA-107, confirmed critical) and
sender authentication is being reworked in a separate PR.

Refs: INFRA-107
This commit is contained in:
Adam Moussa 2026-07-15 18:48:06 -04:00
parent d0c696a434
commit 55a647537c
No known key found for this signature in database
2 changed files with 30 additions and 16 deletions

View file

@ -211,14 +211,21 @@ The Coupa commodity/category field if present in the email (e.g., "Maintenance -
def get_anthropic_client() -> anthropic.Anthropic:
"""Create Anthropic client, fetching API key from Secrets Manager."""
if ANTHROPIC_API_KEY_SECRET_ARN:
secrets = boto3.client("secretsmanager")
secret = secrets.get_secret_value(SecretId=ANTHROPIC_API_KEY_SECRET_ARN)
api_key = secret["SecretString"]
return anthropic.Anthropic(api_key=api_key)
# Fall back to ANTHROPIC_API_KEY env var (for local testing)
return anthropic.Anthropic()
"""Create Anthropic client, fetching API key from Secrets Manager.
Requires ANTHROPIC_API_KEY_SECRET_ARN to be set. The previous silent
fallback to a plaintext ANTHROPIC_API_KEY env var is removed: a misconfigured
deploy must fail loudly rather than quietly run on an unmanaged key.
"""
if not ANTHROPIC_API_KEY_SECRET_ARN:
raise RuntimeError(
"ANTHROPIC_API_KEY_SECRET_ARN is not set; refusing to fall back to a "
"plaintext API key. Configure the Secrets Manager ARN."
)
secrets = boto3.client("secretsmanager")
secret = secrets.get_secret_value(SecretId=ANTHROPIC_API_KEY_SECRET_ARN)
api_key = secret["SecretString"]
return anthropic.Anthropic(api_key=api_key)
def parse_raw_email(raw_bytes: bytes) -> dict:

View file

@ -66,14 +66,21 @@ Rules:
def get_anthropic_client() -> anthropic.Anthropic:
"""Create Anthropic client, fetching API key from Secrets Manager if configured."""
if ANTHROPIC_API_KEY_SECRET_ARN:
secrets = boto3.client("secretsmanager")
secret = secrets.get_secret_value(SecretId=ANTHROPIC_API_KEY_SECRET_ARN)
api_key = secret["SecretString"]
return anthropic.Anthropic(api_key=api_key)
# Fall back to ANTHROPIC_API_KEY env var (for local testing)
return anthropic.Anthropic()
"""Create Anthropic client, fetching API key from Secrets Manager.
Requires ANTHROPIC_API_KEY_SECRET_ARN to be set. The previous silent
fallback to a plaintext ANTHROPIC_API_KEY env var is removed: a misconfigured
deploy must fail loudly rather than quietly run on an unmanaged key.
"""
if not ANTHROPIC_API_KEY_SECRET_ARN:
raise RuntimeError(
"ANTHROPIC_API_KEY_SECRET_ARN is not set; refusing to fall back to a "
"plaintext API key. Configure the Secrets Manager ARN."
)
secrets = boto3.client("secretsmanager")
secret = secrets.get_secret_value(SecretId=ANTHROPIC_API_KEY_SECRET_ARN)
api_key = secret["SecretString"]
return anthropic.Anthropic(api_key=api_key)
def parse_raw_email(raw_bytes: bytes) -> dict: