GitHub Security Advisory: GHSA-3j8v-cgw4-2g6q
Package: fast-jwt (NPM)
Severity: MODERATE (CVSS 5.3)
CWE: CWE-440: Expected Behavior Violation, CWE-697: Incorrect Comparison
Vulnerable versions: < 6.2.1
Patched in: 6.2.1

Summary: fast-jwt: Stateful RegExp (/g or /y) causes non-deterministic allowed-claim validation (logical DoS)

Details:
## Impact

Using certain modifiers on RegExp objects in the allowedAud, allowedIss, allowedSub, allowedJti, or allowedNonce options in verify functions can cause certain unintended behaviours. This is because some modifiers are stateful and will cause failures in every second verification attempt regardless of the validity of the token provided.

Such modifiers are:
- /g : Global matching
- /y : Sticky matching

This does NOT allow invalid tokens to be accepted, only for valid tokens to be improperly rejected in some configurations. Instead it causes **50% of valid authentication requests to fail** in an alternating pattern, leading to:
  - Intermittent user authentication failures
  - Potential retry storms in applications
  - Operational monitoring alerts

## Affected Configurations

### This vulnerability ONLY affects applications that:

- Use RegExp objects (not strings) in the allowedAud, allowedIss, allowedSub, allowedJti, or allowedNonce options
- Use stateful RegExp modifiers such a /g or /y

Example: allowedAud: /abc/g ← IMPACTED
Example: allowedAud: "/abc/" ← SAFE

### Not Affected

- Applications using string patterns for audience validation (most common)
- Applications using RegExp patterns without stateful modifiers

### Assessment Guide
To determine if you're affected:

Check if  allowedAud, allowedIss, allowedSub, allowedJti, or allowedNonce options use RegExp objects (/pattern/ or new RegExp())
If yes, review the pattern for stateful modifiers like /g, /y
If no RegExp usage or no stateful modifiers, you are NOT affected

## Mitigation Options
While a fix will be coming in the next version of the package you can take steps to mitigate the issue immediately by removing any such modifiers (/g, /y) from the regex.

---

Summary

fast-jwt accepts RegExp for allowedAud, allowedIss, allowedSub, allowedJti, and allowedNonce.

If the provided regular expression uses the g (global) or y (sticky) flag, verification becomes non-deterministic: the same valid token alternates between acceptance and rejection across successive calls.

This occurs because RegExp.prototype.test() is stateful when g/y is set (it mutates lastIndex), and fast-jwt reuses the same RegExp object without resetting lastIndex.


Affected component

src/verifier.js

ensureStringClaimMatcher() returns the RegExp object directly.

validateClaimValues() performs repeated a.test(v) calls without resetting lastIndex.


Impact

Logical denial-of-service / authentication flapping.

A valid signed JWT can be intermittently rejected.

Causes unpredictable authentication outcomes across repeated verification calls.

Can trigger retry storms and cascading failures in API gateways and authentication middleware.

Affects any deployment that configures allowed* using RegExp and includes g or y flags.


Root cause

validateClaimValues() uses: allowed.some(a => a.test(v))


When `a` is a RegExp with g or y, `a.test()` mutates `a.lastIndex`.

Subsequent calls against the same input can retu