Get this on every PR automatically. Webhook fires, AI reviews, you approve.
Install on vercel/eve →
// reviewed by pulllight
PullLight found 2 issues
1 high
1 medium
// convert to your repo
PullLight caught 2 1 high, 1 medium on this PR.
Install on vercel/eve to get this on every PR automatically — 60 seconds.
Findings
2 issues
high
auth
`timingSafeEqualStrings` short-circuits with `false` when the lengths differ, but this early-exit leaks the length of the expected secret via a timing side-channel. An attacker can binary-search the secret length by observing whether the function returns immediately (lengths differ) or takes the full `timingSafeEqual` time. Fix by always running `timingSafeEqual` on equal-length buffers — pad or hash both sides to a fixed length before comparing.
// packages/eve/src/internal/nitro/routes/external-cron.ts:66
⚡ Suggested fix
function timingSafeEqualStrings(left: string, right: string): boolean {
const leftBuffer = Buffer.from(left);
const rightBuffer = Buffer.from(right);
// Pad to the same length so timingSafeEqual always runs, preventing
// length-based timing leaks. We still return false when lengths differ.
const maxLen = Math.max(leftBuffer.length, rightBuffer.length);
const paddedLeft = Buffer.concat([leftBuffer, Buffer.alloc(maxLen - leftBuffer.length)]);
const paddedRight = Buffer.concat([rightBuffer, Buffer.alloc(maxLen - rightBuffer.length)]);
return leftBuffer.length === rightBuffer.length && timingSafeEqual(paddedLeft, paddedRight);
}
medium
error-handling
`Promise.all` over `dispatchScheduleTask` calls means a single failing dispatch rejects the entire promise, causing the handler to throw an unhandled error (no try/catch) and return a 500 with no body. A partial failure (one schedule errors, others succeed) silently drops all results. Consider `Promise.allSettled` and returning per-schedule success/error status.
// packages/eve/src/internal/nitro/routes/external-cron.ts:55