Files
OmniRoute/tests/unit/gemini-responses-error-redaction.test.ts
Diego Rodrigues de Sa e Souza 2265ce761f fix(security): harden public error boundaries (#12506)
Validado sobre o tip de `release/v3.8.51` depois de reconciliar com o #12620, que entrou primeiro nesta mesma sessão e ataca a mesma classe de problema por outra arquitetura.

**A colisão e como foi resolvida.** O #12620 consertou o GHSA-qv45-56jc-4wmj adicionando `RAW_CREDENTIAL_PATTERNS` a `error.ts` e importando-os em `upstreamErrorPassthrough.ts`. Este PR resolve o mesmo problema quebrando `error.ts` em `errorSanitization.ts` + `errorPathRedaction.ts`. Mantive a divisão em módulos deste PR, porque ao comparar os dois vocabulários o dele já era mais amplo: o `STRONG_CREDENTIAL_TOKEN` daqui cobre `sk-`/`sk_` **com lookbehind e uma variante para a forma embutida** (que pega `sk-proj-…`), mais Slack `xox-`, AWS `AKIA`/`ASIA`, `github_pat_`/`ghp_`/`glpat-` e JWT de três segmentos.

A única forma que o #12620 carregava e este conjunto não tinha era a chave do Google (`AIza…`) — adicionada aqui, com o mesmo quantificador limitado que os irmãos usam (AGENTS.md → PII §1, já que isso roda sobre corpos upstream não confiáveis).

**A verificação não foi por inspeção.** Rodei as suítes do próprio #12620 contra esta estrutura: **48/48** em `error-sanitizer-sk-key-qv45`, `bifrost-relay-response-leak-9m72`, `search-baseurl-client-override-3f8g` e `search-baseurl-ssrf-guard` — incluindo a asserção anti-drift daquela suíte, que é o oráculo certo aqui: *para todo corpo que a camada de passthrough recusa como vazante, o sanitizador de fallback não pode devolvê-lo intacto*. Ela passa, então a propriedade de segurança dos três GHSAs sobrevive à troca de arquitetura.

Os 21 arquivos de teste deste PR: **259/259**. `typecheck:core` limpo.
2026-09-03 21:31:13 -03:00

40 lines
1.4 KiB
TypeScript

import assert from "node:assert/strict";
import test from "node:test";
import { translateResponse, initState } from "../../open-sse/translator/index.ts";
import { FORMATS } from "../../open-sse/translator/formats.ts";
test("Gemini keeps raw failure wording internal but projects response.completed.error", () => {
const state = initState(FORMATS.OPENAI_RESPONSES);
const hostileMessage =
"Gemini failed at /srv/omniroute/private-runtime.ts:71:3 token=sk-gemini-secret-123456";
const translated = translateResponse(
FORMATS.GEMINI,
FORMATS.OPENAI_RESPONSES,
{
response: {
error: {
code: 503,
status: "UNAVAILABLE",
message: hostileMessage,
api_key: "sk-gemini-secret-abcdef",
},
},
},
state
);
assert.equal(translated?.length ?? 0, 0);
assert.match(state.upstreamError?.message ?? "", /private-runtime\.ts/);
const flushed = translateResponse(FORMATS.GEMINI, FORMATS.OPENAI_RESPONSES, null, state);
const completed = flushed.find((event) => event?.data?.type === "response.completed");
assert.ok(completed);
assert.equal(completed.data.response.status, "failed");
const publicError = JSON.stringify(completed.data.response.error);
assert.doesNotMatch(publicError, /private-runtime\.ts/);
assert.doesNotMatch(publicError, /sk-gemini-secret/);
assert.doesNotMatch(publicError, /api_key/);
assert.equal(completed.data.response.error.code, "503");
});