Skip to content

🛡️ Sentinel: [CRITICAL] Fix string timing attack risk#50

Open
aloewright wants to merge 1 commit into
mainfrom
sentinel-fix-timing-attack-5930281496800758417
Open

🛡️ Sentinel: [CRITICAL] Fix string timing attack risk#50
aloewright wants to merge 1 commit into
mainfrom
sentinel-fix-timing-attack-5930281496800758417

Conversation

@aloewright

@aloewright aloewright commented May 29, 2026

Copy link
Copy Markdown
Owner
  • 🚨 Severity: CRITICAL
  • 💡 Vulnerability: Constant-time comparison function timingSafeEqual from hono/utils/buffer was used with raw strings instead of Uint8Array buffers. This undermines the timing attack protection because string representations in JavaScript engines can cause varying execution times.
  • 🎯 Impact: Potential for timing attacks against the admin endpoints or webhook processing, enabling adversaries to deduce secure tokens.
  • 🔧 Fix: Used TextEncoder().encode() to encode strings to Uint8Array before passing to timingSafeEqual.
  • ✅ Verification: Tested using pnpm build, pnpm typecheck, pnpm check.

PR created automatically by Jules for task 5930281496800758417 started by @aloewright

Summary by CodeRabbit

  • Bug Fixes

    • Enhanced security hardening for webhook signature verification and admin authentication.
  • Documentation

    • Updated security guidelines for safe comparison operations.

Review Change Stack

Convert string parameters to Uint8Array using TextEncoder before using timingSafeEqual to avoid JS engine string implementation timing differences.

Co-authored-by: aloewright <3641844+aloewright@users.noreply.github.com>
Copilot AI review requested due to automatic review settings May 29, 2026 05:28
@google-labs-jules

Copy link
Copy Markdown
Contributor

👋 Jules, reporting for duty! I'm here to lend a hand with this pull request.

When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down.

I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job!

For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with @jules. You can find this option in the Pull Request section of your global Jules UI settings. You can always switch back!

New to Jules? Learn more at jules.google/docs.


For security, I will only act on instructions from the user who triggered this task.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented May 29, 2026

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
postpilot c9f7d90 May 29 2026, 05:29 AM

@coderabbitai

coderabbitai Bot commented May 29, 2026

Copy link
Copy Markdown

Walkthrough

This PR hardens security-critical string comparisons by applying constant-time byte-encoded equality checks. It documents the timing-safe pattern requirement and applies it to webhook signature verification and admin API key validation.

Changes

Timing-Safe Comparison Pattern

Layer / File(s) Summary
Timing-safe comparison pattern documentation
.jules/sentinel.md
Adds a sentinel entry documenting that timingSafeEqual must compare Uint8Array buffers (encoded via TextEncoder().encode()) rather than raw strings.
Webhook signature verification with encoded comparison
apps/quill/src/lib/polar.ts
verifyWebhook now pre-encodes the expected signature and each candidate signature to bytes before calling timingSafeEqual for constant-time comparison.
Admin key validation with encoded comparison
apps/quill/src/routes/admin.ts
Admin auth middleware now encodes both the expected admin key and the provided header value to bytes before calling timingSafeEqual.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly addresses the main change: fixing a critical timing-attack vulnerability by ensuring timingSafeEqual is used with Uint8Array buffers instead of strings, which is reflected across all three file modifications.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch sentinel-fix-timing-attack-5930281496800758417

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.

Warning

Review ran into problems

🔥 Problems

These MCP integrations need to be re-authenticated in the Integrations settings: Sentry


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request addresses a timing attack vulnerability by converting raw strings to Uint8Array buffers using TextEncoder before comparing them with Hono's timingSafeEqual in both polar.ts and admin.ts. The reviewer correctly identified that passing Uint8Array directly to timingSafeEqual will trigger TypeScript compilation errors because the function expects string, ArrayBuffer, or DataView. The reviewer provided actionable suggestions to resolve this by accessing the underlying .buffer property of the Uint8Array.

Comment on lines +185 to +193
const encoder = new TextEncoder();
const expectedBuf = encoder.encode(expected);
for (const c of candidates) {
const [version, sig] = c.split(",");
if (version === "v1" && sig && (await timingSafeEqual(sig, expected))) {
if (
version === "v1" &&
sig &&
(await timingSafeEqual(encoder.encode(sig), expectedBuf))
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Passing a Uint8Array (returned by TextEncoder.prototype.encode()) directly to Hono's timingSafeEqual will cause a TypeScript compilation error because timingSafeEqual expects string | ArrayBuffer | DataView, and Uint8Array is not assignable to ArrayBuffer | DataView in strict mode. Accessing the underlying .buffer property of the Uint8Array resolves this type mismatch cleanly and safely.

Suggested change
const encoder = new TextEncoder();
const expectedBuf = encoder.encode(expected);
for (const c of candidates) {
const [version, sig] = c.split(",");
if (version === "v1" && sig && (await timingSafeEqual(sig, expected))) {
if (
version === "v1" &&
sig &&
(await timingSafeEqual(encoder.encode(sig), expectedBuf))
) {
const encoder = new TextEncoder();
const expectedBuf = encoder.encode(expected).buffer;
for (const c of candidates) {
const [version, sig] = c.split(",");
if (
version === "v1" &&
sig &&
(await timingSafeEqual(encoder.encode(sig).buffer, expectedBuf))
) {

Comment on lines +22 to +26
const encoder = new TextEncoder();
if (
!provided ||
!(await timingSafeEqual(encoder.encode(expected), encoder.encode(provided)))
) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

high

Passing a Uint8Array directly to Hono's timingSafeEqual will cause a TypeScript compilation error because timingSafeEqual expects string | ArrayBuffer | DataView. Accessing the underlying .buffer property of the Uint8Array resolves this type mismatch cleanly and safely.

Suggested change
const encoder = new TextEncoder();
if (
!provided ||
!(await timingSafeEqual(encoder.encode(expected), encoder.encode(provided)))
) {
const encoder = new TextEncoder();
if (
!provided ||
!(await timingSafeEqual(encoder.encode(expected).buffer, encoder.encode(provided).buffer))
) {

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.jules/sentinel.md:
- Around line 9-12: Update the sentinel entry so the date and guidance are
consistent: change the future date on the current entry to the PR date
(2026-05-29) and modify the earlier entry (the one that currently implies raw
strings are acceptable) to require converting strings to Uint8Array before using
timingSafeEqual; explicitly reference the constant-time function timingSafeEqual
and the recommended conversion via new TextEncoder().encode() (or equivalent) so
both entries uniformly instruct encoding strings to bytes prior to comparison.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2cd79bf5-c6fe-44cd-8f13-56940b671118

📥 Commits

Reviewing files that changed from the base of the PR and between bab8f67 and c9f7d90.

📒 Files selected for processing (3)
  • .jules/sentinel.md
  • apps/quill/src/lib/polar.ts
  • apps/quill/src/routes/admin.ts

Comment thread .jules/sentinel.md
Comment on lines +9 to +12
## 2026-12-29 - [Fix String Timing Attack Risk]
**Vulnerability:** Constant-time comparison function `timingSafeEqual` from `hono/utils/buffer` was used with raw strings instead of Uint8Array buffers. This undermines the timing attack protection because string representations in JavaScript engines can cause varying execution times, negating the constant-time guarantee.
**Learning:** Custom constant-time string comparisons in JavaScript/TypeScript are unreliable against timing attacks due to JIT optimizations and engine string storage mechanisms.
**Prevention:** Always convert strings to `Uint8Array` buffers using `new TextEncoder().encode()` before comparing them with `timingSafeEqual` to prevent timing leaks.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Align this sentinel entry with the earlier one and fix the date.

Line 9 is future-dated (2026-12-29) even though this PR was opened on May 29, 2026, and this new guidance now conflicts with Lines 3-4, which still imply raw strings are fine. Please update the earlier entry to require byte encoding too, otherwise the security note stays internally inconsistent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.jules/sentinel.md around lines 9 - 12, Update the sentinel entry so the
date and guidance are consistent: change the future date on the current entry to
the PR date (2026-05-29) and modify the earlier entry (the one that currently
implies raw strings are acceptable) to require converting strings to Uint8Array
before using timingSafeEqual; explicitly reference the constant-time function
timingSafeEqual and the recommended conversion via new TextEncoder().encode()
(or equivalent) so both entries uniformly instruct encoding strings to bytes
prior to comparison.

@aloewright aloewright enabled auto-merge (squash) June 4, 2026 05:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants