From 37698ef78e6decd237c1bea6bd6e6c537f5a8082 Mon Sep 17 00:00:00 2001 From: "google-labs-jules[bot]" <161369871+google-labs-jules[bot]@users.noreply.github.com> Date: Sun, 31 May 2026 05:21:20 +0000 Subject: [PATCH] fix: prevent timing leak in timingSafeEqual by enforcing Uint8Array inputs This commit updates `timingSafeEqual` call sites in `apps/quill/src/routes/admin.ts` and `apps/quill/src/lib/polar.ts` to convert string arguments into `Uint8Array`s using `new TextEncoder().encode()` before comparing them. This prevents JIT optimizations and JavaScript engine string storage mechanisms from introducing timing side-channel leaks. Co-authored-by: aloewright <3641844+aloewright@users.noreply.github.com> --- apps/quill/src/lib/polar.ts | 9 ++++++++- apps/quill/src/routes/admin.ts | 8 +++++++- 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/apps/quill/src/lib/polar.ts b/apps/quill/src/lib/polar.ts index 699056c..324958b 100644 --- a/apps/quill/src/lib/polar.ts +++ b/apps/quill/src/lib/polar.ts @@ -184,7 +184,14 @@ export async function verifyWebhook( const candidates = sigHeader.split(" "); for (const c of candidates) { const [version, sig] = c.split(","); - if (version === "v1" && sig && (await timingSafeEqual(sig, expected))) { + if ( + version === "v1" && + sig && + (await timingSafeEqual( + new TextEncoder().encode(sig), + new TextEncoder().encode(expected) + )) + ) { return true; } } diff --git a/apps/quill/src/routes/admin.ts b/apps/quill/src/routes/admin.ts index 063cd53..2b4ef19 100644 --- a/apps/quill/src/routes/admin.ts +++ b/apps/quill/src/routes/admin.ts @@ -19,7 +19,13 @@ adminRouter.use("*", async (c, next) => { throw new HTTPException(503, { message: "Admin API not configured." }); } const provided = c.req.header("x-admin-key"); - if (!provided || !(await timingSafeEqual(expected, provided))) { + if ( + !provided || + !(await timingSafeEqual( + new TextEncoder().encode(expected), + new TextEncoder().encode(provided) + )) + ) { throw new HTTPException(401, { message: "Invalid admin key." }); } await next();