diff --git a/src/lib/isJWT.js b/src/lib/isJWT.js index 1d0ade5ee..dd9eeeae1 100644 --- a/src/lib/isJWT.js +++ b/src/lib/isJWT.js @@ -1,6 +1,32 @@ import assertString from './util/assertString'; import isBase64 from './isBase64'; +function getGlobalScope() { + if (typeof global !== 'undefined') return global; + if (typeof self !== 'undefined') return self; + if (typeof window !== 'undefined') return window; + return {}; +} + +function isBase64EncodedJSON(base64Str) { + // Convert URL-safe base64 to standard base64 + const standardBase64 = base64Str.replace(/-/g, '+').replace(/_/g, '/'); + try { + const scope = getGlobalScope(); + const decoded = typeof scope.atob === 'function' + ? scope.atob(standardBase64) + : Buffer.from(standardBase64, 'base64').toString('binary'); + try { + JSON.parse(decoded); + return true; + } catch (e2) { + return false; + } + } catch (e) { + return false; + } +} + export default function isJWT(str) { assertString(str); @@ -11,5 +37,14 @@ export default function isJWT(str) { return false; } - return dotSplit.reduce((acc, currElem) => acc && isBase64(currElem, { urlSafe: true }), true); + const [header, payload, signature] = dotSplit; + + if (!isBase64(header, { urlSafe: true }) + || !isBase64(payload, { urlSafe: true }) + || !isBase64(signature, { urlSafe: true })) { + return false; + } + + // header and payload must be valid JSON when decoded + return isBase64EncodedJSON(header) && isBase64EncodedJSON(payload); } diff --git a/test/validators.test.js b/test/validators.test.js index a4c3d7193..afa5b20fd 100644 --- a/test/validators.test.js +++ b/test/validators.test.js @@ -5549,6 +5549,10 @@ describe('Validators', () => { 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NSIsIm5hbWUiOiJKb2huIERvZSIsImlhdCI6MTYxNjY1Mzg3Mn0.eyJpc3MiOiJodHRwczovL2V4YW1wbGUuY29tIiwiaWF0IjoxNjE2NjUzODcyLCJleHAiOjE2MTY2NTM4ODJ9.a1jLRQkO5TV5y5ERcaPAiM9Xm2gBdRjKrrCpHkGr_8M', '$Zs.ewu.su84', 'ks64$S/9.dy$§kz.3sd73b', + // non-JSON header (valid base64 URL-safe but not valid JSON when decoded) + 'ZmFrZSBoZWFkZXI.eyJzdWIiOiIxMjM0NTY3ODkwIn0.ZmFrZXNpZw', + // non-JSON payload (valid base64 URL-safe but not valid JSON) + 'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.bm9uLWpzb24tcGF5bG9hZA.sig', ], error: [ [],