url-sanitizer 0.7.6 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -14
- package/dist/url-sanitizer.js +48 -6
- package/dist/url-sanitizer.js.map +3 -3
- package/dist/url-sanitizer.min.js +3 -3
- package/dist/url-sanitizer.min.js.map +4 -4
- package/package.json +8 -6
- package/src/mjs/file-reader.js +304 -0
- package/src/mjs/uri-util.js +59 -7
package/README.md
CHANGED
|
@@ -35,9 +35,8 @@ import urlSanitizer, {
|
|
|
35
35
|
## sanitizeURL(url, opt)
|
|
36
36
|
|
|
37
37
|
Sanitize the given URL.
|
|
38
|
-
* `data` and `file` schemes must be explicitly allowed.
|
|
39
|
-
* `blob`
|
|
40
|
-
See [Figure out how to sanitize the blob URL · Issue #4](https://github.com/asamuzaK/urlSanitizer/issues/4)
|
|
38
|
+
* `blob`, `data` and `file` schemes must be explicitly allowed.
|
|
39
|
+
* Given a `blob` URL, returns a sanitized `data` URL.
|
|
41
40
|
|
|
42
41
|
### Parameters
|
|
43
42
|
|
|
@@ -59,6 +58,7 @@ const res1 = await sanitizeURL('http://example.com/"onmouseover="alert(1)"?<scri
|
|
|
59
58
|
console.log(decodeURIComponent(res1));
|
|
60
59
|
// => 'http://example.com/"onmouseover="alert(1)"?<script>alert('XSS');</script>'
|
|
61
60
|
|
|
61
|
+
|
|
62
62
|
// Parse and purify data URL
|
|
63
63
|
const res2 = await sanitizeURL('data:text/html,<div><script>alert(1);</script></div><p onclick="alert(2)"></p>', {
|
|
64
64
|
allow: ['data']
|
|
@@ -68,7 +68,8 @@ const res2 = await sanitizeURL('data:text/html,<div><script>alert(1);</script></
|
|
|
68
68
|
console.log(decodeURIComponent(res2));
|
|
69
69
|
// => 'data:text/html,<div></div><p></p>'
|
|
70
70
|
|
|
71
|
-
|
|
71
|
+
|
|
72
|
+
// Can parse and purify base64 encoded data
|
|
72
73
|
const base64data3 = btoa('<div><script>alert(1);</script></div>');
|
|
73
74
|
const res3 = await sanitizeURL(`data:text/html;base64,${base64data3}`, {
|
|
74
75
|
allow: ['data']
|
|
@@ -78,58 +79,79 @@ const res3 = await sanitizeURL(`data:text/html;base64,${base64data3}`, {
|
|
|
78
79
|
console.log(decodeURIComponent(res3));
|
|
79
80
|
// => 'data:text/html,<div></div>'
|
|
80
81
|
|
|
81
|
-
const
|
|
82
|
-
const
|
|
82
|
+
const base64data3_2 = btoa('<div><img src="javascript:alert(1)"></div>');
|
|
83
|
+
const res3_2 = await sanitizeURL(`data:text/html;base64,${base64data3_2}`);
|
|
83
84
|
// => 'data:text/html,%3Cdiv%3E%3Cimg%3E%3C/div%3E'
|
|
84
85
|
|
|
85
|
-
console.log(decodeURIComponent(
|
|
86
|
+
console.log(decodeURIComponent(res3_2));
|
|
86
87
|
// => 'data:text/html,<div><img></div>'
|
|
87
88
|
|
|
89
|
+
|
|
90
|
+
// Can parse and sanitize blob URL
|
|
91
|
+
const blob4 = new Blob(['<svg><g onload="alert(1)"/></svg>'], {
|
|
92
|
+
type: 'image/svg+xml'
|
|
93
|
+
});
|
|
94
|
+
const url4 = URL.createObjectURL(blob4);
|
|
95
|
+
const res4 = await func(url, {
|
|
96
|
+
allow: ['blob']
|
|
97
|
+
});
|
|
98
|
+
// => 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E'
|
|
99
|
+
|
|
100
|
+
console.log(decodeURIComponent(res4));
|
|
101
|
+
// => 'data:image/svg+xml,<svg><g></g></svg>'
|
|
102
|
+
|
|
103
|
+
|
|
88
104
|
// Deny if the scheme matches the `deny` list
|
|
89
105
|
const res5 = await sanitizeURL('web+foo://example.com', {
|
|
90
106
|
deny: ['web+foo']
|
|
91
107
|
});
|
|
92
108
|
// => null
|
|
93
109
|
|
|
110
|
+
|
|
94
111
|
// Allow only if the scheme matches the `only` list
|
|
95
112
|
const res6 = await sanitizeURL('http://example.com', {
|
|
96
113
|
only: ['data', 'git', 'https']
|
|
97
114
|
});
|
|
98
115
|
// => null
|
|
99
116
|
|
|
100
|
-
const
|
|
117
|
+
const res6_2 = await sanitizeURL('https://example.com/"onmouseover="alert(1)"', {
|
|
101
118
|
only: ['data', 'git', 'https']
|
|
102
119
|
});
|
|
103
120
|
// => 'https://example.com/%26quot;onmouseover=%26quot;alert(1)%26quot;'
|
|
104
121
|
|
|
105
|
-
console.log(decodeURIComponent(
|
|
122
|
+
console.log(decodeURIComponent(res6_2));
|
|
106
123
|
// => 'https://example.com/"onmouseover="alert(1)"'
|
|
107
124
|
|
|
125
|
+
|
|
108
126
|
// `only` also allows combination of the schemes in the list
|
|
109
|
-
const
|
|
127
|
+
const res7 = await sanitizeURL('git+https://example.com/foo.git?<script>alert(1)</script>', {
|
|
110
128
|
only: ['data', 'git', 'https']
|
|
111
129
|
});
|
|
112
130
|
// => 'git+https://example.com/foo.git?%26lt;script%26gt;alert(1)%26lt;/script%26gt;'
|
|
113
131
|
|
|
114
|
-
console.log(decodeURIComponent(
|
|
132
|
+
console.log(decodeURIComponent(res7));
|
|
115
133
|
// => 'git+https://example.com/foo.git?<script>alert(1)</script>'
|
|
116
134
|
|
|
135
|
+
|
|
117
136
|
// Remove the tag or quote and the rest of the URL following it.
|
|
118
|
-
const
|
|
137
|
+
const res8 = await sanitizeURL('https://example.com/" onclick="alert(1)"', {
|
|
119
138
|
remove: true
|
|
120
139
|
});
|
|
121
140
|
// => 'https://example.com/'
|
|
122
141
|
|
|
123
|
-
const
|
|
142
|
+
const res8_2 = await sanitizeURL('https://example.com/?<script>alert(1)</script>', {
|
|
124
143
|
remove: true
|
|
125
144
|
});
|
|
126
145
|
// => 'https://example.com/?'
|
|
127
146
|
```
|
|
128
147
|
|
|
129
148
|
|
|
130
|
-
## sanitizeURLSync
|
|
149
|
+
## sanitizeURLSync(url, opt)
|
|
131
150
|
|
|
132
151
|
Synchronous version of the [sanitizeURL()](#sanitizeurlurl-opt).
|
|
152
|
+
* `data` and `file` schemes must be explicitly allowed.
|
|
153
|
+
* `blob` scheme is not supported, returns `null`.
|
|
154
|
+
Use async [sanitizeURL()](#sanitizeurlurl-opt) for `blob`.
|
|
133
155
|
|
|
134
156
|
|
|
135
157
|
## parseURL(url)
|
package/dist/url-sanitizer.js
CHANGED
|
@@ -998,6 +998,10 @@ var require_purify = __commonJS({
|
|
|
998
998
|
}
|
|
999
999
|
});
|
|
1000
1000
|
|
|
1001
|
+
// bundle/mjs/file-reader.js
|
|
1002
|
+
var FileReader = window.FileReader;
|
|
1003
|
+
var fileReader = new FileReader();
|
|
1004
|
+
|
|
1001
1005
|
// bundle/mjs/common.js
|
|
1002
1006
|
var TYPE_FROM = 8;
|
|
1003
1007
|
var TYPE_TO = -1;
|
|
@@ -1704,6 +1708,13 @@ var parseURLEncodedNumCharRef = (str, nest = 0) => {
|
|
|
1704
1708
|
}
|
|
1705
1709
|
return res;
|
|
1706
1710
|
};
|
|
1711
|
+
var createDataURLFromBlob = (blob) => new Promise((resolve, reject) => {
|
|
1712
|
+
const reader = new FileReader();
|
|
1713
|
+
reader.addEventListener("error", () => reject(reader.error));
|
|
1714
|
+
reader.addEventListener("abort", () => resolve(reader.result));
|
|
1715
|
+
reader.addEventListener("load", () => resolve(reader.result));
|
|
1716
|
+
reader.readAsDataURL(blob);
|
|
1717
|
+
});
|
|
1707
1718
|
var URISchemes = class {
|
|
1708
1719
|
/* private fields */
|
|
1709
1720
|
#schemes;
|
|
@@ -1848,7 +1859,8 @@ var URLSanitizer = class extends URISchemes {
|
|
|
1848
1859
|
}
|
|
1849
1860
|
/**
|
|
1850
1861
|
* sanitize URL
|
|
1851
|
-
* NOTE: `
|
|
1862
|
+
* NOTE: `data` and `file` schemes must be explicitly allowed
|
|
1863
|
+
* `blob` URLs should be converted to `data` URLs
|
|
1852
1864
|
* `javascript` and `vbscript` schemes can not be allowed
|
|
1853
1865
|
*
|
|
1854
1866
|
* @param {string} url - URL
|
|
@@ -2097,12 +2109,12 @@ var URLSanitizer = class extends URISchemes {
|
|
|
2097
2109
|
var urlSanitizer = new URLSanitizer();
|
|
2098
2110
|
var isURISync = (uri) => urlSanitizer.verify(uri);
|
|
2099
2111
|
var isURI = async (uri) => {
|
|
2100
|
-
const res =
|
|
2112
|
+
const res = urlSanitizer.verify(uri);
|
|
2101
2113
|
return res;
|
|
2102
2114
|
};
|
|
2103
2115
|
var parseURLSync = (url) => urlSanitizer.parse(url);
|
|
2104
2116
|
var parseURL = async (url) => {
|
|
2105
|
-
const res =
|
|
2117
|
+
const res = urlSanitizer.parse(url);
|
|
2106
2118
|
return res;
|
|
2107
2119
|
};
|
|
2108
2120
|
var sanitizeURLSync = (url, opt) => urlSanitizer.sanitize(url, opt ?? {
|
|
@@ -2110,9 +2122,39 @@ var sanitizeURLSync = (url, opt) => urlSanitizer.sanitize(url, opt ?? {
|
|
|
2110
2122
|
deny: [],
|
|
2111
2123
|
only: []
|
|
2112
2124
|
});
|
|
2113
|
-
var sanitizeURL = async (url, opt
|
|
2114
|
-
|
|
2115
|
-
|
|
2125
|
+
var sanitizeURL = async (url, opt = {
|
|
2126
|
+
allow: [],
|
|
2127
|
+
deny: [],
|
|
2128
|
+
only: []
|
|
2129
|
+
}) => {
|
|
2130
|
+
const { protocol } = new URL(url);
|
|
2131
|
+
let res;
|
|
2132
|
+
if (protocol === "blob:") {
|
|
2133
|
+
const { allow, deny, only } = opt;
|
|
2134
|
+
if (Array.isArray(allow) && allow.includes("blob") && !(Array.isArray(deny) && deny.includes("blob")) || Array.isArray(only) && only.includes("blob")) {
|
|
2135
|
+
const blob = await fetch(url).then((r) => r.blob());
|
|
2136
|
+
const data = await createDataURLFromBlob(blob);
|
|
2137
|
+
if (data) {
|
|
2138
|
+
if (Array.isArray(only)) {
|
|
2139
|
+
if (!only.includes("data")) {
|
|
2140
|
+
only.push("data");
|
|
2141
|
+
}
|
|
2142
|
+
} else if (Array.isArray(allow)) {
|
|
2143
|
+
if (!allow.includes("data")) {
|
|
2144
|
+
allow.push("data");
|
|
2145
|
+
}
|
|
2146
|
+
if (Array.isArray(deny) && deny.includes("data")) {
|
|
2147
|
+
const i = deny.indexOf("data");
|
|
2148
|
+
deny.splice(i, 1);
|
|
2149
|
+
}
|
|
2150
|
+
}
|
|
2151
|
+
res = urlSanitizer.sanitize(data, opt);
|
|
2152
|
+
}
|
|
2153
|
+
}
|
|
2154
|
+
} else {
|
|
2155
|
+
res = urlSanitizer.sanitize(url, opt);
|
|
2156
|
+
}
|
|
2157
|
+
return res || null;
|
|
2116
2158
|
};
|
|
2117
2159
|
export {
|
|
2118
2160
|
urlSanitizer as default,
|