From fd1155ae581b046fc0e300ef9b04cf7b6d43f426 Mon Sep 17 00:00:00 2001 From: Jason Ng Date: Tue, 18 Nov 2025 12:38:18 -0500 Subject: [PATCH] [PM-27103] Add URL Check to Send (#17056) * add dangerousPatters check to api service --- libs/common/src/platform/misc/utils.spec.ts | 26 +++++++++++ libs/common/src/platform/misc/utils.ts | 49 +++++++++++++++++++++ libs/common/src/services/api.service.ts | 10 ++++- 3 files changed, 84 insertions(+), 1 deletion(-) diff --git a/libs/common/src/platform/misc/utils.spec.ts b/libs/common/src/platform/misc/utils.spec.ts index 9f01db61fa6..664c6e22b3a 100644 --- a/libs/common/src/platform/misc/utils.spec.ts +++ b/libs/common/src/platform/misc/utils.spec.ts @@ -689,6 +689,32 @@ describe("Utils Service", () => { }); }); + describe("invalidUrlPatterns", () => { + it("should return false if no invalid patterns are found", () => { + const urlString = "https://www.example.com/api/my/account/status"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(false); + }); + + it("should return true if an invalid pattern is found", () => { + const urlString = "https://www.example.com/api/%2e%2e/secret"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(true); + }); + + it("should return true if an invalid pattern is found in a param", () => { + const urlString = "https://www.example.com/api/history?someToken=../secret"; + + const actual = Utils.invalidUrlPatterns(urlString); + + expect(actual).toBe(true); + }); + }); + describe("getUrl", () => { it("assumes a http protocol if no protocol is specified", () => { const urlString = "www.exampleapp.com.au:4000"; diff --git a/libs/common/src/platform/misc/utils.ts b/libs/common/src/platform/misc/utils.ts index 5f977da3979..136b0ac394f 100644 --- a/libs/common/src/platform/misc/utils.ts +++ b/libs/common/src/platform/misc/utils.ts @@ -612,6 +612,55 @@ export class Utils { return path.normalize(decodeURIComponent(denormalizedPath)).replace(/^(\.\.(\/|\\|$))+/, ""); } + /** + * Validates an url checking against invalid patterns + * @param url + * @returns true if invalid patterns found, false if safe + */ + static invalidUrlPatterns(url: string): boolean { + const invalidUrlPatterns = ["..", "%2e", "\\", "%5c"]; + + const decodedUrl = decodeURIComponent(url.toLocaleLowerCase()); + + // Check URL for invalidUrl patterns across entire URL + if (invalidUrlPatterns.some((p) => decodedUrl.includes(p))) { + return true; + } + + // Check for additional invalid patterns inside URL params + if (decodedUrl.includes("?")) { + const hasInvalidParams = this.validateQueryParameters(decodedUrl); + if (hasInvalidParams) { + return true; + } + } + + return false; + } + + /** + * Validates query parameters for additional invalid patterns + * @param url - The URL containing query parameters + * @returns true if invalid patterns found, false if safe + */ + private static validateQueryParameters(url: string): boolean { + try { + let queryString: string; + + if (url.includes("?")) { + queryString = url.split("?")[1]; + } else { + return false; + } + + const paramInvalidPatterns = ["/", "%2f", "#", "%23"]; + + return paramInvalidPatterns.some((p) => queryString.includes(p)); + } catch (error) { + throw new Error(`Error validating query parameters: ${error}`); + } + } + private static isMobile(win: Window) { let mobile = false; ((a) => { diff --git a/libs/common/src/services/api.service.ts b/libs/common/src/services/api.service.ts index 8314e44e75f..633c7eedcc4 100644 --- a/libs/common/src/services/api.service.ts +++ b/libs/common/src/services/api.service.ts @@ -1588,8 +1588,16 @@ export class ApiService implements ApiServiceAbstraction { ); apiUrl = Utils.isNullOrWhitespace(apiUrl) ? env.getApiUrl() : apiUrl; - // Prevent directory traversal from malicious paths const pathParts = path.split("?"); + // Check for path traversal patterns from any URL. + const fullUrlPath = apiUrl + pathParts[0] + (pathParts.length > 1 ? `?${pathParts[1]}` : ""); + + const isInvalidUrl = Utils.invalidUrlPatterns(fullUrlPath); + if (isInvalidUrl) { + throw new Error("The request URL contains dangerous patterns."); + } + + // Prevent directory traversal from malicious paths const requestUrl = apiUrl + Utils.normalizePath(pathParts[0]) + (pathParts.length > 1 ? `?${pathParts[1]}` : "");