fix(codex): stop injecting api key placeholder
test / unit (pull_request) Successful in 27s
test / integration (pull_request) Successful in 41s

This commit is contained in:
2026-05-29 02:39:37 -04:00
parent 50baf63669
commit cea832b21d
9 changed files with 82 additions and 41 deletions
+2 -2
View File
@@ -53,8 +53,8 @@ _RUNTIMES = {
command="codex",
image="bot-bottle-codex:latest",
dockerfile=str(_REPO_ROOT / "Dockerfile.codex"),
auth_role="codex_auth",
placeholder_env="OPENAI_API_KEY",
auth_role="",
placeholder_env="",
prompt_mode="read_prompt_file",
bypass_args=("--dangerously-bypass-approvals-and-sandbox",),
resume_args=("resume", "--last"),
+10 -10
View File
@@ -201,18 +201,18 @@ def resolve_plan(
# never lands on argv or in env_file) goes into one dict. Nothing
# mutates the host os.environ.
forwarded_env: dict[str, str] = dict(resolved.forwarded)
# When the bottle declares an egress route with the
# `claude_code_oauth` role marker, claude-code's outbound
# Authorization gets stripped + re-injected by egress. The
# agent's environ still needs *something* claude-code recognises
# as a credential or it refuses to start; ship a non-secret
# placeholder. The placeholder isn't any real token value, so
# leaking it would tell an attacker only that egress is in
# front. Manifest validation enforces singleton on this role.
# Some provider CLIs refuse to start without *some* credential
# env var even when egress will strip + re-inject the real
# Authorization header. For those providers, auth_role names the
# route marker that enables a non-secret placeholder env. Codex is
# intentionally absent here: it should use its device/ChatGPT login
# state, and an OPENAI_API_KEY placeholder would force API-key auth.
has_provider_auth = any(
provider_runtime.auth_role in r.roles for r in egress_plan.routes
provider_runtime.auth_role
and provider_runtime.auth_role in r.roles
for r in egress_plan.routes
)
if has_provider_auth:
if has_provider_auth and provider_runtime.placeholder_env:
forwarded_env[provider_runtime.placeholder_env] = "egress-placeholder"
if provider.template == "claude" and has_provider_auth:
# Belt-and-braces: turn off telemetry endpoints (statsig,
+7 -7
View File
@@ -34,12 +34,12 @@ def visible_agent_env_names(
) -> list[str]:
"""Env names worth showing in launch summaries.
Provider auth placeholders (`OPENAI_API_KEY`,
`CLAUDE_CODE_OAUTH_TOKEN`) are implementation details: they are
non-secret dummy values that satisfy the provider CLI while egress
injects the real upstream Authorization header. Showing them in
preflight makes the operator think a real key is entering the
agent, so hide only that provider-owned placeholder.
Provider auth placeholders (currently `CLAUDE_CODE_OAUTH_TOKEN`)
are implementation details: they are non-secret dummy values that
satisfy the provider CLI while egress injects the real upstream
Authorization header. Showing them in preflight makes the operator
think a real key is entering the agent, so hide only the active
provider-owned placeholder.
"""
hidden = {runtime_for(agent_provider_template).placeholder_env}
return sorted({name for name in env_names if name not in hidden})
return sorted({name for name in env_names if name and name not in hidden})
+10 -10
View File
@@ -112,18 +112,18 @@ def resolve_plan(
egress_dir.mkdir(parents=True, exist_ok=True)
egress_plan = Egress().prepare(bottle, slug, egress_dir)
# Claude-code refuses to start without *something* it
# recognises as a credential. When the bottle has an egress
# route carrying the `claude_code_oauth` role marker, egress
# strips + re-injects the real Authorization header on the
# outbound leg using a token held in egress's own environ — so
# the agent gets a non-secret placeholder here (matches the
# docker backend's forwarded_env logic in
# bot_bottle/backend/docker/prepare.py).
# Some provider CLIs refuse to start without *some* credential
# env var even when egress will strip + re-inject the real
# Authorization header. For those providers, auth_role names the
# route marker that enables a non-secret placeholder env. Codex is
# intentionally absent here: it should use its device/ChatGPT login
# state, and an OPENAI_API_KEY placeholder would force API-key auth.
has_provider_auth = any(
provider_runtime.auth_role in r.roles for r in egress_plan.routes
provider_runtime.auth_role
and provider_runtime.auth_role in r.roles
for r in egress_plan.routes
)
if has_provider_auth:
if has_provider_auth and provider_runtime.placeholder_env:
guest_env[provider_runtime.placeholder_env] = "egress-placeholder"
if provider.template == "claude" and has_provider_auth:
guest_env.setdefault("CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC", "1")
+8 -4
View File
@@ -188,6 +188,11 @@ EGRESS_AUTH_SCHEMES = ("Bearer", "token")
# logic — declare the role on whichever route
# injects the OAuth header.
#
# codex_auth: legacy marker for Codex API-key-style egress routes.
# It is still accepted for older bottle manifests, but
# no longer triggers an OPENAI_API_KEY placeholder. Codex
# bottles should prefer device/ChatGPT login state.
#
# Routes without a `role` are pure proxy entries: egress
# enforces path_allowlist + injects auth on its own, but nothing
# special happens on the agent side.
@@ -196,10 +201,9 @@ EGRESS_ROLES = frozenset({
"codex_auth",
})
# Singleton roles may appear on at most one route per bottle.
# claude_code_oauth drives a single placeholder env var; two routes
# claiming it would leave "which one is the canonical OAuth route?"
# ambiguous for any future role-aware logic.
# Singleton roles may appear on at most one route per bottle. Some
# roles drive a single provider auth path; two routes claiming one
# marker would leave "which one is canonical?" ambiguous.
EGRESS_SINGLETON_ROLES = frozenset({
"claude_code_oauth",
"codex_auth",