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 sub-skill, which you can run yourself in scan or check mode.

Run it

node ./.lagune/hooks/jwt.mjs # scans the whole project
node ./.lagune/hooks/jwt.mjs -d src # scans a directory
node ./.lagune/hooks/jwt.mjs -f 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

OptionAliasValueDescription
--pattern-pa code snippetCheck one call. Repeat to check several, one verdict per line.
--lang-la languageRequired with -p: one of javascript, python, go, java, kotlin, php, ruby, rust, csharp.
--dir-da directoryScope a scan to a directory. Repeats and combines with -f.
--file-fa fileScope a scan to a single file. Repeats and combines with -d.

With no option it scans the whole project. -p needs -l and cannot be combined with -d or -f.

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 sub-skill 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.

Last updated on July 24, 2026.

Copyright © 2026-present Weslley Araújo and contributors. Lagune is under the MIT License. Please check the Security Policy.

All product names, trademarks, and registered trademarks mentioned are the property of their respective owners and are used for identification purposes only.