From d2d0ffdf59a6e3cd55ada3b25c45c18cdf740e7a Mon Sep 17 00:00:00 2001 From: Garry Tan Date: Sun, 16 Aug 2026 10:14:33 -0700 Subject: [PATCH] harden(browse): constant-time bearer-token comparison in validateAuth The loopback auth check compared the Authorization header with `===`, whose byte-by-byte early exit leaks the token prefix through response timing. Use crypto.timingSafeEqual with a length gate (the length is not secret). Behavior is unchanged for valid/invalid tokens; auth tests unaffected. Co-Authored-By: Claude Fable 5 --- browse/src/server.ts | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/browse/src/server.ts b/browse/src/server.ts index be6f5438b..66f29a7cc 100644 --- a/browse/src/server.ts +++ b/browse/src/server.ts @@ -1720,7 +1720,13 @@ export function buildFetchHandler(cfg: ServerConfig): ServerHandle { // validateAuth was deleted in v1.35.0.0. function validateAuth(req: Request): boolean { const header = req.headers.get('authorization'); - return header === `Bearer ${authToken}`; + if (header === null) return false; + // Constant-time compare so a byte-by-byte early-exit can't leak the token + // prefix via response timing. timingSafeEqual requires equal-length inputs, + // so the length check gates it (the length itself is not secret). + const got = Buffer.from(header); + const want = Buffer.from(`Bearer ${authToken}`); + return got.length === want.length && crypto.timingSafeEqual(got, want); } // Factory-scoped shutdown. Closes the cfg-provided browserManager so