Skip to content

fix(jwt): reject tokens whose header or payload is not a JSON object - #108

Open
tnalbant wants to merge 1 commit into
codercops:developfrom
tnalbant:fix/reject-non-object-jwt-parts
Open

tnalbant wants to merge 1 commit into
codercops:developfrom
tnalbant:fix/reject-non-object-jwt-parts

Conversation

@tnalbant

@tnalbant tnalbant commented Oct 1, 2026

Copy link
Copy Markdown

What and why

Closes #87

decodeJwt only checked whether JSON.parse threw, not what it returned. JSON.parse("null"), JSON.parse("[]") and JSON.parse("123") all succeed, so a token with a null, array or primitive header or payload came back as ok: true carrying a value that was not actually a Record<string, unknown>. Every reader downstream then threw on it — securityAudit, computeHealth, verifyJwt, verifyWithJwks and JwtDecoderClient all read .alg or .exp off that value.

The guard is now applied to both parsed halves. RFC 7519 defines the JOSE header and the claims set as JSON objects, so a non-object result is rejected with a message that names which half was wrong, in the same voice as the existing decode errors.

Type of change

  • Bug fix
  • Tests

Checklist

  • npm run lint && npm run test && npm run build passes locally
  • Logic changes live in lib/ and have a test in lib/__tests__/
  • No analytics, trackers, or calls that send user data off-device
  • Works in both light and dark themes (if UI changed) — no UI change
  • Inputs have labels and it works with the keyboard alone (if UI changed) — no UI change
  • New tool: registry entry, page.tsx, client component, opengraph-image.tsx, and the bug report dropdown are all done — not a new tool

Tests

Added 7 cases to lib/__tests__/jwt-utils.test.ts covering a null header, an array header, a null payload, an array payload, a primitive payload, the issue's bnVsbA.e30.x token, and a guard that a well-formed token still decodes.

Run Result
npx vitest run lib/__tests__/jwt-utils.test.ts 13/13 passed
npm test (full suite) 92/92 passed, 7 files
npm run lint 0 errors (13 pre-existing warnings, all in lib/useLocalStorage.ts)
npm run build passed

The new cases fail on the base commit and pass with the fix: with lib/jwt-utils.ts reverted, 6 of them fail; restored, all 13 pass.

A direct probe of the base behaviour also showed verifyJwt("bnVsbA.e30.x", ...) rejecting with TypeError: Cannot read properties of null (reading 'alg') — including as an unhandled promise rejection. That path is now unreachable from the UI: decodeJwt returns ok: false, so JwtDecoderClient keeps decoded null and never renders the verify panel.

Notes for reviewers

  • I did not add a guard to verifyJwt / verifyWithJwks, which the issue marks as optional. decodeJwt now rejects the token first, VerifyPanel is only rendered when decoded is truthy, and runVerify is wrapped in a try/catch anyway. Adding it would change code the issue did not ask for. Happy to include it if you would rather have the defence in depth.
  • The UI banner (pasting bnVsbA.e30.x shows the normal "Decoding failed" banner) was verified by reading the code path, not by clicking through the browser.
  • I used a Record<string, unknown> cast only after the shape check, so the declared type is now actually true.

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
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.

Reject JWTs whose header or payload is not a JSON object

1 participant