mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-18 21:22:28 +03:00
fix(a2a): use a constant-time bearer compare in /api/a2a/tasks (#10544)
* fix(a2a): use a constant-time bearer compare in /api/a2a/tasks * fix(a2a): drop new Function from tasks-auth test in favor of dynamic import The regression test for the constant-time bearer compare loaded tokensMatch and authenticateA2A by regex-extracting their source and eval'ing it via new Function, which trips the repo's no-new-func/no-implied-eval ESLint rules (error-level everywhere, including tests). Export both helpers as a test seam from the route module (mirrors the existing bridgeSecretMatches/authRouteInternals pattern) and import them directly in the test instead. Also drops the now-unused eslint-disable directives. Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com> --------- Co-authored-by: adevwithpurpose <adevwithpurpose@users.noreply.github.com> Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { timingSafeEqual } from "node:crypto";
|
||||
import { NextResponse } from "next/server";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -68,12 +69,32 @@ const delegationSchema = z.object({
|
||||
.optional(),
|
||||
});
|
||||
|
||||
/** Mesma semântica de auth do JSON-RPC A2A (src/app/a2a/route.ts): Bearer vs OMNIROUTE_API_KEY; aberto se não configurada. */
|
||||
function authenticateA2A(request: Request): boolean {
|
||||
/**
|
||||
* Constant-time comparison of the presented bearer token against the configured
|
||||
* key. A plain `===` short-circuits on the first differing byte, leaking the
|
||||
* length of the shared prefix through response timing; `timingSafeEqual` does
|
||||
* not. It requires equal-length buffers, so mismatched lengths are rejected up
|
||||
* front (the length itself is not secret).
|
||||
*
|
||||
* Exported as a test seam only — not part of the route contract.
|
||||
*/
|
||||
export function tokensMatch(provided: string, expected: string): boolean {
|
||||
const a = Buffer.from(provided);
|
||||
const b = Buffer.from(expected);
|
||||
if (a.length !== b.length) return false;
|
||||
return timingSafeEqual(a, b);
|
||||
}
|
||||
|
||||
/**
|
||||
* Mesma semântica de auth do JSON-RPC A2A (src/app/a2a/route.ts): Bearer vs OMNIROUTE_API_KEY; aberto se não configurada.
|
||||
*
|
||||
* Exported as a test seam only — not part of the route contract.
|
||||
*/
|
||||
export function authenticateA2A(request: Request): boolean {
|
||||
const configuredKey = process.env.OMNIROUTE_API_KEY;
|
||||
if (!configuredKey) return true;
|
||||
const token = (request.headers.get("authorization") || "").replace(/^Bearer\s+/i, "");
|
||||
return token === configuredKey;
|
||||
return tokensMatch(token, configuredKey);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user