Hooks

jwt hook: Detect unpinned JWT verification

Sweep a codebase for JWT verification without an algorithm pin, or score a single call, from the command line.

The jwt hook flags JWT verification without an algorithm pin, the algorithm-confusion hole where a verifier trusts the token's own alg header, and verification turned off entirely. It is language-aware, reading each file by its own JWT library, and it is the deterministic engine behind the federation specialization, which you can run yourself in scan or check mode.

Run it​

npx lagune read jwt # scans the whole project
npx lagune read jwt --dir src # scans a directory
npx lagune read jwt --file auth.ts # scans a single file

Every line under Unpinned JWT verification found is a verify call that sets no algorithms allowlist (where the library needs one), accepts an unsafe one (none, or an asymmetric algorithm mixed with HMAC), or disables signature checking outright, so it trusts the token's own alg. A confirmed exposure, so the hook exits non-zero. A clean run prints no unpinned JWT verification found.

How to read the verdict​

VerdictMeaning
unpinnedA verify call with no algorithm allowlist (where one is needed), an unsafe one, or with verification disabled.
safeA verify call that pins a sound algorithm allowlist, or whose library pins it implicitly.
invalidThe snippet contains no recognizable verify call to judge.

CLI options​

OptionValueDescription
--patterna code snippetCheck one call. Repeat to check several, one verdict per line.
--langa languageRequired with --pattern: one of javascript, python, go, java, kotlin, php, ruby, rust, csharp.
--dira directoryScope a scan to a directory. Repeats and combines with --file.
--filea fileScope a scan to a single file. Repeats and combines with --dir.

With no option it scans the whole project. --pattern needs --lang and cannot be combined with --dir or --file.

Choose what it scans​

It leaves out what Git ignores. To leave out more, or to scan what Git ignores, give jwt settings of its own:

npx lagune write add --ignore test/fixtures --checker jwt
{
"checkers": {
"jwt": { "gitignore": false, "ignore": ["test/fixtures"] }
}
}

Configure the hooks covers every setting.

Supported languages​

The scan reads these libraries, keyed by file extension. Because some pin the algorithm implicitly (a missing explicit list is sound), the "no allowlist ⇒ unpinned" rule applies only where the library expects an explicit one, so a correctly-configured call is never flagged.

LanguageLibraryFlags no-allowlist as unpinned?Always flags
JavaScript / TypeScriptjsonwebtoken, joseYesnone, asymmetric+HMAC mix
PythonPyJWT, python-joseYes (PyJWT requires it, python-jose defaults algorithms=None)none, verify_signature: False
Gogolang-jwtYes (no WithValidMethods)none, ParseUnverified
Rubyruby-jwtYesnone, verify = false
Java / Kotlinjjwt, nimbusNo (key-derived)none, an unsigned/unsecured parser
PHPfirebase/php-jwtNo (Key carries the alg)none
RustjsonwebtokenNo (Validation requires one)none, insecure_disable_signature_validation
C#Microsoft.IdentityModelNo (multi-line params)none, RequireSignedTokens = false
tip

The hook decides only the algorithm pin: the claim, key, and flow checks are judgment the federation specialization still covers.

Frequently Asked Questions

A verifier that reads the algorithm from the token header lets an attacker swap RS256 for HS256 and sign with the public key. Pinning a sound algorithm allowlist in code prevents it, and the jwt hook flags a verify call that sets none or an unsafe one.