From c6ddbb28f3eb09e0feb08fba9f006d72a5c64159 Mon Sep 17 00:00:00 2001 From: Dennis Henry Date: Fri, 1 Aug 2025 08:01:37 -0400 Subject: [PATCH] fix: ensure newlines are supported in jwt input --- package-lock.json | 2 +- package.json | 2 +- src/features/common/services/utils.ts | 8 +++- tests/utils.test.ts | 53 +++++++++++++++++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 tests/utils.test.ts diff --git a/package-lock.json b/package-lock.json index 8a603afc..dfccb0fd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -59,7 +59,7 @@ "prettier": "^3.2.5", "typescript": "^5.4.5", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.4.0" + "vitest": "^1.6.1" } }, "node_modules/@aashutoshrathi/word-wrap": { diff --git a/package.json b/package.json index 33fb8a43..c00b4668 100644 --- a/package.json +++ b/package.json @@ -65,6 +65,6 @@ "prettier": "^3.2.5", "typescript": "^5.4.5", "vite-tsconfig-paths": "^4.3.2", - "vitest": "^1.4.0" + "vitest": "^1.6.1" } } diff --git a/src/features/common/services/utils.ts b/src/features/common/services/utils.ts index a6d69d8e..ac0fdf42 100644 --- a/src/features/common/services/utils.ts +++ b/src/features/common/services/utils.ts @@ -22,8 +22,14 @@ export const extractJwt = (value: string): string => { return ""; } - const jwt = value.split(/\s+/).filter((element) => element.startsWith("ey")); + // Check if it's a JWT string with newlines - compact it if so + if (value.trim().startsWith("ey") && (value.match(/\./g) || []).length >= 2) { + // It looks like a valid JWT, so remove all whitespace (including newlines) + return value.replace(/\s+/g, ""); + } + // Otherwise, use the extraction logic to find JWT in a larger text block + const jwt = value.split(/\s+/).filter((element) => element.startsWith("ey")); return jwt[0] || value; }; diff --git a/tests/utils.test.ts b/tests/utils.test.ts new file mode 100644 index 00000000..bab743b7 --- /dev/null +++ b/tests/utils.test.ts @@ -0,0 +1,53 @@ +import { describe, expect, test } from "vitest"; +import { extractJwt } from "@/features/common/services/utils"; + +describe("extractJwt", () => { + test("should return empty string for empty input", () => { + expect(extractJwt("")).toBe(""); + expect(extractJwt(null as unknown as string)).toBe(""); + expect(extractJwt(undefined as unknown as string)).toBe(""); + }); + + test("should extract JWT from normal input", () => { + const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + expect(extractJwt(jwt)).toBe(jwt); + }); + + test("should extract JWT with leading/trailing whitespace", () => { + const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + const jwtWithSpaces = ` ${jwt} `; + expect(extractJwt(jwtWithSpaces)).toBe(jwt); + }); + + test("should compact multiline JWTs by removing all whitespace", () => { + const multilineJWT = `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9. +eyJjbGllbnRfaWQiOiJZekV6TUdkb01ISm5PSEJpT0cxaWJEaHlOVEE9IiwicmVzcG9uc2Vf +dHlwZSI6ImNvZGUiLCJzY29wZSI6ImludHJvc2NwZWN0X3Rva2VucywgcmV2b2tlX3Rva2Vu +cyIsImlzcyI6ImJqaElSak0xY1hwYWEyMXpkV3RJU25wNmVqbE1iazQ0YlRsTlpqazNkWEU9 +Iiwic3ViIjoiWXpFek1HZG9NSEpuT0hCaU9HMWliRGh5TlRBPSIsImF1ZCI6Imh0dHBzOi8v +bG9jYWxob3N0Ojg0NDMve3RpZH0ve2FpZH0vb2F1dGgyL2F1dGhvcml6ZSIsImp0aSI6IjE1 +MTYyMzkwMjIiLCJleHAiOiIyMDIxLTA1LTE3VDA3OjA5OjQ4LjAwMCswNTQ1In0. +IxvaN4ER-PlPgLYzfRhk_JiY4VAow3GNjaK5rYCINFsEPa7VaYnRsaCmQVq8CTgddihEPPXe +t2laH8_c3WqxY4AeZO5eljwSCobCHzxYdOoFKbpNXIm7dqHg_5xpQz-YBJMiDM1ILOEsER8A +DyF4NC2sN0K_0t6xZLSAQIRrHvpGOrtYr5E-SllTWHWPmqCkX2BUZxoYNK2FWgQZpuUOD55H +fsvFXNVQa_5TFRDibi9LsT7Sd_az0iGB0TfAb0v3ZR0qnmgyp5pTeIeU5UqhtbgU9RnUCVmG +IK-SZYNvrlXgv9hiKAZGhLgeI8hO40utfT2YTYHgD2Aiufqo3RIbJA`; + + const compactedJWT = multilineJWT.replace(/\s+/g, ""); + expect(extractJwt(multilineJWT)).toBe(compactedJWT); + }); + + test("should extract JWT from text with other content", () => { + const jwt = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + const textWithJWT = `Here is my token: ${jwt} and some other text`; + expect(extractJwt(textWithJWT)).toBe(jwt); + }); + + test("should compact JWT with internal spaces", () => { + // This is a scenario where the JWT itself contains spaces (invalid, but should be handled) + const jwtWithSpaces = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0 NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"; + // The spaces should be removed by our new implementation + const compactedJWT = jwtWithSpaces.replace(/\s+/g, ""); + expect(extractJwt(jwtWithSpaces)).toBe(compactedJWT); + }); +}); \ No newline at end of file