url-sanitizer 4.1.3 → 4.2.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 +7 -4
- package/dist/url-sanitizer-wo-dompurify.min.js +1 -1
- package/dist/url-sanitizer-wo-dompurify.min.js.map +3 -3
- package/dist/url-sanitizer.min.js +3 -3
- package/dist/url-sanitizer.min.js.map +3 -3
- package/package.json +9 -10
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/dompurify.js +1 -1
- package/src/mjs/regexp.js +7 -0
- package/src/mjs/sanitizer.js +37 -35
- package/src/mjs/uri-util.js +15 -14
- package/types/index.d.ts +8 -1
- package/types/mjs/common.d.ts +2 -2
- package/types/mjs/constant.d.ts +7 -7
- package/types/mjs/dompurify.d.ts +1 -1
- package/types/mjs/regexp.d.ts +15 -14
- package/types/mjs/sanitizer.d.ts +63 -52
- package/types/mjs/text-chars.d.ts +3 -3
- package/types/mjs/uri-util.d.ts +9 -9
package/package.json
CHANGED
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"@braintree/sanitize-url": "^7.1.2",
|
|
33
33
|
"@types/jsdom": "^28.0.3",
|
|
34
34
|
"@types/node": "^26.1.1",
|
|
35
|
-
"c8": "^
|
|
35
|
+
"c8": "^12.0.0",
|
|
36
36
|
"chai": "^6.2.2",
|
|
37
37
|
"commander": "^15.0.0",
|
|
38
38
|
"copyfiles-ng": "^2.4.7",
|
|
39
39
|
"esbuild": "^0.28.1",
|
|
40
40
|
"eslint": "^9.39.5",
|
|
41
41
|
"eslint-config-prettier": "^10.1.8",
|
|
42
|
-
"eslint-plugin-jsdoc": "^63.
|
|
42
|
+
"eslint-plugin-jsdoc": "^63.2.2",
|
|
43
43
|
"eslint-plugin-prettier": "^5.5.6",
|
|
44
44
|
"eslint-plugin-regexp": "^3.1.1",
|
|
45
45
|
"eslint-plugin-unicorn": "^64.0.0",
|
|
@@ -50,23 +50,22 @@
|
|
|
50
50
|
"mocha": "^11.7.6",
|
|
51
51
|
"neostandard": "^0.13.0",
|
|
52
52
|
"papaparse": "^5.5.4",
|
|
53
|
-
"prettier": "^3.9.
|
|
54
|
-
"sinon": "^22.
|
|
53
|
+
"prettier": "^3.9.6",
|
|
54
|
+
"sinon": "^22.1.0",
|
|
55
55
|
"strict-url-sanitise": "^0.0.1",
|
|
56
|
-
"typescript": "^
|
|
57
|
-
"undici": "^7.
|
|
56
|
+
"typescript": "^7.0.2",
|
|
57
|
+
"undici": "^7.29.0"
|
|
58
58
|
},
|
|
59
59
|
"overrides": {
|
|
60
60
|
"c8": {
|
|
61
61
|
"yargs": "^18.0.0"
|
|
62
62
|
},
|
|
63
63
|
"eslint": {
|
|
64
|
-
"
|
|
65
|
-
"js-yaml": "^4.2.0"
|
|
64
|
+
"js-yaml": "^4.3.0"
|
|
66
65
|
},
|
|
67
66
|
"mocha": {
|
|
68
67
|
"diff": "^8.0.3",
|
|
69
|
-
"js-yaml": "^4.
|
|
68
|
+
"js-yaml": "^4.3.0"
|
|
70
69
|
},
|
|
71
70
|
"serialize-javascript": "^7.0.4"
|
|
72
71
|
},
|
|
@@ -94,5 +93,5 @@
|
|
|
94
93
|
"engines": {
|
|
95
94
|
"node": ">=22.13"
|
|
96
95
|
},
|
|
97
|
-
"version": "4.
|
|
96
|
+
"version": "4.2.0"
|
|
98
97
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","afp","afs","agtp","aim","amss","android","appdata","apt","ar","ari","ark","at","attachment","aw","barion","beshare","bitcoin","bitcoincash","bl","blob","bluetooth","bolo","brid","browserext","cabal","caip","calculator","callto","cap","cast","casts","chrome","chrome-extension","cid","coap","coaps","com-eventbrite-attendee","content","content-type","crid","cstr","cttps","cvs","dab","dat","data","dav","dhttp","diaspora","dict","did","dilithium3","dis","dlna-playcontainer","dlna-playsingle","dnp","dns","dntp","doi","donau","dpp","drm","dtmi","dtn","dvb","dvx","dweb","ed2k","eid","elsi","embedded","ens","esim","ethereum","example","ez","facetime","feed","feedready","fido","file","finger","first-run-pen-experience","fish","fm","ftp","fuchsia-pkg","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gtalk","h323","ham","hcap","hcp","hs20","http","https","hxxp","hxxps","hydrazone","hyper","i0","iax","ibi","ibi-","icap","icon","ilstring","im","imap","info","iotdisco","ipfs","ipn","ipns","ipp","ipps","irc","irc6","ircs","iris","iris.beep","iris.lwz","iris.xpc","iris.xpcs","isostore","itms","jabber","jar","jms","keyparc","lastfm","lbry","ldap","ldaps","leaptofrogans","lid","linkid","lorawan","lpa","lvlt","machineProvisioningProgressReporter","magnet","mailto","maps","market","matrix","mdoc","mdoc-openid4vp","message","microsoft.windows.camera","microsoft.windows.camera.multipicker","microsoft.windows.camera.picker","mid","mms","mongodb","moz","moz-extension","mqtt","mqtts","ms-access","ms-appinstaller","ms-browser-extension","ms-calculator","ms-drive-to","ms-enrollment","ms-excel","ms-eyecontrolspeech","ms-gamebarservices","ms-gamingoverlay","ms-getoffice","ms-help","ms-infopath","ms-inputapp","ms-launchremotedesktop","ms-lockscreencomponent-config","ms-media-stream-id","ms-meetnow","ms-mixedrealitycapture","ms-mobileplans","ms-newsandinterests","ms-officeapp","ms-people","ms-personacard","ms-powerpoint","ms-project","ms-publisher","ms-recall","ms-remotedesktop","ms-remotedesktop-launch","ms-restoretabcompanion","ms-screenclip","ms-screensketch","ms-search","ms-search-repair","ms-secondary-screen-controller","ms-secondary-screen-setup","ms-settings","ms-settings-airplanemode","ms-settings-bluetooth","ms-settings-camera","ms-settings-cellular","ms-settings-cloudstorage","ms-settings-connectabledevices","ms-settings-displays-topology","ms-settings-emailandaccounts","ms-settings-language","ms-settings-location","ms-settings-lock","ms-settings-nfctransactions","ms-settings-notifications","ms-settings-power","ms-settings-privacy","ms-settings-proximity","ms-settings-screenrotation","ms-settings-wifi","ms-settings-workplace","ms-spd","ms-stickers","ms-sttoverlay","ms-transit-to","ms-useractivityset","ms-uup","ms-virtualtouchpad","ms-visio","ms-walk-to","ms-whiteboard","ms-whiteboard-cmd","ms-widgetboard","ms-widgets","ms-word","msnim","msrp","msrps","mss","mt","mtqp","mtrust","mumble","mupdate","musik","mvn","mvrp","mvrps","news","nfs","ni","nih","nntp","notes","npamp","num","ocf","oid","onenote","onenote-cmd","opaquelocktoken","openid","openpgp4fpr","otpauth","palm","paparazzi","payto","pkcs11","platform","pop","pres","proxy","psyc","pttp","pwid","qb","query","quic-transport","redis","rediss","reload","res","resource","rmi","rsync","rtmfp","rtmp","rtsp","rtsps","rtspu","sarif","secondlife","secret-token","service","session","sftp","sgn","shc","shelter","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","soap.beep","soap.beeps","soldat","spacify","spiffe","spotify","ssb","ssh","sss","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","tttps","turn","turns","tv","udp","unreal","upn","urn","ut2004","uuid-in-package","v-event","vemmi","ventrilo","ves","view-source","vnc","vscode","vscode-insiders","vsls","w3","wasm","wasm-js","wcr","web3","webcal","wifi","ws","wss","wtai","wyciwyg","xcompute","xcon","xcon-userid","xfire","xftp","xmlrpc.beep","xmlrpc.beeps","xmpp","xrcp","xri","ymsgr","z39.50r","z39.50s","ztdnaid"]
|
|
1
|
+
["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","afp","afs","agtp","aim","amss","android","appdata","apt","ar","ari","ark","at","attachment","aw","barion","beshare","bitcoin","bitcoincash","bl","blob","bluetooth","bolo","brid","browserext","cabal","caip","calculator","callto","cap","cast","casts","chrome","chrome-extension","cid","coap","coaps","com-eventbrite-attendee","content","content-type","crid","cstr","cttps","cvs","dab","dat","data","dav","dhttp","diaspora","dict","did","dilithium3","dis","dlna-playcontainer","dlna-playsingle","dnp","dns","dntp","doi","donau","dpp","drm","dtmi","dtn","dvb","dvx","dweb","ed2k","eid","elsi","embedded","ens","esim","ethereum","example","ez","facetime","feed","feedready","fido","file","finger","first-run-pen-experience","fish","fm","ftp","fuchsia-pkg","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gtalk","h323","ham","hcap","hcp","hs20","http","https","hxxp","hxxps","hydrazone","hyper","i0","iax","ibi","ibi-","icap","icon","ilstring","im","imap","info","iotdisco","ipfs","ipn","ipns","ipp","ipps","irc","irc6","ircs","iris","iris.beep","iris.lwz","iris.xpc","iris.xpcs","isostore","itms","jabber","jar","jms","keyparc","lastfm","lbry","ldap","ldaps","leaptofrogans","lid","linkid","lorawan","lpa","lvlt","machineProvisioningProgressReporter","magnet","mailto","maps","market","matrix","mdoc","mdoc-openid4vp","message","microsoft.windows.camera","microsoft.windows.camera.multipicker","microsoft.windows.camera.picker","mid","mms","mongodb","moz","moz-extension","mqtt","mqtts","ms-access","ms-appinstaller","ms-browser-extension","ms-calculator","ms-drive-to","ms-enrollment","ms-excel","ms-eyecontrolspeech","ms-gamebarservices","ms-gamingoverlay","ms-getoffice","ms-help","ms-infopath","ms-inputapp","ms-launchremotedesktop","ms-lockscreencomponent-config","ms-media-stream-id","ms-meetnow","ms-mixedrealitycapture","ms-mobileplans","ms-newsandinterests","ms-officeapp","ms-people","ms-personacard","ms-powerpoint","ms-project","ms-publisher","ms-recall","ms-remotedesktop","ms-remotedesktop-launch","ms-restoretabcompanion","ms-screenclip","ms-screensketch","ms-search","ms-search-repair","ms-secondary-screen-controller","ms-secondary-screen-setup","ms-settings","ms-settings-airplanemode","ms-settings-bluetooth","ms-settings-camera","ms-settings-cellular","ms-settings-cloudstorage","ms-settings-connectabledevices","ms-settings-displays-topology","ms-settings-emailandaccounts","ms-settings-language","ms-settings-location","ms-settings-lock","ms-settings-nfctransactions","ms-settings-notifications","ms-settings-power","ms-settings-privacy","ms-settings-proximity","ms-settings-screenrotation","ms-settings-wifi","ms-settings-workplace","ms-spd","ms-stickers","ms-sttoverlay","ms-transit-to","ms-useractivityset","ms-uup","ms-virtualtouchpad","ms-visio","ms-walk-to","ms-whiteboard","ms-whiteboard-cmd","ms-widgetboard","ms-widgets","ms-word","msnim","msrp","msrps","mss","mt","mtqp","mtrust","mumble","mupdate","musik","mvn","mvrp","mvrps","news","nfs","ni","nih","nntp","nostr","notes","npamp","num","ocf","oid","onenote","onenote-cmd","opaquelocktoken","openid","openpgp4fpr","otpauth","palm","paparazzi","payto","pkcs11","platform","pop","pres","proxy","psyc","pttp","pwid","qb","query","quic-transport","redis","rediss","reload","res","resource","rmi","rsync","rtmfp","rtmp","rtsp","rtsps","rtspu","sarif","secondlife","secret-token","service","session","sftp","sgn","shc","shelter","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","soap.beep","soap.beeps","soldat","spacify","spiffe","spotify","ssb","ssh","sss","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","tttps","turn","turns","tv","udp","unreal","upn","urn","ust","ut2004","uuaid","uuid-in-package","v-event","vemmi","ventrilo","ves","view-source","vnc","vscode","vscode-insiders","vsls","w3","wasm","wasm-js","wcr","web3","webcal","wifi","ws","wss","wtai","wyciwyg","xcompute","xcon","xcon-userid","xfire","xftp","xmlrpc.beep","xmlrpc.beeps","xmpp","xrcp","xri","ymsgr","z39.50r","z39.50s","ztdnaid"]
|
package/src/mjs/dompurify.js
CHANGED
|
@@ -10,6 +10,6 @@ const { window } = new JSDOM('');
|
|
|
10
10
|
/**
|
|
11
11
|
* An instance of DOMPurify initialized with a JSDOM window context.
|
|
12
12
|
* Used to sanitize HTML/SVG strings.
|
|
13
|
-
* @type {import('dompurify')}
|
|
13
|
+
* @type {import('dompurify').DOMPurify}
|
|
14
14
|
*/
|
|
15
15
|
export const domPurify = DOMPurify(window);
|
package/src/mjs/regexp.js
CHANGED
|
@@ -80,9 +80,16 @@ export const REG_URL_ENC = /^%[\dA-F]{2}$/i;
|
|
|
80
80
|
|
|
81
81
|
/**
|
|
82
82
|
* Matches an ampersand-encoded character (&foo).
|
|
83
|
+
* @type {RegExp}
|
|
83
84
|
*/
|
|
84
85
|
export const REG_AMP_ENC = /&(?=[#a-z0-9])(?![a-z0-9]+=)/i;
|
|
85
86
|
|
|
87
|
+
/**
|
|
88
|
+
* Match one or more consecutive URL percent-encoded sequences (%20, %E3%81%82).
|
|
89
|
+
* @type {RegExp}
|
|
90
|
+
*/
|
|
91
|
+
export const REG_PCT_ENC = /(?:%[\dA-F]{2})+/gi;
|
|
92
|
+
|
|
86
93
|
/**
|
|
87
94
|
* Validates relative URLs and flags invalid patterns.
|
|
88
95
|
* @type {RegExp}
|
package/src/mjs/sanitizer.js
CHANGED
|
@@ -126,10 +126,8 @@ class URLSanitizer extends URISchemes {
|
|
|
126
126
|
if (!sanitizer || !ctx) {
|
|
127
127
|
return;
|
|
128
128
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
urlObj = new URL(evt.attrValue);
|
|
132
|
-
} catch {
|
|
129
|
+
const urlObj = URL.parse(evt.attrValue);
|
|
130
|
+
if (!urlObj) {
|
|
133
131
|
return;
|
|
134
132
|
}
|
|
135
133
|
if (urlObj.protocol === 'data:') {
|
|
@@ -357,10 +355,10 @@ class URLSanitizer extends URISchemes {
|
|
|
357
355
|
let relativeParsedPath = '';
|
|
358
356
|
// Handle Relative URLs
|
|
359
357
|
if (!isVerified && allowRelative && !REG_VERIFY_RELATIVE.test(url)) {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
358
|
+
const normUrl = url.normalize('NFKC');
|
|
359
|
+
const dummyUrl = URL.parse(url, 'http://dummy.local');
|
|
360
|
+
const dummyUrlNormalized = URL.parse(normUrl, 'http://dummy.local');
|
|
361
|
+
if (dummyUrl && dummyUrlNormalized) {
|
|
364
362
|
if (
|
|
365
363
|
dummyUrl.protocol === 'http:' &&
|
|
366
364
|
dummyUrl.hostname === 'dummy.local' &&
|
|
@@ -370,8 +368,8 @@ class URLSanitizer extends URISchemes {
|
|
|
370
368
|
isRelative = true;
|
|
371
369
|
relativeParsedPath = `${dummyUrl.pathname}${dummyUrl.search}${dummyUrl.hash}`;
|
|
372
370
|
}
|
|
373
|
-
}
|
|
374
|
-
logDebug(ctx.debug, 'Failed to parse relative URL.'
|
|
371
|
+
} else {
|
|
372
|
+
logDebug(ctx.debug, 'Failed to parse relative URL.');
|
|
375
373
|
}
|
|
376
374
|
}
|
|
377
375
|
if (!isVerified) {
|
|
@@ -388,7 +386,7 @@ class URLSanitizer extends URISchemes {
|
|
|
388
386
|
urlToSanitize: relativeParsedPath
|
|
389
387
|
};
|
|
390
388
|
}
|
|
391
|
-
const urlObj =
|
|
389
|
+
const urlObj = URL.parse(url);
|
|
392
390
|
const scheme = urlObj.protocol.replace(/:$/, '').normalize('NFKC');
|
|
393
391
|
const schemeParts = scheme.split('+');
|
|
394
392
|
return {
|
|
@@ -455,14 +453,18 @@ class URLSanitizer extends URISchemes {
|
|
|
455
453
|
const decodedData = parseURLEncodedNumCharRef(parsedData).trim();
|
|
456
454
|
const normalizedData = decodedData.normalize('NFKC');
|
|
457
455
|
const dummy = 'http://dummy.local';
|
|
458
|
-
const
|
|
456
|
+
const parsedUrl = URL.parse(normalizedData, dummy);
|
|
457
|
+
if (!parsedUrl) {
|
|
458
|
+
logDebug(ctx.debug, 'Failed to parse inner data URL protocol.');
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
const dataScheme = parsedUrl.protocol;
|
|
459
462
|
const dataSchemeParts = dataScheme.replace(/:$/, '').split('+');
|
|
460
463
|
if (dataSchemeParts.some(s => REG_SCRIPT_BLOB.test(s))) {
|
|
461
464
|
return null;
|
|
462
465
|
}
|
|
463
466
|
} catch (e) {
|
|
464
|
-
|
|
465
|
-
logDebug(ctx.debug, msg, e);
|
|
467
|
+
logDebug(ctx.debug, 'Failed to parse inner data URL protocol.', e);
|
|
466
468
|
return null;
|
|
467
469
|
}
|
|
468
470
|
if (!mediaType || REG_MIME_DOM.test(mediaType)) {
|
|
@@ -531,12 +533,11 @@ class URLSanitizer extends URISchemes {
|
|
|
531
533
|
!REG_TAG_QUOT.test(url) &&
|
|
532
534
|
!url.includes('data:')
|
|
533
535
|
) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
return href.replace(/%26/g, escapeURLEncodedHTMLChars);
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
logDebug(opt?.debug, msg, e);
|
|
536
|
+
const parsedUrl = URL.parse(url);
|
|
537
|
+
if (parsedUrl) {
|
|
538
|
+
return parsedUrl.href.replace(/%26/g, escapeURLEncodedHTMLChars);
|
|
539
|
+
} else {
|
|
540
|
+
logDebug(opt?.debug, `Failed to parse URL.`);
|
|
540
541
|
return null;
|
|
541
542
|
}
|
|
542
543
|
}
|
|
@@ -662,7 +663,9 @@ class URLSanitizer extends URISchemes {
|
|
|
662
663
|
* @returns {string[]} The updated array of registered schemes.
|
|
663
664
|
*/
|
|
664
665
|
add(scheme) {
|
|
665
|
-
if (
|
|
666
|
+
if (isString(scheme)) {
|
|
667
|
+
scheme = scheme.trim();
|
|
668
|
+
} else {
|
|
666
669
|
throw new TypeError(`Expected String but got ${getType(scheme)}.`);
|
|
667
670
|
}
|
|
668
671
|
const schemeParts = scheme.split('+');
|
|
@@ -742,11 +745,11 @@ export const sanitizeURL = async (
|
|
|
742
745
|
let res;
|
|
743
746
|
if (url && isString(url)) {
|
|
744
747
|
let scheme;
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
scheme = protocol.replace(/:$/, '');
|
|
748
|
-
}
|
|
749
|
-
logDebug(isDebug, `Invalid URL input format: ${url}
|
|
748
|
+
const parsedUrl = URL.parse(url);
|
|
749
|
+
if (parsedUrl) {
|
|
750
|
+
scheme = parsedUrl.protocol.replace(/:$/, '');
|
|
751
|
+
} else {
|
|
752
|
+
logDebug(isDebug, `Invalid URL input format: ${url}`);
|
|
750
753
|
}
|
|
751
754
|
if (scheme === 'blob') {
|
|
752
755
|
const { allow, deny, only } = opt;
|
|
@@ -822,12 +825,11 @@ export const sanitizeURLSync = (
|
|
|
822
825
|
let res;
|
|
823
826
|
if (url && isString(url)) {
|
|
824
827
|
let scheme;
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
scheme = protocol.replace(/:$/, '');
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
logDebug(isDebug, msg, e);
|
|
828
|
+
const parsedUrl = URL.parse(url);
|
|
829
|
+
if (parsedUrl) {
|
|
830
|
+
scheme = parsedUrl.protocol.replace(/:$/, '');
|
|
831
|
+
} else {
|
|
832
|
+
logDebug(isDebug, `Invalid URL input format: ${url}`);
|
|
831
833
|
}
|
|
832
834
|
if (scheme === 'blob') {
|
|
833
835
|
if (opt?.revokeObjectURL) {
|
|
@@ -852,11 +854,11 @@ export const inspectURL = async url => {
|
|
|
852
854
|
};
|
|
853
855
|
|
|
854
856
|
/**
|
|
855
|
-
* @deprecated Use inspectURL instead.
|
|
857
|
+
* @deprecated Use {@link inspectURL} instead.
|
|
856
858
|
* @param {string} url - The URL string to inspect.
|
|
857
859
|
* @returns {Promise<InspectedURLResult>} A promise resolving to the inspected URL result.
|
|
858
860
|
*/
|
|
859
|
-
export const parseURL = url => inspectURL(url);
|
|
861
|
+
export const parseURL = async url => inspectURL(url);
|
|
860
862
|
|
|
861
863
|
/**
|
|
862
864
|
* Inspects, parses, and sanitizes the given URL synchronously.
|
|
@@ -866,7 +868,7 @@ export const parseURL = url => inspectURL(url);
|
|
|
866
868
|
export const inspectURLSync = url => urlSanitizer.inspect(url);
|
|
867
869
|
|
|
868
870
|
/**
|
|
869
|
-
* @deprecated Use inspectURLSync instead.
|
|
871
|
+
* @deprecated Use {@link inspectURLSync} instead.
|
|
870
872
|
* @param {string} url - The URL string to inspect.
|
|
871
873
|
* @returns {InspectedURLResult} The inspected URL result.
|
|
872
874
|
*/
|
package/src/mjs/uri-util.js
CHANGED
|
@@ -11,6 +11,7 @@ import { CHUNK_SIZE, DECI, HEX, MAX_BLOB_SIZE, MAX_NEST } from './constant.js';
|
|
|
11
11
|
import {
|
|
12
12
|
REG_HASH,
|
|
13
13
|
REG_NUM_REF,
|
|
14
|
+
REG_PCT_ENC,
|
|
14
15
|
REG_QUERY,
|
|
15
16
|
REG_SCHEME_EXT,
|
|
16
17
|
REG_SCRIPT,
|
|
@@ -88,18 +89,18 @@ export class URISchemes {
|
|
|
88
89
|
if (!isString(uri)) {
|
|
89
90
|
return false;
|
|
90
91
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
} catch {
|
|
92
|
+
const normalizedUri = uri.normalize('NFKC');
|
|
93
|
+
const parsedUrl = URL.parse(normalizedUri);
|
|
94
|
+
if (!parsedUrl) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
const scheme = parsedUrl.protocol.replace(/:$/, '');
|
|
98
|
+
const parts = scheme.split('+');
|
|
99
|
+
const isScript = parts.some(s => REG_SCRIPT.test(s));
|
|
100
|
+
if (isScript) {
|
|
101
101
|
return false;
|
|
102
102
|
}
|
|
103
|
+
return REG_SCHEME_EXT.test(scheme) || parts.every(s => schemes.has(s));
|
|
103
104
|
}
|
|
104
105
|
}
|
|
105
106
|
|
|
@@ -113,11 +114,11 @@ export const getURLEncodedString = str => {
|
|
|
113
114
|
throw new TypeError(`Expected String but got ${getType(str)}.`);
|
|
114
115
|
}
|
|
115
116
|
const bytes = encoder.encode(str);
|
|
116
|
-
|
|
117
|
+
let encoded = '';
|
|
117
118
|
for (let i = 0; i < bytes.length; i++) {
|
|
118
|
-
encoded
|
|
119
|
+
encoded += HEX_TABLE[bytes[i]];
|
|
119
120
|
}
|
|
120
|
-
return encoded
|
|
121
|
+
return encoded;
|
|
121
122
|
};
|
|
122
123
|
|
|
123
124
|
/**
|
|
@@ -226,7 +227,7 @@ export const parseURLEncodedNumCharRef = (str, nest = 0) => {
|
|
|
226
227
|
res = decodeURIComponent(str);
|
|
227
228
|
} catch {
|
|
228
229
|
// Fallback: decode only valid multi-byte %XX sequences.
|
|
229
|
-
res = str.replace(
|
|
230
|
+
res = str.replace(REG_PCT_ENC, match => {
|
|
230
231
|
try {
|
|
231
232
|
return decodeURIComponent(match);
|
|
232
233
|
} catch {
|
package/types/index.d.ts
CHANGED
|
@@ -1 +1,8 @@
|
|
|
1
|
-
|
|
1
|
+
/*!
|
|
2
|
+
* URL Sanitizer
|
|
3
|
+
*
|
|
4
|
+
* @license MIT
|
|
5
|
+
* @copyright asamuzaK (Kazz)
|
|
6
|
+
* @see {@link https://github.com/asamuzaK/urlSanitizer/blob/main/LICENSE}
|
|
7
|
+
*/
|
|
8
|
+
export { default, inspectURL, inspectURLSync, isURI, isURISync, parseURL, parseURLSync, sanitizeURL, sanitizeURLSync } from './mjs/sanitizer.js';
|
package/types/mjs/common.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
2
|
-
export
|
|
1
|
+
export declare const getType: (o: unknown) => string;
|
|
2
|
+
export declare const isString: (o: unknown) => boolean;
|
package/types/mjs/constant.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
export const CHUNK_SIZE: number;
|
|
2
|
-
export const DECI: number;
|
|
3
|
-
export const HEX: number;
|
|
4
|
-
export const MAX_BLOB_SIZE: number;
|
|
5
|
-
export const MAX_NEST: number;
|
|
6
|
-
export const TYPE_FROM: number;
|
|
7
|
-
export const TYPE_TO: number;
|
|
1
|
+
export declare const CHUNK_SIZE: number;
|
|
2
|
+
export declare const DECI: number;
|
|
3
|
+
export declare const HEX: number;
|
|
4
|
+
export declare const MAX_BLOB_SIZE: number;
|
|
5
|
+
export declare const MAX_NEST: number;
|
|
6
|
+
export declare const TYPE_FROM: number;
|
|
7
|
+
export declare const TYPE_TO: number;
|
package/types/mjs/dompurify.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const domPurify:
|
|
1
|
+
export declare const domPurify: import('dompurify').DOMPurify;
|
package/types/mjs/regexp.d.ts
CHANGED
|
@@ -1,14 +1,15 @@
|
|
|
1
|
-
export const REG_CHARSET: RegExp;
|
|
2
|
-
export const REG_HASH: RegExp;
|
|
3
|
-
export const REG_MIME_DOM: RegExp;
|
|
4
|
-
export const REG_MIME_TEXT: RegExp;
|
|
5
|
-
export const REG_NUM_REF: RegExp;
|
|
6
|
-
export const REG_QUERY: RegExp;
|
|
7
|
-
export const REG_SCHEME: RegExp;
|
|
8
|
-
export const REG_SCHEME_EXT: RegExp;
|
|
9
|
-
export const REG_SCRIPT: RegExp;
|
|
10
|
-
export const REG_SCRIPT_BLOB: RegExp;
|
|
11
|
-
export const REG_TAG_QUOT: RegExp;
|
|
12
|
-
export const REG_URL_ENC: RegExp;
|
|
13
|
-
export const REG_AMP_ENC: RegExp;
|
|
14
|
-
export const
|
|
1
|
+
export declare const REG_CHARSET: RegExp;
|
|
2
|
+
export declare const REG_HASH: RegExp;
|
|
3
|
+
export declare const REG_MIME_DOM: RegExp;
|
|
4
|
+
export declare const REG_MIME_TEXT: RegExp;
|
|
5
|
+
export declare const REG_NUM_REF: RegExp;
|
|
6
|
+
export declare const REG_QUERY: RegExp;
|
|
7
|
+
export declare const REG_SCHEME: RegExp;
|
|
8
|
+
export declare const REG_SCHEME_EXT: RegExp;
|
|
9
|
+
export declare const REG_SCRIPT: RegExp;
|
|
10
|
+
export declare const REG_SCRIPT_BLOB: RegExp;
|
|
11
|
+
export declare const REG_TAG_QUOT: RegExp;
|
|
12
|
+
export declare const REG_URL_ENC: RegExp;
|
|
13
|
+
export declare const REG_AMP_ENC: RegExp;
|
|
14
|
+
export declare const REG_PCT_ENC: RegExp;
|
|
15
|
+
export declare const REG_VERIFY_RELATIVE: RegExp;
|
package/types/mjs/sanitizer.d.ts
CHANGED
|
@@ -1,30 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export function sanitizeURL(url: string, opt?: {
|
|
3
|
-
allow?: string[] | undefined;
|
|
4
|
-
deny?: string[] | undefined;
|
|
5
|
-
only?: string[] | undefined;
|
|
6
|
-
allowRelative?: boolean | undefined;
|
|
7
|
-
debug?: boolean | undefined;
|
|
8
|
-
revokeObjectURL?: boolean | undefined;
|
|
9
|
-
maxBlobSize?: number | undefined;
|
|
10
|
-
maxLength?: number | undefined;
|
|
11
|
-
}): Promise<string | null>;
|
|
12
|
-
export function sanitizeURLSync(url: string, opt?: {
|
|
13
|
-
allow?: string[] | undefined;
|
|
14
|
-
deny?: string[] | undefined;
|
|
15
|
-
only?: string[] | undefined;
|
|
16
|
-
allowRelative?: boolean | undefined;
|
|
17
|
-
debug?: boolean | undefined;
|
|
18
|
-
revokeObjectURL?: boolean | undefined;
|
|
19
|
-
maxLength?: number | undefined;
|
|
20
|
-
}): string | null;
|
|
21
|
-
export function inspectURL(url: string): Promise<InspectedURLResult>;
|
|
22
|
-
export function parseURL(url: string): Promise<InspectedURLResult>;
|
|
23
|
-
export function inspectURLSync(url: string): InspectedURLResult;
|
|
24
|
-
export function parseURLSync(url: string): InspectedURLResult;
|
|
25
|
-
export function isURI(uri: string): Promise<boolean>;
|
|
26
|
-
export function isURISync(uri: string): boolean;
|
|
27
|
-
export default urlSanitizer;
|
|
1
|
+
import { URISchemes } from './uri-util.js';
|
|
28
2
|
export type InspectedDataURL = {
|
|
29
3
|
mime: string;
|
|
30
4
|
base64: boolean;
|
|
@@ -33,38 +7,75 @@ export type InspectedDataURL = {
|
|
|
33
7
|
export type InspectedURLResult = {
|
|
34
8
|
input: string;
|
|
35
9
|
valid: boolean;
|
|
36
|
-
reason?: string
|
|
37
|
-
data?: InspectedDataURL | null
|
|
38
|
-
href?: string
|
|
39
|
-
origin?: string
|
|
40
|
-
protocol?: string
|
|
41
|
-
username?: string
|
|
42
|
-
password?: string
|
|
43
|
-
host?: string
|
|
44
|
-
hostname?: string
|
|
45
|
-
port?: string
|
|
46
|
-
pathname?: string
|
|
47
|
-
search?: string
|
|
48
|
-
hash?: string
|
|
10
|
+
reason?: string;
|
|
11
|
+
data?: InspectedDataURL | null;
|
|
12
|
+
href?: string;
|
|
13
|
+
origin?: string;
|
|
14
|
+
protocol?: string;
|
|
15
|
+
username?: string;
|
|
16
|
+
password?: string;
|
|
17
|
+
host?: string;
|
|
18
|
+
hostname?: string;
|
|
19
|
+
port?: string;
|
|
20
|
+
pathname?: string;
|
|
21
|
+
search?: string;
|
|
22
|
+
hash?: string;
|
|
49
23
|
};
|
|
50
|
-
export
|
|
51
|
-
|
|
52
|
-
|
|
24
|
+
export declare const logDebug: (isDebug: boolean, message: string, error?: Error) => void;
|
|
25
|
+
declare class URLSanitizer extends URISchemes {
|
|
26
|
+
#private;
|
|
53
27
|
private static #uponSanitizeAttribute;
|
|
28
|
+
constructor();
|
|
29
|
+
private #registerScheme;
|
|
30
|
+
private #purify;
|
|
31
|
+
private #process;
|
|
32
|
+
private #resolveSchemeRules;
|
|
33
|
+
private #parseAndVerifyURL;
|
|
34
|
+
private #isSchemeAllowed;
|
|
35
|
+
private #sanitizeDataURL;
|
|
36
|
+
private #sanitizeStandardURL;
|
|
54
37
|
sanitize(url: string, opt?: {
|
|
55
|
-
allow?: string[]
|
|
56
|
-
deny?: string[]
|
|
57
|
-
only?: string[]
|
|
58
|
-
allowRelative?: boolean
|
|
59
|
-
debug?: boolean
|
|
60
|
-
maxBlobSize?: number
|
|
61
|
-
maxLength?: number
|
|
38
|
+
allow?: string[];
|
|
39
|
+
deny?: string[];
|
|
40
|
+
only?: string[];
|
|
41
|
+
allowRelative?: boolean;
|
|
42
|
+
debug?: boolean;
|
|
43
|
+
maxBlobSize?: number;
|
|
44
|
+
maxLength?: number;
|
|
62
45
|
}): string | null;
|
|
63
46
|
inspect(url: string, opt?: object): InspectedURLResult;
|
|
47
|
+
get(): string[];
|
|
48
|
+
has(scheme: string): boolean;
|
|
64
49
|
add(scheme: string): string[];
|
|
65
50
|
remove(scheme: string): boolean;
|
|
66
51
|
reset(): void;
|
|
67
|
-
|
|
52
|
+
verify(uri: string, schemes?: Set<string>): boolean;
|
|
68
53
|
}
|
|
54
|
+
export { URLSanitizer };
|
|
69
55
|
declare const urlSanitizer: URLSanitizer;
|
|
70
|
-
|
|
56
|
+
export declare const sanitizeURL: (url: string, opt?: {
|
|
57
|
+
allow?: string[];
|
|
58
|
+
deny?: string[];
|
|
59
|
+
only?: string[];
|
|
60
|
+
allowRelative?: boolean;
|
|
61
|
+
debug?: boolean;
|
|
62
|
+
revokeObjectURL?: boolean;
|
|
63
|
+
maxBlobSize?: number;
|
|
64
|
+
maxLength?: number;
|
|
65
|
+
}) => Promise<string | null>;
|
|
66
|
+
export declare const sanitizeURLSync: (url: string, opt?: {
|
|
67
|
+
allow?: string[];
|
|
68
|
+
deny?: string[];
|
|
69
|
+
only?: string[];
|
|
70
|
+
allowRelative?: boolean;
|
|
71
|
+
debug?: boolean;
|
|
72
|
+
revokeObjectURL?: boolean;
|
|
73
|
+
maxLength?: number;
|
|
74
|
+
}) => string | null;
|
|
75
|
+
export declare const inspectURL: (url: string) => Promise<InspectedURLResult>;
|
|
76
|
+
export declare const parseURL: (url: string) => Promise<InspectedURLResult>;
|
|
77
|
+
export declare const inspectURLSync: (url: string) => InspectedURLResult;
|
|
78
|
+
export declare const parseURLSync: (url: string) => InspectedURLResult;
|
|
79
|
+
export declare const isURI: (uri: string) => Promise<boolean>;
|
|
80
|
+
export declare const isURISync: (uri: string) => boolean;
|
|
81
|
+
export default urlSanitizer;
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export const TEXT_CHAR_CODES: Set<number>;
|
|
2
|
-
export const WINDOWS1252_TO_UNICODE: Map<number, number>;
|
|
3
|
-
export const CTRL_CHAR_CODES: Map<number, string>;
|
|
1
|
+
export declare const TEXT_CHAR_CODES: Set<number>;
|
|
2
|
+
export declare const WINDOWS1252_TO_UNICODE: Map<number, number>;
|
|
3
|
+
export declare const CTRL_CHAR_CODES: Map<number, string>;
|
package/types/mjs/uri-util.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
export class URISchemes {
|
|
1
|
+
export declare class URISchemes {
|
|
2
|
+
#private;
|
|
2
3
|
get(): string[];
|
|
3
4
|
has(scheme: string): boolean;
|
|
4
5
|
verify(uri: string, schemes?: Set<string>): boolean;
|
|
5
|
-
#private;
|
|
6
6
|
}
|
|
7
|
-
export
|
|
8
|
-
export
|
|
9
|
-
export
|
|
10
|
-
export
|
|
11
|
-
export
|
|
12
|
-
export
|
|
13
|
-
export
|
|
7
|
+
export declare const getURLEncodedString: (str: string) => string;
|
|
8
|
+
export declare const escapeURLEncodedHTMLChars: (ch: string) => string;
|
|
9
|
+
export declare const parseBase64: (data: string) => string;
|
|
10
|
+
export declare const replaceNumCharRef: (match: string, value: string) => string;
|
|
11
|
+
export declare const trimTrailingEmptyQueryAndHash: (url: string) => string;
|
|
12
|
+
export declare const parseURLEncodedNumCharRef: (str: string, nest?: number) => string;
|
|
13
|
+
export declare const convertBlobToDataURL: (blob: Blob, maxSize?: number) => Promise<string | null>;
|