This commit is contained in:
nik
2025-10-03 22:27:28 +03:00
parent 829fad0e17
commit 871cf7e792
16520 changed files with 2967597 additions and 3 deletions

2
node_modules/@braintree/sanitize-url/.eslintignore generated vendored Normal file
View File

@@ -0,0 +1,2 @@
dist
coverage

6
node_modules/@braintree/sanitize-url/.eslintrc generated vendored Normal file
View File

@@ -0,0 +1,6 @@
{
"extends": "braintree/client",
"rules": {
"no-control-regex": 0
}
}

View File

@@ -0,0 +1 @@
* @braintree/team-sdk-js

View File

@@ -0,0 +1,17 @@
name: "Unit Tests"
on: [push]
jobs:
build:
name: "Unit Tests on Ubuntu"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Use Node.js
uses: actions/setup-node@v1
with:
node-version: "18.x"
- run: npm install
- run: npm test

1
node_modules/@braintree/sanitize-url/.nvmrc generated vendored Normal file
View File

@@ -0,0 +1 @@
v18

3
node_modules/@braintree/sanitize-url/.prettierignore generated vendored Normal file
View File

@@ -0,0 +1,3 @@
*-lock.json
dist
coverage

142
node_modules/@braintree/sanitize-url/CHANGELOG.md generated vendored Normal file
View File

@@ -0,0 +1,142 @@
# CHANGELOG
## 7.1.1
- DevDependency Changes
- happy-dom to 15.11.6
- Update (sub-)dependencies
- cross-spawn to 7.0.6
- micromatch to 4.0.8
- vite to 4.5.5
## 7.1.0
- Updated to handle back-slashes
## 7.0.4
- Updates get-func-name to 2.0.2
## 7.0.3
- Dependencies
- Update braces to 3.0.3
## 7.0.2
- Improve sanitization of whitespace escapes
## 7.0.1
- Improve sanitization of HTML entities
## 7.0.0
- Move constant declarations from index file to `constants.ts` file
- Update to node v18
- Dev Dependency Updates
- Update to TypeScript 5
- Other minor dependency updates
## 6.0.4
- Add additional null byte sanitization prior to html decoding (#48)
## 6.0.3
- Add null check to beginning of `sanitizeUrl` function ([#54](https://github.com/braintree/sanitize-url/issues/54))
## 6.0.2
- Fix issue where urls in the form `https://example.com

/something` were not properly sanitized
## 6.0.1
- Fix issue where urls in the form `javascript:alert('xss');` were not properly sanitized
- Fix issue where urls in the form `javasc	ript:alert('XSS');` were not properly sanitized
## 6.0.0
**Breaking Changes**
- Decode HTML characters automatically that would result in an XSS vulnerability when rendering links via a server rendered HTML file
```js
// decodes to javacript:alert('XSS')
const vulnerableUrl =
"&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041";
sanitizeUrl(vulnerableUrl); // 'about:blank'
const okUrl = "https://example.com/" + vulnerableUrl;
// since the javascript bit is in the path instead of the protocol
// this is successfully sanitized
sanitizeUrl(okUrl); // 'https://example.com/javascript:alert('XSS');
```
## 5.0.2
- Fix issue where certain invisible white space characters were not being sanitized (#35)
## 5.0.1
- Fix issue where certain safe characters were being filtered out (#31 thanks @akirchmyer)
## 5.0.0
_Breaking Changes_
- Sanitize vbscript urls (thanks @vicnicius)
## 4.1.1
- Fixup path to type declaration (closes #25)
## 4.1.0
- Add typescript types
## 4.0.1
- Fix issue where urls with accented characters were incorrectly sanitized
## 4.0.0
_Breaking Changes_
- Protocol-less urls (ie: www.example.com) will be sanitised and passed on instead of sending out `about:blank` (Thanks @chawes13 #18)
## 3.1.0
- Trim whitespace from urls
## 3.0.0
_breaking changes_
- Replace blank strings with about:blank
- Replace null values with about:blank
## 2.1.0
- Allow relative urls to be sanitized
## 2.0.2
- Sanitize malicious URLs that begin with `\s`
## 2.0.1
- Sanitize malicious URLs that begin with %20
## 2.0.0
- sanitize data: urls
## 1.0.0
- sanitize javascript: urls

21
node_modules/@braintree/sanitize-url/LICENSE generated vendored Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017 Braintree
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

39
node_modules/@braintree/sanitize-url/README.md generated vendored Normal file
View File

@@ -0,0 +1,39 @@
# sanitize-url
## Installation
```sh
npm install -S @braintree/sanitize-url
```
## Usage
```js
var sanitizeUrl = require("@braintree/sanitize-url").sanitizeUrl;
sanitizeUrl("https://example.com"); // 'https://example.com'
sanitizeUrl("http://example.com"); // 'http://example.com'
sanitizeUrl("www.example.com"); // 'www.example.com'
sanitizeUrl("mailto:hello@example.com"); // 'mailto:hello@example.com'
sanitizeUrl(
"https&#0000058//example.com"
); // https://example.com
sanitizeUrl("javascript:alert(document.domain)"); // 'about:blank'
sanitizeUrl("jAvasCrIPT:alert(document.domain)"); // 'about:blank'
sanitizeUrl(decodeURIComponent("JaVaScRiP%0at:alert(document.domain)")); // 'about:blank'
// HTML encoded javascript:alert('XSS')
sanitizeUrl(
"&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041"
); // 'about:blank'
```
## Testing
This library uses [Vitest](https://vitest.dev/). All testing dependencies
will be installed upon `npm install` and the test suite can be executed with
`npm test`. Running the test suite will also run lint checks upon exiting.
npm test
To generate a coverage report, use `npm run coverage`.

View File

@@ -0,0 +1,8 @@
export declare const invalidProtocolRegex: RegExp;
export declare const htmlEntitiesRegex: RegExp;
export declare const htmlCtrlEntityRegex: RegExp;
export declare const ctrlCharactersRegex: RegExp;
export declare const urlSchemeRegex: RegExp;
export declare const whitespaceEscapeCharsRegex: RegExp;
export declare const relativeFirstCharacters: string[];
export declare const BLANK_URL = "about:blank";

11
node_modules/@braintree/sanitize-url/dist/constants.js generated vendored Normal file
View File

@@ -0,0 +1,11 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.BLANK_URL = exports.relativeFirstCharacters = exports.whitespaceEscapeCharsRegex = exports.urlSchemeRegex = exports.ctrlCharactersRegex = exports.htmlCtrlEntityRegex = exports.htmlEntitiesRegex = exports.invalidProtocolRegex = void 0;
exports.invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
exports.htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
exports.htmlCtrlEntityRegex = /&(newline|tab);/gi;
exports.ctrlCharactersRegex = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
exports.urlSchemeRegex = /^.+(:|:)/gim;
exports.whitespaceEscapeCharsRegex = /(\\|%5[cC])((%(6[eE]|72|74))|[nrt])/g;
exports.relativeFirstCharacters = [".", "/"];
exports.BLANK_URL = "about:blank";

1
node_modules/@braintree/sanitize-url/dist/index.d.ts generated vendored Normal file
View File

@@ -0,0 +1 @@
export declare function sanitizeUrl(url?: string): string;

81
node_modules/@braintree/sanitize-url/dist/index.js generated vendored Normal file
View File

@@ -0,0 +1,81 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitizeUrl = void 0;
var constants_1 = require("./constants");
function isRelativeUrlWithoutProtocol(url) {
return constants_1.relativeFirstCharacters.indexOf(url[0]) > -1;
}
function decodeHtmlCharacters(str) {
var removedNullByte = str.replace(constants_1.ctrlCharactersRegex, "");
return removedNullByte.replace(constants_1.htmlEntitiesRegex, function (match, dec) {
return String.fromCharCode(dec);
});
}
function isValidUrl(url) {
return URL.canParse(url);
}
function decodeURI(uri) {
try {
return decodeURIComponent(uri);
}
catch (e) {
// Ignoring error
// It is possible that the URI contains a `%` not associated
// with URI/URL-encoding.
return uri;
}
}
function sanitizeUrl(url) {
if (!url) {
return constants_1.BLANK_URL;
}
var charsToDecode;
var decodedUrl = decodeURI(url.trim());
do {
decodedUrl = decodeHtmlCharacters(decodedUrl)
.replace(constants_1.htmlCtrlEntityRegex, "")
.replace(constants_1.ctrlCharactersRegex, "")
.replace(constants_1.whitespaceEscapeCharsRegex, "")
.trim();
decodedUrl = decodeURI(decodedUrl);
charsToDecode =
decodedUrl.match(constants_1.ctrlCharactersRegex) ||
decodedUrl.match(constants_1.htmlEntitiesRegex) ||
decodedUrl.match(constants_1.htmlCtrlEntityRegex) ||
decodedUrl.match(constants_1.whitespaceEscapeCharsRegex);
} while (charsToDecode && charsToDecode.length > 0);
var sanitizedUrl = decodedUrl;
if (!sanitizedUrl) {
return constants_1.BLANK_URL;
}
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {
return sanitizedUrl;
}
// Remove any leading whitespace before checking the URL scheme
var trimmedUrl = sanitizedUrl.trimStart();
var urlSchemeParseResults = trimmedUrl.match(constants_1.urlSchemeRegex);
if (!urlSchemeParseResults) {
return sanitizedUrl;
}
var urlScheme = urlSchemeParseResults[0].toLowerCase().trim();
if (constants_1.invalidProtocolRegex.test(urlScheme)) {
return constants_1.BLANK_URL;
}
var backSanitized = trimmedUrl.replace(/\\/g, "/");
// Handle special cases for mailto: and custom deep-link protocols
if (urlScheme === "mailto:" || urlScheme.includes("://")) {
return backSanitized;
}
// For http and https URLs, perform additional validation
if (urlScheme === "http:" || urlScheme === "https:") {
if (!isValidUrl(backSanitized)) {
return constants_1.BLANK_URL;
}
var url_1 = new URL(backSanitized);
url_1.protocol = url_1.protocol.toLowerCase();
url_1.hostname = url_1.hostname.toLowerCase();
return url_1.toString();
}
return backSanitized;
}
exports.sanitizeUrl = sanitizeUrl;

40
node_modules/@braintree/sanitize-url/package.json generated vendored Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "@braintree/sanitize-url",
"version": "7.1.1",
"description": "A url sanitizer",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"author": "",
"scripts": {
"prepublishOnly": "npm run build",
"prebuild": "prettier --write .",
"build": "tsc --declaration",
"lint": "eslint --ext js,ts .",
"posttest": "npm run lint",
"test": "vitest",
"coverage": "vitest run --coverage"
},
"repository": {
"type": "git",
"url": "git+https://github.com/braintree/sanitize-url.git"
},
"keywords": [],
"license": "MIT",
"bugs": {
"url": "https://github.com/braintree/sanitize-url/issues"
},
"homepage": "https://github.com/braintree/sanitize-url#readme",
"devDependencies": {
"@types/jest": "^29.4.0",
"@typescript-eslint/eslint-plugin": "^5.54.1",
"@vitest/coverage-v8": "^0.34.2",
"chai": "^4.3.7",
"eslint": "^8.36.0",
"eslint-config-braintree": "^6.0.0-typescript-prep-rc.2",
"eslint-plugin-prettier": "^4.2.1",
"happy-dom": "^15.11.6",
"prettier": "^2.8.4",
"typescript": "^5.1.6",
"vitest": "^0.34.2"
}
}

View File

@@ -0,0 +1,261 @@
/* eslint-disable no-script-url */
import { sanitizeUrl } from "..";
import { BLANK_URL } from "../constants";
describe("sanitizeUrl", () => {
it("does not alter http URLs with alphanumeric characters", () => {
expect(sanitizeUrl("http://example.com/path/to:something")).toBe(
"http://example.com/path/to:something"
);
});
it("does not alter http URLs with ports with alphanumeric characters", () => {
expect(sanitizeUrl("http://example.com:4567/path/to:something")).toBe(
"http://example.com:4567/path/to:something"
);
});
it("does not alter https URLs with alphanumeric characters", () => {
expect(sanitizeUrl("https://example.com")).toBe("https://example.com/");
});
it("does not alter https URLs with ports with alphanumeric characters", () => {
expect(sanitizeUrl("https://example.com:4567/path/to:something")).toBe(
"https://example.com:4567/path/to:something"
);
});
it("does not alter relative-path reference URLs with alphanumeric characters", () => {
expect(sanitizeUrl("./path/to/my.json")).toBe("./path/to/my.json");
});
it("does not alter absolute-path reference URLs with alphanumeric characters", () => {
expect(sanitizeUrl("/path/to/my.json")).toBe("/path/to/my.json");
});
it("does not alter protocol-less network-path URLs with alphanumeric characters", () => {
expect(sanitizeUrl("//google.com/robots.txt")).toBe(
"//google.com/robots.txt"
);
});
it("does not alter protocol-less URLs with alphanumeric characters", () => {
expect(sanitizeUrl("www.example.com")).toBe("www.example.com");
});
it("does not alter deep-link urls with alphanumeric characters", () => {
expect(sanitizeUrl("com.braintreepayments.demo://example")).toBe(
"com.braintreepayments.demo://example"
);
});
it("does not alter mailto urls with alphanumeric characters", () => {
expect(sanitizeUrl("mailto:test@example.com?subject=hello+world")).toBe(
"mailto:test@example.com?subject=hello+world"
);
});
it("does not alter urls with accented characters", () => {
expect(sanitizeUrl("www.example.com/with-áccêntš")).toBe(
"www.example.com/with-áccêntš"
);
});
it("does not strip harmless unicode characters", () => {
expect(sanitizeUrl("www.example.com/лот.рфшишкиü–")).toBe(
"www.example.com/лот.рфшишкиü–"
);
});
it("strips out ctrl chars", () => {
expect(
sanitizeUrl("www.example.com/\u200D\u0000\u001F\x00\x1F\uFEFFfoo")
).toBe("www.example.com/foo");
});
it(`replaces blank urls with ${BLANK_URL}`, () => {
expect(sanitizeUrl("")).toBe(BLANK_URL);
});
it(`replaces null values with ${BLANK_URL}`, () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(sanitizeUrl(null)).toBe(BLANK_URL);
});
it(`replaces undefined values with ${BLANK_URL}`, () => {
expect(sanitizeUrl()).toBe(BLANK_URL);
});
it("removes whitespace from urls", () => {
expect(sanitizeUrl(" http://example.com/path/to:something ")).toBe(
"http://example.com/path/to:something"
);
});
it("removes newline entities from urls", () => {
expect(sanitizeUrl("https://example.com

/something")).toBe(
"https://example.com/something"
);
});
it("decodes html entities", () => {
// all these decode to javascript:alert('xss');
const attackVectors = [
"&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041",
"javascript:alert('XSS')",
"&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29",
"jav	ascript:alert('XSS');",
"  javascript:alert('XSS');",
"javasc	ript: alert('XSS');",
"javasc&#\u0000x09;ript:alert(1)",
"java&&#78&#59;ewLine&newline&#59;&#59;script:alert('XSS')",
"java&NewLine&newline;;script:alert('XSS')",
];
attackVectors.forEach((vector) => {
expect(sanitizeUrl(vector)).toBe(BLANK_URL);
});
// https://example.com/javascript:alert('XSS')
// since the javascript is the url path, and not the protocol,
// this url is technically sanitized
expect(
sanitizeUrl(
"https&#0000058//example.com/&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041"
)
).toBe("https://example.com/javascript:alert('XSS')");
});
it("removes whitespace escape sequences", () => {
const attackVectors = [
"javascri\npt:alert('xss')",
"javascri\rpt:alert('xss')",
"javascri\tpt:alert('xss')",
"javascrip\\%74t:alert('XSS')",
"javascrip%5c%72t:alert()",
"javascrip%5Ctt:alert()",
"javascrip%255Ctt:alert()",
"javascrip%25%35Ctt:alert()",
"javascrip%25%35%43tt:alert()",
"javascrip%25%32%35%25%33%35%25%34%33rt:alert()",
"javascrip%255Crt:alert('%25xss')",
];
attackVectors.forEach((vector) => {
expect(sanitizeUrl(vector)).toBe(BLANK_URL);
});
});
it("backslash prefixed attack vectors", () => {
const attackVectors = [
"\fjavascript:alert()",
"\vjavascript:alert()",
"\tjavascript:alert()",
"\njavascript:alert()",
"\rjavascript:alert()",
"\u0000javascript:alert()",
"\u0001javascript:alert()",
];
attackVectors.forEach((vector) => {
expect(sanitizeUrl(vector)).toBe(BLANK_URL);
});
});
it("reverses backslashes", () => {
const attack = "\\j\\av\\a\\s\\cript:alert()";
expect(sanitizeUrl(attack)).toBe("/j/av/a/s/cript:alert()");
});
describe("invalid protocols", () => {
describe.each(["javascript", "data", "vbscript"])("%s", (protocol) => {
it(`replaces ${protocol} urls with ${BLANK_URL}`, () => {
expect(sanitizeUrl(`${protocol}:alert(document.domain)`)).toBe(
BLANK_URL
);
});
it(`allows ${protocol} urls that start with a letter prefix`, () => {
expect(sanitizeUrl(`not_${protocol}:alert(document.domain)`)).toBe(
`not_${protocol}:alert(document.domain)`
);
});
it(`disallows ${protocol} urls that start with non-\w characters as a suffix for the protocol`, () => {
expect(sanitizeUrl(`&!*${protocol}:alert(document.domain)`)).toBe(
BLANK_URL
);
});
it(`disallows ${protocol} urls that use : for the colon portion of the url`, () => {
expect(sanitizeUrl(`${protocol}:alert(document.domain)`)).toBe(
BLANK_URL
);
expect(sanitizeUrl(`${protocol}:alert(document.domain)`)).toBe(
BLANK_URL
);
});
it(`disregards capitalization for ${protocol} urls`, () => {
// upper case every other letter in protocol name
const mixedCapitalizationProtocol = protocol
.split("")
.map((character, index) => {
if (index % 2 === 0) {
return character.toUpperCase();
}
return character;
})
.join("");
expect(
sanitizeUrl(`${mixedCapitalizationProtocol}:alert(document.domain)`)
).toBe(BLANK_URL);
});
it(`ignores invisible ctrl characters in ${protocol} urls`, () => {
const protocolWithControlCharacters = protocol
.split("")
.map((character, index) => {
if (index === 1) {
return character + "%EF%BB%BF%EF%BB%BF";
} else if (index === 2) {
return character + "%e2%80%8b";
}
return character;
})
.join("");
expect(
sanitizeUrl(
decodeURIComponent(
`${protocolWithControlCharacters}:alert(document.domain)`
)
)
).toBe(BLANK_URL);
});
it(`replaces ${protocol} urls with ${BLANK_URL} when url begins with %20`, () => {
expect(
sanitizeUrl(
decodeURIComponent(`%20%20%20%20${protocol}:alert(document.domain)`)
)
).toBe(BLANK_URL);
});
it(`replaces ${protocol} urls with ${BLANK_URL} when ${protocol} url begins with spaces`, () => {
expect(sanitizeUrl(` ${protocol}:alert(document.domain)`)).toBe(
BLANK_URL
);
});
it(`does not replace ${protocol}: if it is not in the scheme of the URL`, () => {
expect(sanitizeUrl(`http://example.com#${protocol}:foo`)).toBe(
`http://example.com#${protocol}:foo`
);
});
});
});
});

10
node_modules/@braintree/sanitize-url/src/constants.ts generated vendored Normal file
View File

@@ -0,0 +1,10 @@
export const invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
export const htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
export const htmlCtrlEntityRegex = /&(newline|tab);/gi;
export const ctrlCharactersRegex =
/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
export const urlSchemeRegex = /^.+(:|:)/gim;
export const whitespaceEscapeCharsRegex =
/(\\|%5[cC])((%(6[eE]|72|74))|[nrt])/g;
export const relativeFirstCharacters = [".", "/"];
export const BLANK_URL = "about:blank";

107
node_modules/@braintree/sanitize-url/src/index.ts generated vendored Normal file
View File

@@ -0,0 +1,107 @@
import {
BLANK_URL,
ctrlCharactersRegex,
htmlCtrlEntityRegex,
htmlEntitiesRegex,
invalidProtocolRegex,
relativeFirstCharacters,
whitespaceEscapeCharsRegex,
urlSchemeRegex,
} from "./constants";
function isRelativeUrlWithoutProtocol(url: string): boolean {
return relativeFirstCharacters.indexOf(url[0]) > -1;
}
function decodeHtmlCharacters(str: string) {
const removedNullByte = str.replace(ctrlCharactersRegex, "");
return removedNullByte.replace(htmlEntitiesRegex, (match, dec) => {
return String.fromCharCode(dec);
});
}
function isValidUrl(url: string): boolean {
return URL.canParse(url);
}
function decodeURI(uri: string): string {
try {
return decodeURIComponent(uri);
} catch (e: unknown) {
// Ignoring error
// It is possible that the URI contains a `%` not associated
// with URI/URL-encoding.
return uri;
}
}
export function sanitizeUrl(url?: string): string {
if (!url) {
return BLANK_URL;
}
let charsToDecode;
let decodedUrl = decodeURI(url.trim());
do {
decodedUrl = decodeHtmlCharacters(decodedUrl)
.replace(htmlCtrlEntityRegex, "")
.replace(ctrlCharactersRegex, "")
.replace(whitespaceEscapeCharsRegex, "")
.trim();
decodedUrl = decodeURI(decodedUrl);
charsToDecode =
decodedUrl.match(ctrlCharactersRegex) ||
decodedUrl.match(htmlEntitiesRegex) ||
decodedUrl.match(htmlCtrlEntityRegex) ||
decodedUrl.match(whitespaceEscapeCharsRegex);
} while (charsToDecode && charsToDecode.length > 0);
const sanitizedUrl = decodedUrl;
if (!sanitizedUrl) {
return BLANK_URL;
}
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {
return sanitizedUrl;
}
// Remove any leading whitespace before checking the URL scheme
const trimmedUrl = sanitizedUrl.trimStart();
const urlSchemeParseResults = trimmedUrl.match(urlSchemeRegex);
if (!urlSchemeParseResults) {
return sanitizedUrl;
}
const urlScheme = urlSchemeParseResults[0].toLowerCase().trim();
if (invalidProtocolRegex.test(urlScheme)) {
return BLANK_URL;
}
const backSanitized = trimmedUrl.replace(/\\/g, "/");
// Handle special cases for mailto: and custom deep-link protocols
if (urlScheme === "mailto:" || urlScheme.includes("://")) {
return backSanitized;
}
// For http and https URLs, perform additional validation
if (urlScheme === "http:" || urlScheme === "https:") {
if (!isValidUrl(backSanitized)) {
return BLANK_URL;
}
const url = new URL(backSanitized);
url.protocol = url.protocol.toLowerCase();
url.hostname = url.hostname.toLowerCase();
return url.toString();
}
return backSanitized;
}

12
node_modules/@braintree/sanitize-url/tsconfig.json generated vendored Normal file
View File

@@ -0,0 +1,12 @@
{
"compilerOptions": {
"outDir": "./dist",
"allowJs": true,
"strict": true,
"target": "es5",
"resolveJsonModule": true,
"lib": ["es2015", "dom"]
},
"include": ["./src/**/*"],
"exclude": ["**/__tests__/*"]
}

View File

@@ -0,0 +1,8 @@
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
globals: true,
environment: "happy-dom",
},
});