diff --git a/.jules/sentinel.md b/.jules/sentinel.md new file mode 100644 index 00000000..f8c95703 --- /dev/null +++ b/.jules/sentinel.md @@ -0,0 +1,4 @@ +## 2025-02-23 - Timing Attack Vulnerability in Secret Validation +**Vulnerability:** A timing attack vulnerability in A2A token authentication where the code checked if `tokenBuffer.length === authTokenBuffer.length` before calling `crypto.timingSafeEqual`. +**Learning:** Using a short-circuit length check before a constant-time comparison leaks the length of the expected secret via observable timing differences, allowing an attacker to deduce the secret's length and reducing the search space for brute force attacks. +**Prevention:** When validating secrets, always hash both the provided input and the expected secret using a cryptographically secure hash function (e.g., SHA-256) before passing them to a constant-time comparison function like `timingSafeEqual`. This ensures the inputs to `timingSafeEqual` are always of the same fixed length, eliminating the need for length checks entirely. diff --git a/src/cli/commands/serve.ts b/src/cli/commands/serve.ts index 2c37293e..4bdc2eac 100644 --- a/src/cli/commands/serve.ts +++ b/src/cli/commands/serve.ts @@ -349,10 +349,9 @@ export async function handleServeCommand(_options: unknown, command: Command) { const tokenBuffer = Buffer.from(token); for (const authToken of authTokens) { const authTokenBuffer = Buffer.from(authToken); - if ( - tokenBuffer.length === authTokenBuffer.length && - crypto.timingSafeEqual(tokenBuffer, authTokenBuffer) - ) { + const tokenHash = crypto.createHash('sha256').update(tokenBuffer).digest(); + const authTokenHash = crypto.createHash('sha256').update(authTokenBuffer).digest(); + if (crypto.timingSafeEqual(tokenHash, authTokenHash)) { isAuthenticated = true; break; } diff --git a/tests/integration/prompt_templates.test.ts b/tests/integration/prompt_templates.test.ts index 21905259..f1360d78 100644 --- a/tests/integration/prompt_templates.test.ts +++ b/tests/integration/prompt_templates.test.ts @@ -36,7 +36,9 @@ describe('Prompt templates', () => { expect(planSystem).toContain('You are SalmonLoop.'); expect(patchSystem).toContain('You are PATCH, a phase-native diff compiler.'); - expect(autopilotSystem).toContain('You are a senior software engineer running in "autopilot" mode.'); + expect(autopilotSystem).toContain( + 'You are a senior software engineer running in "autopilot" mode.', + ); expect(answerSystem).toContain('You are a coding assistant in "answer" mode.'); expect(researchSystem).toContain('You are a research assistant.'); });