rules:
  - id: auth.py.jwt.alg-none
    languages:
      - python
    severity: ERROR
    message: |
      A JWT is decoded or signed with the `none` algorithm. The `none`
      algorithm means the token is NOT cryptographically signed, so any
      attacker can forge a token with arbitrary claims and have it accepted,
      a complete authentication bypass (CVE-class JWT alg=none vulnerability).

      Never allow `none`. Pin a strong signing algorithm explicitly:
      `jwt.decode(token, key, algorithms=["RS256"])` for verification, or
      `jwt.encode(claims, key, algorithm="RS256")` (also ES256 / HS256) when
      issuing tokens. Never include `"none"` in the `algorithms` allowlist.
    # Scoped to PyJWT's `jwt.decode(...)` / `jwt.encode(...)`. Uses a
    # case-insensitive metavariable-regex so "none", "None", and "NONE" all
    # match, while strong algs like RS256/ES256/HS256 are left untouched.
    pattern-either:
      - patterns:
          - pattern: jwt.decode(..., algorithms=$ALGS, ...)
          - metavariable-regex:
              metavariable: $ALGS
              regex: (?i).*['"]none['"].*
      - patterns:
          - pattern: jwt.encode(..., algorithm=$ALG, ...)
          - metavariable-regex:
              metavariable: $ALG
              regex: (?i)^['"]none['"]$
    metadata:
      oauthlint-rule-id: AUTH-PY-JWT-002
      oauthlint-doc-url: https://oauthlint.dev/rules/py-jwt-alg-none
      category: security
      cwe: CWE-347
      owasp: API2:2023
      llm-prevalence: HIGH
      technology:
        - pyjwt
      references:
        - https://pyjwt.readthedocs.io/en/stable/api.html#jwt.decode
        - https://cwe.mitre.org/data/definitions/347.html
