CVE-2026-35040
Description
fast-jwt provides fast JSON Web Token (JWT) implementation. Prior to 6.2.1, 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) and /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. This vulnerability is fixed in 6.2.1.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
fast-jwtnpm | < 6.2.1 | 6.2.1 |
Affected products
1Patches
118d25904e461Fix/regex non deterministic validation (#593)
2 files changed · +87 −1
src/verifier.js+13 −1 modified@@ -44,6 +44,15 @@ function ensureStringClaimMatcher(raw) { return raw .filter(r => r) .map(r => { + if (r instanceof RegExp) { + return { + test: v => { + r.lastIndex = 0 + return r.test(v) + } + } + } + if (r && typeof r.test === 'function') { return r } @@ -510,7 +519,10 @@ module.exports = function createVerifier(options) { throw new TokenError(TokenError.codes.invalidOption, 'The requiredClaims option must be an array.') } - if (allowedCritHeaders !== undefined && !Array.isArray(allowedCritHeaders)) { + if ( + allowedCritHeaders !== undefined && + (!Array.isArray(allowedCritHeaders) || allowedCritHeaders.some(h => typeof h !== 'string' || h.length === 0)) + ) { throw new TokenError(TokenError.codes.invalidOption, 'The allowedCritHeaders option must be an array of strings.') }
test/verifier.spec.js+74 −0 modified@@ -1778,6 +1778,73 @@ test('default errorCacheTTL should not cache errors when sub millisecond executi t.mock.timers.reset() }) +test('stateful RegExp /g flag must not cause non-deterministic claim validation - allowedAud', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedAud: /^admin$/g }) + + // All 8 successive calls with the same valid token must succeed + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /g flag on allowedAud`) + } +}) + +test('stateful RegExp /y flag must not cause non-deterministic claim validation - allowedAud', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedAud: /^admin$/y }) + + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /y flag on allowedAud`) + } +}) + +test('stateful RegExp /g flag must not cause non-deterministic claim validation - allowedIss', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedIss: /^issuer$/g }) + + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /g flag on allowedIss`) + } +}) + +test('stateful RegExp /g flag must not cause non-deterministic claim validation - allowedSub', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedSub: /^subject$/g }) + + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /g flag on allowedSub`) + } +}) + +test('stateful RegExp /g flag must not cause non-deterministic claim validation - allowedJti', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedJti: /^id-123$/g }) + + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /g flag on allowedJti`) + } +}) + +test('stateful RegExp /g flag must not cause non-deterministic claim validation - allowedNonce', t => { + t.mock.timers.enable({ now: 100000 }) + const sign = createSigner({ key: 'secret' }) + const token = sign({ aud: 'admin', iss: 'issuer', sub: 'subject', jti: 'id-123', nonce: 'nonce-xyz' }) + const verifier = createVerifier({ key: 'secret', allowedNonce: /^nonce-xyz$/g }) + + for (let i = 0; i < 8; i++) { + t.assert.doesNotThrow(() => verifier(token), `call ${i} should pass with /g flag on allowedNonce`) + } +}) + // --- crit header validation (RFC 7515 §4.1.11) --- test('crit: rejects token with unknown critical extension (secure-by-default, no allowedCritHeaders)', t => { @@ -1874,3 +1941,10 @@ test('crit: throws on invalid allowedCritHeaders option (not an array)', t => { message: 'The allowedCritHeaders option must be an array of strings.' }) }) + +test('crit: throws on invalid allowedCritHeaders option (empty string)', t => { + t.assert.throws(() => createVerifier({ key: 'secret', allowedCritHeaders: [''] }), { + code: 'FAST_JWT_INVALID_OPTION', + message: 'The allowedCritHeaders option must be an array of strings.' + }) +})
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
6- github.com/nearform/fast-jwt/commit/18d25904e4617e8753526d1b3ab5a2cccdea726anvdPatchWEB
- github.com/nearform/fast-jwt/pull/593nvdIssue TrackingPatchWEB
- github.com/nearform/fast-jwt/security/advisories/GHSA-3j8v-cgw4-2g6qnvdExploitMitigationVendor AdvisoryWEB
- github.com/advisories/GHSA-3j8v-cgw4-2g6qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2026-35040ghsaADVISORY
- github.com/nearform/fast-jwt/releases/tag/v6.2.1nvdProductRelease NotesWEB
News mentions
0No linked articles in our index yet.