What is wrong
Edit tokens are hashed with an inline createHash("sha256").update(token).digest("hex") in six separate files. There is no shared helper, so the hashing scheme for a security-relevant value is copy-pasted:
app/api/posts/route.ts line ~98, inline in the submit handler
app/api/status/[token]/route.ts lines 5 to 7, local hashToken()
app/api/edit/[token]/route.ts lines 7 to 9, local hashToken(), called twice (lines ~19 and ~61)
app/api/posts/edit/[token]/route.ts line ~29, another local helper
app/api/admin/posts/[id]/resend-token/route.ts line ~27, inline
app/edit/[token]/page.tsx line ~34, another local helper
lib/utils/hash.ts is already the home for hashing helpers. It exports hashIp() and getClientIp() but nothing for tokens.
Why it matters
Every one of these must produce byte-identical output or an edit link silently stops resolving. Six copies means a future change (adding a pepper, switching algorithm, trimming input) has to be made in six places, and missing one breaks edit access for real submitters with no error message.
What to change
- In
lib/utils/hash.ts, add and document an exported hashEditToken(token: string): string that does exactly what the existing copies do today. Do not change the algorithm in this PR: keeping the output identical is the whole point, since existing rows in posts.edit_token_hash were written with it.
- Replace all six inline implementations with imports of the new helper, deleting the now-unused local
hashToken functions and the now-unused createHash imports.
- Add a small test in
lib/utils/hash.test.ts asserting hashEditToken is deterministic and that it matches a known hex digest for a fixed input, so a future refactor cannot silently change the scheme. Copy the shape from lib/utils/urls.test.ts.
Note that app/api/posts/route.ts and app/api/admin/posts/[id]/resend-token/route.ts also import randomBytes for token generation. Leave that alone, this issue is only about hashing.
Verify
npx tsc --noEmit
npm run lint
npm test
npm run format
Mechanical and self-contained, a good first change in this codebase.
Comment here to claim it and ask anything you are unsure about. You will usually get a reply within a day.
What is wrong
Edit tokens are hashed with an inline
createHash("sha256").update(token).digest("hex")in six separate files. There is no shared helper, so the hashing scheme for a security-relevant value is copy-pasted:app/api/posts/route.tsline ~98, inline in the submit handlerapp/api/status/[token]/route.tslines 5 to 7, localhashToken()app/api/edit/[token]/route.tslines 7 to 9, localhashToken(), called twice (lines ~19 and ~61)app/api/posts/edit/[token]/route.tsline ~29, another local helperapp/api/admin/posts/[id]/resend-token/route.tsline ~27, inlineapp/edit/[token]/page.tsxline ~34, another local helperlib/utils/hash.tsis already the home for hashing helpers. It exportshashIp()andgetClientIp()but nothing for tokens.Why it matters
Every one of these must produce byte-identical output or an edit link silently stops resolving. Six copies means a future change (adding a pepper, switching algorithm, trimming input) has to be made in six places, and missing one breaks edit access for real submitters with no error message.
What to change
lib/utils/hash.ts, add and document an exportedhashEditToken(token: string): stringthat does exactly what the existing copies do today. Do not change the algorithm in this PR: keeping the output identical is the whole point, since existing rows inposts.edit_token_hashwere written with it.hashTokenfunctions and the now-unusedcreateHashimports.lib/utils/hash.test.tsassertinghashEditTokenis deterministic and that it matches a known hex digest for a fixed input, so a future refactor cannot silently change the scheme. Copy the shape fromlib/utils/urls.test.ts.Note that
app/api/posts/route.tsandapp/api/admin/posts/[id]/resend-token/route.tsalso importrandomBytesfor token generation. Leave that alone, this issue is only about hashing.Verify
npx tsc --noEmit npm run lint npm test npm run formatMechanical and self-contained, a good first change in this codebase.
Comment here to claim it and ask anything you are unsure about. You will usually get a reply within a day.