fix: rate limit counted a full day, and Access errors were unreadable - #59
Open
ctala wants to merge 1 commit into
Open
fix: rate limit counted a full day, and Access errors were unreadable#59ctala wants to merge 1 commit into
ctala wants to merge 1 commit into
Conversation
**Rate limit was daily, not hourly.** Emails are stored with
`toISOString()` ("2026-08-08T20:41:19.952Z") but the cap compared them
against `datetime('now', '-1 hour')`, which returns "2026-08-08
21:14:29" — a space instead of "T". SQLite compares these as TEXT and
'T'(84) > ' '(32), so every row from the current day always satisfied
the condition. The documented "20 per hour" behaved as "20 per day",
and only released when the UTC date rolled over. Same defect in the
daily check, which spanned roughly two calendar days.
Fixed by computing the cutoff in JS and binding it as a parameter, so
both sides of the comparison are ISO 8601.
**Access JWT failures were reported as expired tokens.** The catch
block swallowed the error, so any failure surfaced as "Invalid or
expired Access token". A TEAM_DOMAIN without a scheme (e.g.
"team.cloudflareaccess.com" instead of "https://team.cloudflareaccess.com")
makes `new URL()` throw, and the message points at the token, which is
perfectly valid. Now the error says what actually failed and what it
was validated against. The token is never logged.
Both were found while running a second instance in production.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Two bugs found while running a second instance in production.
Rate limit was daily, not hourly
Emails are stored with
toISOString()(2026-08-08T20:41:19.952Z), butcheckSendRateLimitcompared them againstdatetime("now", "-1 hour"), which returns2026-08-08 21:14:29— a space instead ofT.SQLite compares these as TEXT, and
T(84) >(32), so every row from the current day always satisfied the condition. The documented "20 per hour" behaved as "20 per day" and only released when the UTC date rolled over. The daily check has the same defect and effectively spans two calendar days.Symptom: a mailbox returns
429long after the hour has passed, with no way to tell when it will recover.Fixed by computing the cutoff in JS and binding it as a parameter, so both sides of the comparison are ISO 8601.
Access JWT failures were reported as expired tokens
The
catchblock discarded the error, so any failure surfaced asInvalid or expired Access token.A
TEAM_DOMAINwithout a scheme —team.cloudflareaccess.cominstead ofhttps://team.cloudflareaccess.com— makesnew URL()throw insidegetAccessUrls. The message then points at the token, which is perfectly valid, and the actual cause is a config typo that is invisible from the outside.Now the log states what failed and what it was validated against. The token is never logged.
Notes
429accepted sends again after the fix.