fix(codex): strip nested extra_body retention at the consumer Codex wire

The wire guard only removed the top-level prompt_cache_retention kwarg, but
the OpenAI SDK merges extra_body into the outgoing JSON body, so a nested
extra_body.prompt_cache_retention reaches chatgpt.com/backend-api/codex just
the same and still triggers the non-retryable HTTP 400. Both injection
vectors are real and probe-verified: the Relay overlay's 'key not in
baseline' arm admits an interceptor-added extra_body, and
request_overrides={'extra_body': {...}} lands verbatim in build_kwargs
output.

Close the gap in the same helper: strip the nested field too (copy-on-write,
never mutating the caller's mapping), drop extra_body entirely when it
empties, and log the same warning. Compatible endpoints keep nested
retention untouched.

Mutation-verified: removing the extra_body leg fails both new nested tests.

Reported by egilewski's review on #89969.
This commit is contained in:
kshitijk4poor
2026-08-20 02:06:11 +05:30
committed by kshitij
parent ba4bc39afd
commit 26530e7df5
2 changed files with 81 additions and 2 deletions

View File

@@ -1319,7 +1319,9 @@ def _sanitize_consumer_codex_request(
Explicit ``request_overrides`` are subject to the same endpoint contract:
unsupported retention is dropped with a warning instead of being sent and
rejected by the provider.
rejected by the provider. The check covers both the top-level kwarg and a
nested ``extra_body`` entry — the OpenAI SDK merges ``extra_body`` into
the outgoing JSON body, so either shape reaches the endpoint.
"""
sanitized = dict(request)
# Resolved defensively on purpose: run_codex_stream is also driven with
@@ -1330,8 +1332,26 @@ def _sanitize_consumer_codex_request(
is_consumer_codex = (
bool(backend_predicate()) if callable(backend_predicate) else False
)
if is_consumer_codex and "prompt_cache_retention" in sanitized:
if not is_consumer_codex:
return sanitized
dropped = False
if "prompt_cache_retention" in sanitized:
sanitized.pop("prompt_cache_retention")
dropped = True
# The OpenAI SDK merges ``extra_body`` into the outgoing JSON body, so a
# nested ``extra_body.prompt_cache_retention`` reaches the endpoint just
# like the top-level field would. Copy before editing — the caller's
# mapping must not be mutated — and drop the mapping when it empties.
extra_body = sanitized.get("extra_body")
if isinstance(extra_body, dict) and "prompt_cache_retention" in extra_body:
extra_body = dict(extra_body)
extra_body.pop("prompt_cache_retention")
if extra_body:
sanitized["extra_body"] = extra_body
else:
sanitized.pop("extra_body")
dropped = True
if dropped:
logger.warning(
"Dropped unsupported prompt_cache_retention at consumer Codex "
"wire boundary (model=%s).",

View File

@@ -602,6 +602,65 @@ def test_consumer_codex_wire_guard_preserves_compatible_endpoint_retention():
assert sanitized is not request
def test_consumer_codex_wire_guard_strips_nested_extra_body_retention(caplog):
"""The SDK merges ``extra_body`` into the JSON body, so a nested
``extra_body.prompt_cache_retention`` reaches the endpoint exactly like the
top-level field — the guard must strip both shapes (#89897)."""
from agent.codex_runtime import _sanitize_consumer_codex_request
agent = SimpleNamespace(_is_codex_backend=lambda: True, model="gpt-5.6-sol")
extra_body = {"prompt_cache_retention": "24h", "unrelated": "keep"}
request = {
"model": "gpt-5.6-sol",
"prompt_cache_key": "cache-key-sentinel",
"extra_body": extra_body,
}
with caplog.at_level("WARNING", logger="agent.codex_runtime"):
sanitized = _sanitize_consumer_codex_request(agent, request)
assert "prompt_cache_retention" not in sanitized.get("extra_body", {})
# Unrelated extra_body entries survive; the caller's mapping is untouched.
assert sanitized["extra_body"]["unrelated"] == "keep"
assert extra_body["prompt_cache_retention"] == "24h"
assert sanitized["prompt_cache_key"] == "cache-key-sentinel"
assert any(
"Dropped unsupported prompt_cache_retention" in record.message
for record in caplog.records
)
def test_consumer_codex_wire_guard_drops_emptied_extra_body():
"""When retention was extra_body's only entry, the emptied mapping is
removed rather than sent as ``extra_body={}``."""
from agent.codex_runtime import _sanitize_consumer_codex_request
agent = SimpleNamespace(_is_codex_backend=lambda: True, model="gpt-5.6-sol")
request = {
"model": "gpt-5.6-sol",
"extra_body": {"prompt_cache_retention": "24h"},
}
sanitized = _sanitize_consumer_codex_request(agent, request)
assert "extra_body" not in sanitized
def test_consumer_codex_wire_guard_preserves_nested_retention_on_compatible_endpoint():
"""Compatible endpoints keep a nested extra_body retention untouched."""
from agent.codex_runtime import _sanitize_consumer_codex_request
agent = SimpleNamespace(_is_codex_backend=lambda: False)
request = {
"model": "openai.gpt-5.5",
"extra_body": {"prompt_cache_retention": "24h"},
}
sanitized = _sanitize_consumer_codex_request(agent, request)
assert sanitized["extra_body"]["prompt_cache_retention"] == "24h"
@pytest.mark.parametrize(
"base_url,model,expect_dropped",
[