From 78fca260604778e1aed6474c52b39f8982e2c7cd Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Wed, 22 Jul 2026 21:06:53 +0000 Subject: [PATCH] Fix timing attack vulnerability in token validation --- .jules/sentinel.md | 4 ++++ src/cli/commands/serve.ts | 9 +++------ 2 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 .jules/sentinel.md diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..445506d4 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2026-07-22 - Fix Timing Attack Vulnerability in Token Comparison +**Vulnerability:** Checking buffer lengths before calling `crypto.timingSafeEqual` creates a short-circuit fast path that leaks the secret's length via timing differences. +**Learning:** `crypto.timingSafeEqual` throws an error if buffers have different lengths, so checking lengths first seems necessary, but it introduces a timing attack vulnerability. +**Prevention:** Always hash both secrets to a fixed length (e.g., using `crypto.createHash('sha256')`) before comparison to ensure true constant-time evaluation. diff --git a/src/cli/commands/serve.ts b/src/cli/commands/serve.ts index 2c37293e..505ac4bd 100644 --- a/src/cli/commands/serve.ts +++ b/src/cli/commands/serve.ts @@ -346,13 +346,10 @@ export async function handleServeCommand(_options: unknown, command: Command) { let isAuthenticated = false; if (scheme?.toLowerCase() === 'bearer' && token) { - const tokenBuffer = Buffer.from(token); + const tokenHash = crypto.createHash('sha256').update(token).digest(); for (const authToken of authTokens) { - const authTokenBuffer = Buffer.from(authToken); - if ( - tokenBuffer.length === authTokenBuffer.length && - crypto.timingSafeEqual(tokenBuffer, authTokenBuffer) - ) { + const expectedHash = crypto.createHash('sha256').update(authToken).digest(); + if (crypto.timingSafeEqual(tokenHash, expectedHash)) { isAuthenticated = true; break; }