Socket
Socket
Sign inDemoInstall

@braintree/sanitize-url

Package Overview
Dependencies
Maintainers
1
Versions
25
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@braintree/sanitize-url - npm Package Compare versions

Comparing version 5.0.2 to 6.0.0

20

CHANGELOG.md

@@ -0,1 +1,21 @@

# 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

@@ -2,0 +22,0 @@

13

dist/index.js

@@ -5,2 +5,3 @@ "use strict";

var invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
var htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
var ctrlCharactersRegex = /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;

@@ -12,7 +13,15 @@ var urlSchemeRegex = /^([^:]+):/gm;

}
// 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) {
if (!url) {
var sanitizedUrl = decodeHtmlCharacters(url || "")
.replace(ctrlCharactersRegex, "")
.trim();
if (!sanitizedUrl) {
return "about:blank";
}
var sanitizedUrl = url.replace(ctrlCharactersRegex, "").trim();
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {

@@ -19,0 +28,0 @@ return sanitizedUrl;

20

package.json
{
"name": "@braintree/sanitize-url",
"version": "5.0.2",
"version": "6.0.0",
"description": "A url sanitizer",

@@ -27,10 +27,12 @@ "main": "dist/index.js",

"devDependencies": {
"@types/jest": "^26.0.23",
"@types/jest": "^27.4.0",
"@typescript-eslint/eslint-plugin": "^5.9.0",
"chai": "^4.3.4",
"eslint": "^7.27.0",
"eslint-config-braintree": "^5.0.0-typescript-prep-rc.18",
"jest": "^27.0.3",
"prettier": "^2.3.0",
"ts-jest": "^27.0.2",
"typescript": "^4.3.2"
"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"
},

@@ -42,3 +44,3 @@ "jest": {

"ts-jest": {
"tsConfig": "src/__tests__/tsconfig.json"
"tsconfig": "src/__tests__/tsconfig.json"
}

@@ -45,0 +47,0 @@ }

@@ -18,2 +18,5 @@ # sanitize-url

sanitizeUrl("mailto:hello@example.com"); // 'mailto:hello@example.com'
sanitizeUrl(
"https&#0000058//example.com"
); // https://example.com

@@ -23,2 +26,6 @@ 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'
```

@@ -95,2 +95,26 @@ /* eslint-disable no-script-url */

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", () => {

@@ -97,0 +121,0 @@ describe.each(["javascript", "data", "vbscript"])("%s", (protocol) => {

const invalidProtocolRegex = /^([^\w]*)(javascript|data|vbscript)/im;
const htmlEntitiesRegex = /&#(\w+)(^\w|;)?/g;
const ctrlCharactersRegex =

@@ -11,9 +12,18 @@ /[\u0000-\u001F\u007F-\u009F\u2000-\u200D\uFEFF]/gim;

// 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 {
if (!url) {
const sanitizedUrl = decodeHtmlCharacters(url || "")
.replace(ctrlCharactersRegex, "")
.trim();
if (!sanitizedUrl) {
return "about:blank";
}
const sanitizedUrl = url.replace(ctrlCharactersRegex, "").trim();
if (isRelativeUrlWithoutProtocol(sanitizedUrl)) {

@@ -20,0 +30,0 @@ return sanitizedUrl;

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc