mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 04:12:10 +03:00
fix(mitm): mask bare "Bearer <token>" header values in the inspector (#4358)
sanitizeHeaders() masks header *values* — it calls maskSecret("Bearer
<token>") with the "authorization:" key already stripped. The BEARER regex
was anchored to a literal "authorization:" prefix, so it never fired on those
values; tokens shorter than the sk-(16+)/opaque-(40+) thresholds then leaked
verbatim into the Traffic Inspector buffer (Hard Rule #12).
Found by the AgentBridge live capture: a 'Bearer sk-secret-TESTE' request
header showed up unmasked in /api/tools/traffic-inspector/requests. Real Google
OAuth tokens are long enough to be caught by LONG_TOKEN, but the Bearer pattern
must mask regardless of length.
Re-anchor BEARER to a standalone \bBearer\s+<token> (still ReDoS-safe:
bounded char class, no nested quantifiers). Masks both bare 'Bearer <token>'
header values and 'authorization: Bearer <token>' raw lines; existing cases
(sk-/ak-/pk- keys, short keys, no-secret strings) unchanged.
Tests: bare Bearer value, short opaque Bearer, realistic Google OAuth Bearer,
plus an authorization:-prefixed regression.
This commit is contained in:
committed by
GitHub
parent
4038e4de4a
commit
b710f1a7e3
@@ -6,14 +6,20 @@
|
||||
* Pattern sources: plano 11 §4.8 (origin: llm-interceptor proxy.py:310)
|
||||
*/
|
||||
|
||||
// Pre-compiled regex patterns — ORDER IS SIGNIFICANT (BEARER must run first)
|
||||
const BEARER = /(authorization:\s*Bearer\s+)[A-Za-z0-9._-]+/gi;
|
||||
// Pre-compiled regex patterns — ORDER IS SIGNIFICANT (BEARER must run first).
|
||||
// BEARER matches the token after a standalone "Bearer " — NOT only after a
|
||||
// literal "authorization:" prefix. sanitizeHeaders() masks header *values*
|
||||
// ("Bearer <token>") with the key already stripped, so a prefix-anchored regex
|
||||
// never fired there and short/opaque-but-<40 tokens leaked into the inspector
|
||||
// (found by the AgentBridge live capture). The char class is bounded + linear
|
||||
// (no nested quantifiers) to stay ReDoS-safe.
|
||||
const BEARER = /(\bBearer\s+)[A-Za-z0-9._~+/-]+=*/gi;
|
||||
const SK_KEY = /\b(sk|ak|pk)-[A-Za-z0-9_-]{16,}\b/g;
|
||||
const LONG_TOKEN = /\b[A-Za-z0-9_-]{40,}\b/g;
|
||||
|
||||
/**
|
||||
* Mask secrets in a string value.
|
||||
* - Bearer tokens: replaces token after "Bearer " with "***"
|
||||
* - Bearer tokens: replaces the token after any "Bearer " with "***"
|
||||
* - sk-/ak-/pk- keys: keeps first 6 chars + last 2 chars
|
||||
* - Long opaque tokens (≥40 chars): keeps first 4 chars + last 2 chars
|
||||
*/
|
||||
|
||||
@@ -61,3 +61,28 @@ test("maskSecret — short sk- key below 16 chars is NOT masked", () => {
|
||||
const shortKey = "sk-shortkey";
|
||||
assert.equal(maskSecret(shortKey), shortKey);
|
||||
});
|
||||
|
||||
// Regression: sanitizeHeaders() masks header *values* ("Bearer <token>" with the
|
||||
// "authorization:" key already stripped). The previous prefix-anchored BEARER
|
||||
// regex never fired there, so short/opaque-<40 Bearer tokens leaked into the
|
||||
// Traffic Inspector (found by the AgentBridge live capture).
|
||||
test("maskSecret — Bearer token in a bare header value (no authorization: prefix) is masked", () => {
|
||||
const result = maskSecret("Bearer sk-secret-TESTE");
|
||||
assert.equal(result, "Bearer ***");
|
||||
});
|
||||
|
||||
test("maskSecret — a short opaque Bearer token is still masked", () => {
|
||||
assert.equal(maskSecret("Bearer abc123"), "Bearer ***");
|
||||
});
|
||||
|
||||
test("maskSecret — a realistic Google OAuth Bearer value is masked whole", () => {
|
||||
const result = maskSecret("Bearer ya29.a0AfH6SMxLONGTOKEN_1234567890-abcdefghij");
|
||||
assert.equal(result, "Bearer ***");
|
||||
assert.ok(!result.includes("LONGTOKEN"), "no part of the token should survive");
|
||||
});
|
||||
|
||||
test("maskSecret — 'authorization: Bearer <token>' still masks (no regression)", () => {
|
||||
const result = maskSecret("authorization: Bearer sk-proj-abcdefghijklmnop");
|
||||
assert.ok(result.startsWith("authorization: Bearer ***"), `got: ${result}`);
|
||||
assert.ok(!result.includes("abcdefghijklmnop"));
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user