config redirect

This commit is contained in:
Nuno Coração
2023-01-29 22:30:24 +00:00
parent 17557c7d73
commit 5fb4bd8083
9905 changed files with 1258996 additions and 36355 deletions
+1
View File
@@ -0,0 +1 @@
dist
+6
View File
@@ -0,0 +1,6 @@
{
"extends": "braintree/client",
"rules": {
"no-control-regex": 0
}
}
+1
View File
@@ -0,0 +1 @@
* @braintree/team-sdk-js
+17
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: "14.x"
- run: npm install
- run: npm test
+1
View File
@@ -0,0 +1 @@
v16
+2
View File
@@ -0,0 +1,2 @@
*-lock.json
dist
+84
View File
@@ -0,0 +1,84 @@
# 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
# CHANGELOG
## 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
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.
+29
View File
@@ -0,0 +1,29 @@
# 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'
```
+1
View File
@@ -0,0 +1 @@
export declare function sanitizeUrl(url?: string): string;
+38
View File
@@ -0,0 +1,38 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.sanitizeUrl = void 0;
var invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
var htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
var ctrlCharactersRegex = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
var urlSchemeRegex = /^([^:]+):/gm;
var relativeFirstCharacters = [".", "/"];
function isRelativeUrlWithoutProtocol(url) {
return relativeFirstCharacters.indexOf(url[0]) > -1;
}
// adapted from https://stackoverflow.com/a/29824550/2601552
function decodeHtmlCharacters(str) {
return str.replace(htmlEntitiesRegex, function (match, dec) {
return String.fromCharCode(dec);
});
}
function sanitizeUrl(url) {
var sanitizedUrl = decodeHtmlCharacters(url || "")
.replace(ctrlCharactersRegex, "")
.trim();
if (!sanitizedUrl) {
return "about:blank";
}
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {
return sanitizedUrl;
}
var urlSchemeParseResults = sanitizedUrl.match(urlSchemeRegex);
if (!urlSchemeParseResults) {
return sanitizedUrl;
}
var urlScheme = urlSchemeParseResults[0];
if (invalidProtocolRegex.test(urlScheme)) {
return "about:blank";
}
return sanitizedUrl;
}
exports.sanitizeUrl = sanitizeUrl;
+47
View File
@@ -0,0 +1,47 @@
{
"name": "@braintree/sanitize-url",
"version": "6.0.0",
"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": "jest"
},
"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": "^27.4.0",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"chai": "^4.3.4",
"eslint": "^8.6.0",
"eslint-config-braintree": "^6.0.0-typescript-prep-rc.2",
"eslint-plugin-prettier": "^4.0.0",
"jest": "^27.4.5",
"prettier": "^2.5.1",
"ts-jest": "^27.1.2",
"typescript": "^4.5.4"
},
"jest": {
"testEnvironment": "jsdom",
"preset": "ts-jest",
"globals": {
"ts-jest": {
"tsconfig": "src/__tests__/tsconfig.json"
}
}
}
}
+199
View File
@@ -0,0 +1,199 @@
/* eslint-disable no-script-url */
import { sanitizeUrl } from "..";
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 about:blank", () => {
expect(sanitizeUrl("")).toBe("about:blank");
});
it("replaces null values with about:blank", () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
expect(sanitizeUrl(null)).toBe("about:blank");
});
it("replaces undefined values with about:blank", () => {
expect(sanitizeUrl()).toBe("about:blank");
});
it("removes whitespace from urls", () => {
expect(sanitizeUrl(" http://example.com/path/to:something ")).toBe(
"http://example.com/path/to: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');",
];
attackVectors.forEach((vector) => {
expect(sanitizeUrl(vector)).toBe("about:blank");
});
// 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')");
});
describe("invalid protocols", () => {
describe.each(["javascript", "data", "vbscript"])("%s", (protocol) => {
it(`replaces ${protocol} urls with about:blank`, () => {
expect(sanitizeUrl(`${protocol}:alert(document.domain)`)).toBe(
"about:blank"
);
});
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(
"about:blank"
);
});
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("about:blank");
});
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("about:blank");
});
it(`replaces ${protocol} urls with about:blank when url begins with %20`, () => {
expect(
sanitizeUrl(
decodeURIComponent(`%20%20%20%20${protocol}:alert(document.domain)`)
)
).toBe("about:blank");
});
it(`replaces ${protocol} urls with about:blank when ${protocol} url begins with spaces`, () => {
expect(sanitizeUrl(` ${protocol}:alert(document.domain)`)).toBe(
"about:blank"
);
});
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`
);
});
});
});
});
+5
View File
@@ -0,0 +1,5 @@
{
"compilerOptions": {
"strict": false
}
}
+45
View File
@@ -0,0 +1,45 @@
const invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
const htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
const ctrlCharactersRegex =
/[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;
const urlSchemeRegex = /^([^:]+):/gm;
const relativeFirstCharacters = [".", "/"];
function isRelativeUrlWithoutProtocol(url: string): boolean {
return relativeFirstCharacters.indexOf(url[0]) > -1;
}
// adapted from https://stackoverflow.com/a/29824550/2601552
function decodeHtmlCharacters(str: string) {
return str.replace(htmlEntitiesRegex, (match, dec) => {
return String.fromCharCode(dec);
});
}
export function sanitizeUrl(url?: string): string {
const sanitizedUrl = decodeHtmlCharacters(url || "")
.replace(ctrlCharactersRegex, "")
.trim();
if (!sanitizedUrl) {
return "about:blank";
}
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {
return sanitizedUrl;
}
const urlSchemeParseResults = sanitizedUrl.match(urlSchemeRegex);
if (!urlSchemeParseResults) {
return sanitizedUrl;
}
const urlScheme = urlSchemeParseResults[0];
if (invalidProtocolRegex.test(urlScheme)) {
return "about:blank";
}
return sanitizedUrl;
}
+11
View File
@@ -0,0 +1,11 @@
{
"compilerOptions": {
"outDir": "./dist",
"allowJs": true,
"strict": true,
"target": "es5",
"lib": ["es2015", "dom"]
},
"include": ["./src/**/*"],
"exclude": ["**/__tests__/*"]
}