AntFleet

Anatomy · 528e46ce-0

Cron Authorization compare on truncated/unicode headers can throw on non-Latin Buffer length mismatch

lowsecurityclosed in a58382a
repo e24ef98c·PR #10·reviewed 1 week ago·closed 1 week ago

The vulnerable code

apps/web/app/api/cron/sweep/route.ts:29-37

29 const expected = `Bearer ${secret}`;
30 const provided = authHeader ?? "";
31 const a = Buffer.from(provided);
32 const b = Buffer.from(expected);
33 if (a.length !== b.length || !timingSafeEqual(a, b)) {
34 logWarn("cron.unauthorized", { hasAuth: authHeader !== null });
35 return new NextResponse("unauthorized", { status: 401 });
36 }
37

The reasoning

Opus

Cron Authorization compare on truncated/unicode headers can throw on non-Latin Buffer length mismatch

lowsecurityhigh
  • apps/web/app/api/cron/sweep/route.ts:29-37
Safe — length is checked before timingSafeEqual, which is required (timingSafeEqual throws on length mismatch). Confirmed correct. Withdraw.

Recommendation

No-op.

GPT-5

Authorization compare leaks token length despite ‘constant-time’ comment

lowsecuritymedium
  • apps/web/app/api/cron/sweep/route.ts:22-34
Node’s timingSafeEqual throws on unequal buffer lengths, so the pre-check is necessary but it re-introduces a length oracle via different code paths. The comment suggests denying a length/prefix oracle; currently the implementation still exposes a length side-channel.

Recommendation

Compare fixed-length values to avoid length-based timing differences, e.g. HMAC both values with a server secret and compare the digests with timingSafeEqual; or pad/truncate the provided buffer to the expected length before comparison so timingSafeEqual is always invoked on equal-length inputs. Update the comment accordingly.

The agreement

Both frontier models flagged this within the same line range. AntFleet's unanimous gate fired — the finding posted on the PR. Closed in a58382a.

The fix

29 const expected = `Bearer ${secret}`;
30 const provided = authHeader ?? "";
31 const a = Buffer.from(provided);
32 const b = Buffer.from(expected);
33 if (a.length !== b.length || !timingSafeEqual(a, b)) {
34 logWarn("cron.unauthorized", { hasAuth: authHeader !== null });
35 return new NextResponse("unauthorized", { status: 401 });
36 }
37

Closure

Closed 1 week ago

SHA: a58382a1c8934544d327ad62fd4c9c54b187d8ef

View closure receipt on GitHub →

Tweet thread template

tweet 1 of 8186 / 280

Two frontier models reviewed PR #10 on e24ef98c. Both found this bug: low security: Cron Authorization compare on truncated/unicode headers can throw on non-Latin Buffer length mismatch

tweet 2 of 8128 / 280

The vulnerable code (apps/web/app/api/cron/sweep/route.ts:29-37): (full snippet at https://www.antfleet.dev/anatomy/528e46ce-0)

tweet 3 of 8158 / 280

What Opus saw: "Safe — length is checked before timingSafeEqual, which is required (timingSafeEqual throws on length mismatch). Confirmed correct. Withdraw."

tweet 4 of 8280 / 280

What GPT-5 saw: "Node’s timingSafeEqual throws on unequal buffer lengths, so the pre-check is necessary but it re-introduces a length oracle via different code paths. The comment suggests denying a length/prefix oracle; currently the implementation still exposes a length side-c…

tweet 5 of 897 / 280

Both flagged the same line range. AntFleet's unanimous gate fired — the finding posted on the PR.

tweet 6 of 893 / 280

The fix landed in commit a58382a: (view diff at https://www.antfleet.dev/anatomy/528e46ce-0)

tweet 7 of 881 / 280

AntFleet reviews every PR with two frontier models. Only unanimous findings post.

tweet 8 of 877 / 280

Full anatomy + reasoning + diffs: https://www.antfleet.dev/anatomy/528e46ce-0

Paste into X composer one tweet at a time. X has no multi-tweet intent API.