fix(security): add Groq, xAI and OpenAI-compatible key shapes to the credential catalog - #13744
Merged
Merged
Conversation
…credential catalog GHSA-r4q7-7f24-m29p. `CREDENTIAL_PATTERNS` (open-sse/utils/credentialPatterns.ts) is the single catalog iterated in order by both the opt-in credential-masker guardrail and the public error sanitizer. It had no entry for Groq (`gsk_`) or xAI (`xai-`), and only knew the exact 48-char OpenAI `sk-` form. Measured on the release tip before this change: | Shape | public sanitizer | guardrail | |------------------------------|------------------|-----------| | Groq gsk_ + 52 | LEAK | LEAK | | xAI xai- + 80 | LEAK | LEAK | | DeepSeek sk- + 32 hex | redacted | LEAK | | sk- + 20/36/40/51 (not 48) | redacted | LEAK | The public path already caught every `sk-` shape through STRONG_CREDENTIAL_TOKEN, so the advisory's "both layers" framing only holds for gsk_/xai-; for the sk- family the exposure was the guardrail. Adds `groq` and `xai` after `anthropic_alt`, and a generic `openai_compatible` `sk-` fallback as the LAST entry. Ordering matters: both consumers replace as they iterate, so `openai_proj`, `openai` and `anthropic*` stamp their specific label first and the fallback only sees shapes nothing else claimed. The lookbehind mirrors STRONG_CREDENTIAL_TOKEN so `risk-…`-style words do not match. All three regexes are a fixed prefix plus one bounded character class — linear, no nested quantifiers. Tests are red-first: the new guardrail cases (bare / sentence / JSON-body contexts per shape, plus label-ordering and negative cases) and the catalog coverage array in error-sensitive-redaction both failed on the tip. Follow-ups deliberately left out of scope: `tskey-auth-` (Tailscale) was never in the catalog, and the guardrail does not decode `\uXXXX` escapes the way the public path does.
muhamadgalihsaputra
pushed a commit
to niyatna/NiyatnaRoute
that referenced
this pull request
Sep 27, 2026
…credential catalog (diegosouzapw#13744) GHSA-r4q7-7f24-m29p. `CREDENTIAL_PATTERNS` (open-sse/utils/credentialPatterns.ts) is the single catalog iterated in order by both the opt-in credential-masker guardrail and the public error sanitizer. It had no entry for Groq (`gsk_`) or xAI (`xai-`), and only knew the exact 48-char OpenAI `sk-` form. Measured on the release tip before this change: | Shape | public sanitizer | guardrail | |------------------------------|------------------|-----------| | Groq gsk_ + 52 | LEAK | LEAK | | xAI xai- + 80 | LEAK | LEAK | | DeepSeek sk- + 32 hex | redacted | LEAK | | sk- + 20/36/40/51 (not 48) | redacted | LEAK | The public path already caught every `sk-` shape through STRONG_CREDENTIAL_TOKEN, so the advisory's "both layers" framing only holds for gsk_/xai-; for the sk- family the exposure was the guardrail. Adds `groq` and `xai` after `anthropic_alt`, and a generic `openai_compatible` `sk-` fallback as the LAST entry. Ordering matters: both consumers replace as they iterate, so `openai_proj`, `openai` and `anthropic*` stamp their specific label first and the fallback only sees shapes nothing else claimed. The lookbehind mirrors STRONG_CREDENTIAL_TOKEN so `risk-…`-style words do not match. All three regexes are a fixed prefix plus one bounded character class — linear, no nested quantifiers. Tests are red-first: the new guardrail cases (bare / sentence / JSON-body contexts per shape, plus label-ordering and negative cases) and the catalog coverage array in error-sensitive-redaction both failed on the tip. Follow-ups deliberately left out of scope: `tskey-auth-` (Tailscale) was never in the catalog, and the guardrail does not decode `\uXXXX` escapes the way the public path does.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the privately reported GHSA-r4q7-7f24-m29p.
CREDENTIAL_PATTERNS(open-sse/utils/credentialPatterns.ts) is the single catalog both theopt-in credential-masker guardrail and the public error sanitizer iterate. It had no entry for
Groq or xAI, and only knew the exact 48-char OpenAI
sk-form.Measured on the release tip before this change
gsk_+ 52xai-+ 80sk-+ 32 hexsk-+ 20 / 36 / 40 / 51 (not 48)sk-+ 48Each shape was probed bare, inside a sentence and inside a JSON error body. The advisory says
both layers leak every
sk-variant — that part is overstated: the public path already caughtall of them through
STRONG_CREDENTIAL_TOKEN. For thesk-family the exposure was the guardrail.Change
groqandxaientries afteranthropic_alt.openai_compatiblesk-fallback as the last entry. Order matters: both consumersreplace as they iterate, so
openai_proj/openai/anthropic*stamp their specific labelfirst and the fallback only sees shapes nothing else claimed. Its lookbehind mirrors
STRONG_CREDENTIAL_TOKEN, so words likerisk-…do not match.quantifiers.
Validation (TDD)
typecheck:core,check:open-sse-typecheckNote on the one red:
sanitizers.property.test.ts→terminates on long adversarial input (ReDoS guard)hit 3034 ms while five validation runs were in parallel on a box at load average57. Benchmarking
sanitizeErrorMessageon the untouched tip and on this branch gaveindistinguishable times, and those times do not grow with input length (1 000 → 20 000 chars),
which is the opposite of what ReDoS looks like. The test input (
aaa@bbb.com 111) also matchesnone of the three new patterns.
Out of scope, left as follow-ups
tskey-auth-…(Tailscale) was never in the catalog.\uXXXXescapes the way the public path does.