1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-06 00:13:28 +00:00

[PM-27103] Add URL Check to Send (#17056)

* add dangerousPatters check to api service
This commit is contained in:
Jason Ng
2025-11-18 12:38:18 -05:00
committed by GitHub
parent bbb42d9b17
commit fd1155ae58
3 changed files with 84 additions and 1 deletions

View File

@@ -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";

View File

@@ -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) => {

View File

@@ -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]}` : "");