Skip to content

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

10 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

jotwatch

Catch JWT misconfigurations statically — before an attacker forges a token.

A single wrong flag turns JSON Web Token auth into an open door: accept the "none" algorithm and anyone can send an unsigned token with "role":"admin"; call jwt.decode() instead of jwt.verify() and the claims your app trusts are attacker-controlled. These mistakes ship constantly, they never fail a happy-path test, and they are exactly what RFC 8725 (JWT Best Current Practices) and the OWASP JWT Cheat Sheet warn about.

jotwatch is a zero-config, language-agnostic gate that scans your source and flags these patterns with a clean CI exit code.

$ jotwatch .

● 2 JWT blocker(s):
  src/auth.js:15  JWT verification accepts the "none" algorithm.
      ↳ The "none" alg means an unsigned token. An attacker can drop the signature
        and forge any claim (RFC 8725 §3.1). Pin an explicit allow-list of signing
        algorithms and never include "none".
      [JW001]
  src/auth.js:21  jwt.decode() reads claims without verifying the signature.
      ↳ In jsonwebtoken, decode() does NOT check the signature — its claims are
        attacker-controlled. Use jwt.verify(token, key, {algorithms:[...]}) ...
      [JW002]

2 blockers · 1 warning

Exit code 1 on a blocker, so it drops straight into pre-commit or CI.

Why it's different

Most JWT tools are runtime attack kits (jwt_tool, jwt-scanner) that hammer a live endpoint, or JWT rules buried inside a big SAST platform. jotwatch is the opposite: a standalone, dependency-free static gate focused on one thing — JWT safety — that runs on your source in milliseconds with no server, no config, and no language plugins.

How it works

jotwatch reads each source file line by line and matches a small set of case-insensitive patterns grounded in RFC 8725 / OWASP. It's a heuristic scanner, not a type checker: no code is executed, no network calls are made, nothing to configure. It's a single static Go binary.

The one language-aware detail: a bare jwt.decode() is flagged only in JavaScript/TypeScript, because in Node's jsonwebtoken decode() skips verification — while in Python's PyJWT jwt.decode() is the verifying call.

Rules

Rule Severity What it flags Basis
JW001 blocker The "none" algorithm is accepted ("alg":"none", algorithms:["none"], …) RFC 8725 §3.1
JW002 blocker Signature verification disabled (verify=false, verify_signature:false, JS jwt.decode()) RFC 8725 §2.2
JW003 warning Expiry not enforced (verify_exp:false, ignoreExpiration:true) RFC 8725 §3.9
JW004 warning A signing secret is a hardcoded string literal passed to a JWT call OWASP JWT Cheat Sheet

Rules favor low false positives: JW004 fires only when a string literal is passed directly as the key argument to a jwt.sign/encode/verify call, so a key loaded from the environment is never flagged.

Install

go install github.com/jay-tank/jotwatch@latest

Or build from source: go build -o jotwatch .

Usage

jotwatch                 # scan the current directory
jotwatch ./src           # scan a path
jotwatch --strict        # treat warnings (JW003/JW004) as failures too
jotwatch --json          # machine-readable output

In CI

- run: go run github.com/jay-tank/jotwatch@latest ./ --strict

Exit codes: 0 clean · 1 a blocker (or any finding under --strict) · 2 usage error.

Suppressing false positives

jotwatch is a heuristic gate. To silence a line, add a jotwatch:ignore comment on it (// jotwatch:ignore or # jotwatch:ignore). To skip whole paths, list path substrings in a .jotwatchignore file (one per line, # for comments).

Scope

jotwatch answers a focused question well — "does this code accept unsafe JWTs?" — across languages, as a fast static gate. It does not prove your verification is otherwise perfect (right audience, right issuer, key rotation); it catches the handful of mistakes that most directly let an attacker forge a token.

License

MIT © Jay Tank

About

A zero-config, language-agnostic gate that statically flags JWT misconfigurations (none alg, disabled verification, unenforced expiry, hardcoded secrets) with a clean CI exit code. Single Go binary. Grounded in RFC 8725 + OWASP.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages