From b49ac2fd9e20ba4d501053620e3b5c104fac66f5 Mon Sep 17 00:00:00 2001 From: Ahmed Allam Date: Sat, 3 Oct 2026 22:42:43 +0000 Subject: [PATCH] fix(preflight): check extra headers for subscription models and the dedupe key as sent --- strix/interface/scan_setup.py | 14 ++++----- tests/test_preflight_header_values.py | 43 +++++++++++++++++++++++++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/strix/interface/scan_setup.py b/strix/interface/scan_setup.py index 838a1d1e..2b070bd0 100644 --- a/strix/interface/scan_setup.py +++ b/strix/interface/scan_setup.py @@ -67,10 +67,10 @@ def _first_non_ascii(value: str) -> tuple[int, str] | None: def _header_candidates( - prefix: str, api_key: str | None, extra_headers: dict[str, str] | None + prefix: str, model: str | None, api_key: str | None, extra_headers: dict[str, str] | None ) -> list[tuple[str, str]]: candidates: list[tuple[str, str]] = [] - if api_key: + if api_key and not codex.subscription_model(model): candidates.append((f"{prefix}LLM_API_KEY", api_key)) for header, value in (extra_headers or {}).items(): candidates.append((f"{prefix}LLM_EXTRA_HEADERS header name {header!r}", header)) @@ -81,11 +81,11 @@ def _header_candidates( def check_header_safe_credentials(settings: Settings) -> None: llm = settings.llm dedupe = settings.dedupe - candidates: list[tuple[str, str]] = [] - if not codex.subscription_model(llm.model): - candidates += _header_candidates("", llm.api_key, llm.extra_headers) - if dedupe.model and not codex.subscription_model(dedupe.model): - candidates += _header_candidates("DEDUPE_", dedupe.api_key, dedupe.extra_headers) + candidates = _header_candidates("", llm.model, llm.api_key, llm.extra_headers) + if dedupe.model: + candidates += _header_candidates( + "DEDUPE_", dedupe.model, (dedupe.api_key or "").strip(), dedupe.extra_headers + ) for setting, value in candidates: found = _first_non_ascii(value) if found is None: diff --git a/tests/test_preflight_header_values.py b/tests/test_preflight_header_values.py index 21c0c531..d6595adf 100644 --- a/tests/test_preflight_header_values.py +++ b/tests/test_preflight_header_values.py @@ -82,6 +82,17 @@ def test_subscription_model_ignores_an_unused_main_key(monkeypatch: pytest.Monke check_header_safe_credentials(settings) +def test_subscription_model_still_checks_extra_headers(monkeypatch: pytest.MonkeyPatch) -> None: + settings = _settings( + monkeypatch, + STRIX_LLM="chatgpt/gpt-5", + LLM_EXTRA_HEADERS='{"X-Team": "s\u00e9curit\u00e9"}', + ) + + with pytest.raises(ValueError, match=r"LLM_EXTRA_HEADERS value for 'X-Team'.*U\+00E9"): + check_header_safe_credentials(settings) + + def test_dedupe_key_is_checked_when_a_dedupe_model_is_set( monkeypatch: pytest.MonkeyPatch, ) -> None: @@ -89,10 +100,38 @@ def test_dedupe_key_is_checked_when_a_dedupe_model_is_set( monkeypatch, LLM_API_KEY="sk-plain", STRIX_DEDUPE_MODEL="openai/gpt-4o-mini", - DEDUPE_LLM_API_KEY="sk-dedupe\u00a0", + DEDUPE_LLM_API_KEY="sk-de\u00a0dupe", ) - with pytest.raises(ValueError, match=r"DEDUPE_LLM_API_KEY.*U\+00A0"): + with pytest.raises(ValueError, match=r"DEDUPE_LLM_API_KEY.*U\+00A0.*position 6 of 10"): + check_header_safe_credentials(settings) + + +def test_dedupe_key_is_checked_as_sent_after_resolve_dedupe_model_strips_it( + monkeypatch: pytest.MonkeyPatch, +) -> None: + settings = _settings( + monkeypatch, + LLM_API_KEY="sk-plain", + STRIX_DEDUPE_MODEL="openai/gpt-4o-mini", + DEDUPE_LLM_API_KEY="\u00a0sk-dedupe\u00a0", + ) + + check_header_safe_credentials(settings) + + +def test_dedupe_subscription_model_checks_headers_but_not_the_key( + monkeypatch: pytest.MonkeyPatch, +) -> None: + settings = _settings( + monkeypatch, + LLM_API_KEY="sk-plain", + STRIX_DEDUPE_MODEL="chatgpt/gpt-5", + DEDUPE_LLM_API_KEY="sk-dedupe\u201d", + DEDUPE_LLM_EXTRA_HEADERS='{"X-Team": "s\u00e9curit\u00e9"}', + ) + + with pytest.raises(ValueError, match=r"DEDUPE_LLM_EXTRA_HEADERS value for 'X-Team'"): check_header_safe_credentials(settings)