Summary
JWT Verifier assumes the decoded JWT header alg value is a string. A malformed token with a non-string alg value can cause runtime errors because classifyJwtVerificationAlgorithm() calls .toLowerCase() on the supplied value.
This is a robustness issue for a tool that processes untrusted pasted tokens.
Why this matters
Users paste arbitrary JWT-like strings, including malformed tokens from logs, bug reports, test fixtures, or attacks. A verifier should classify malformed/unsupported algorithms as actionable output, not throw a runtime error or bypass shared action feedback.
This also compounds the fire-and-forget action issue: the page currently starts verify() with void verify(), so an async runtime error can avoid the shared ToolActionBar failure path.
Current behavior
Relevant code:
src/features/tools/jwt-verifier/page.tsxconst alg = h?.alg as string || "unknown"setVerifyResult(await verifyJwtSignature(token, secret, alg))
src/features/tools/jwt-verifier/logic.tsclassifyJwtVerificationAlgorithm(algorithm: string) calls algorithm.toLowerCase().
If the decoded header is:
or:
{ "alg": { "name": "HS256" } }then the runtime value passed as algorithm is not a string.
Expected behavior
Malformed/non-string alg should produce a stable unsupported/malformed status, for example:
{status: "unsupported",algorithm: "non-string alg"}or a distinct status:
{status: "malformed",message: "JWT header alg must be a string."}The UI should display an actionable warning rather than throwing.
Suggested implementation plan
- Normalize the decoded
alg value before passing it to verification:
constrawAlg=h?.algconstalg=typeofrawAlg==="string" ? rawAlg : "unknown"
- Make
classifyJwtVerificationAlgorithm() defensive against unknown input, or keep its type narrow and enforce normalization at the callsite. - Consider adding a
malformed result status for non-string alg. - Return the async verify Promise through
ToolActionBar as part of the related action-state issue. - Add tests:
classifyJwtVerificationAlgorithm or verifyJwtSignature does not throw for non-string runtime values if exposed defensively;- page-level verification of a token with
{ "alg": 123 } shows unsupported/malformed guidance; - unsupported asymmetric algorithms still show unsupported guidance;
alg: none still shows unsigned warning.
Acceptance criteria
Related code pointers
src/features/tools/jwt-verifier/page.tsxsrc/features/tools/jwt-verifier/logic.tstests/unit/jwt-verifier-claims.test.tstests/component/jwt-verifier-page.test.tsx- related action-state issue for fire-and-forget verify handlers
Summary
JWT Verifierassumes the decoded JWT headeralgvalue is a string. A malformed token with a non-stringalgvalue can cause runtime errors becauseclassifyJwtVerificationAlgorithm()calls.toLowerCase()on the supplied value.This is a robustness issue for a tool that processes untrusted pasted tokens.
Why this matters
Users paste arbitrary JWT-like strings, including malformed tokens from logs, bug reports, test fixtures, or attacks. A verifier should classify malformed/unsupported algorithms as actionable output, not throw a runtime error or bypass shared action feedback.
This also compounds the fire-and-forget action issue: the page currently starts
verify()withvoid verify(), so an async runtime error can avoid the sharedToolActionBarfailure path.Current behavior
Relevant code:
src/features/tools/jwt-verifier/page.tsxconst alg = h?.alg as string || "unknown"setVerifyResult(await verifyJwtSignature(token, secret, alg))src/features/tools/jwt-verifier/logic.tsclassifyJwtVerificationAlgorithm(algorithm: string)callsalgorithm.toLowerCase().If the decoded header is:
{ "alg": 123 }or:
{ "alg": { "name": "HS256" } }then the runtime value passed as
algorithmis not a string.Expected behavior
Malformed/non-string
algshould produce a stable unsupported/malformed status, for example:or a distinct status:
The UI should display an actionable warning rather than throwing.
Suggested implementation plan
algvalue before passing it to verification:classifyJwtVerificationAlgorithm()defensive against unknown input, or keep its type narrow and enforce normalization at the callsite.malformedresult status for non-stringalg.ToolActionBaras part of the related action-state issue.classifyJwtVerificationAlgorithmorverifyJwtSignaturedoes not throw for non-string runtime values if exposed defensively;{ "alg": 123 }shows unsupported/malformed guidance;alg: nonestill shows unsigned warning.Acceptance criteria
algis missing, non-string, or malformed.algproduces actionable unsupported/malformed UI guidance.alg: nonebehavior still shows the unsigned warning.npm test -- --run tests/unit/jwt-verifier-claims.test.ts tests/component/jwt-verifier-page.test.tsxpasses.npm run check:typesandnpm run lintpass.Related code pointers
src/features/tools/jwt-verifier/page.tsxsrc/features/tools/jwt-verifier/logic.tstests/unit/jwt-verifier-claims.test.tstests/component/jwt-verifier-page.test.tsx