mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-09-21 14:22:14 +03:00
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.
53 lines
1.7 KiB
TypeScript
53 lines
1.7 KiB
TypeScript
/**
|
|
* chatCore streaming error-result helpers (Quality Gate v2 / Fase 9 — chatCore god-file
|
|
* decomposition, #3501).
|
|
*
|
|
* Extracted from chatCore: identify semaphore capacity errors, build a sanitized SSE error result
|
|
* (an `data: {...}\n\ndata: [DONE]\n\n` body wrapped in an event-stream Response), and pull a string
|
|
* error code off an unknown error. Side-effect-free; behaviour is byte-identical to the previous
|
|
* module-level functions.
|
|
*/
|
|
|
|
import { buildErrorBody } from "../../utils/error.ts";
|
|
|
|
export function isSemaphoreCapacityError(error: unknown): error is Error & { code: string } {
|
|
return (
|
|
!!error &&
|
|
typeof error === "object" &&
|
|
((error as { code?: unknown }).code === "SEMAPHORE_TIMEOUT" ||
|
|
(error as { code?: unknown }).code === "SEMAPHORE_QUEUE_FULL")
|
|
);
|
|
}
|
|
|
|
export function createStreamingErrorResult(
|
|
statusCode: number,
|
|
message: string,
|
|
code?: string,
|
|
type?: string
|
|
) {
|
|
const errorBody = buildErrorBody(statusCode, message, undefined, { code, type });
|
|
|
|
const body = `data: ${JSON.stringify(errorBody)}\n\ndata: [DONE]\n\n`;
|
|
|
|
return {
|
|
success: false as const,
|
|
status: statusCode,
|
|
error: message,
|
|
response: new Response(body, {
|
|
status: statusCode,
|
|
headers: {
|
|
"Content-Type": "text/event-stream",
|
|
"Cache-Control": "no-cache, no-transform",
|
|
Connection: "keep-alive",
|
|
"X-Accel-Buffering": "no",
|
|
},
|
|
}),
|
|
};
|
|
}
|
|
|
|
export function getUpstreamErrorIdentifier(error: unknown): string | undefined {
|
|
if (!error || typeof error !== "object") return undefined;
|
|
const value = (error as { code?: unknown }).code;
|
|
return typeof value === "string" && value.length > 0 ? value : undefined;
|
|
}
|