url-sanitizer 5.3.7 → 6.1.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 +12 -15
- package/dist/url-sanitizer-wo-dompurify.min.js +1 -1
- package/dist/url-sanitizer-wo-dompurify.min.js.map +4 -4
- package/dist/url-sanitizer.min.js +4 -3
- package/dist/url-sanitizer.min.js.map +4 -4
- package/package.json +9 -11
- package/src/index.js +3 -10
- package/src/main/dompurify.js +11 -0
- package/src/{mjs → main}/filter.js +86 -34
- package/src/{mjs → main}/sanitize.js +13 -10
- package/types/index.d.ts +1 -2
- package/types/main/dompurify.d.ts +1 -0
- package/types/{mjs → main}/filter.d.ts +6 -2
- package/src/mjs/dompurify.js +0 -15
- package/types/mjs/dompurify.d.ts +0 -1
- /package/src/{mjs → main}/common.js +0 -0
- /package/src/{mjs → main}/constant.js +0 -0
- /package/src/{mjs → main}/regexp.js +0 -0
- /package/src/{mjs → main}/scheme.js +0 -0
- /package/src/{mjs → main}/text-chars.js +0 -0
- /package/src/{mjs → main}/utility.js +0 -0
- /package/types/{mjs → main}/common.d.ts +0 -0
- /package/types/{mjs → main}/constant.d.ts +0 -0
- /package/types/{mjs → main}/regexp.d.ts +0 -0
- /package/types/{mjs → main}/sanitize.d.ts +0 -0
- /package/types/{mjs → main}/scheme.d.ts +0 -0
- /package/types/{mjs → main}/text-chars.d.ts +0 -0
- /package/types/{mjs → main}/utility.d.ts +0 -0
package/README.md
CHANGED
|
@@ -96,7 +96,6 @@ Sanitizes the given URL asynchronously.
|
|
|
96
96
|
* You can restrict the allowed blob size using the `opt.maxBlobSize` option.
|
|
97
97
|
Default is `16_777_216` (16MB).
|
|
98
98
|
While 16MB may be suitable for client-side processing, it is highly recommended to reduce this limit to `1_048_576` (1MB) or `2_097_152` (2MB) in server-side applications.
|
|
99
|
-
Parsing large HTML/SVG contents inside Data/Blob URLs via JSDOM can consume massive amounts of V8 heap memory.
|
|
100
99
|
|
|
101
100
|
#### Parameters
|
|
102
101
|
|
|
@@ -210,10 +209,10 @@ const res7_2 = await sanitizeURL('https://example.com/"onmouseover="alert(1)"',
|
|
|
210
209
|
|
|
211
210
|
Synchronous version of `sanitizeURL()`.
|
|
212
211
|
|
|
213
|
-
* `
|
|
212
|
+
* `file` schemes must be explicitly allowed.
|
|
214
213
|
* **File URLs:** Allowing the `file` scheme can be extremely dangerous in web applications. [See above](#about-file-scheme)
|
|
215
|
-
* **Blob URLs:** `blob`
|
|
216
|
-
Use the async version for `blob
|
|
214
|
+
* **Blob URLs and Data URLs:** `blob` and `data` schemes are **not supported** and will return `null`.
|
|
215
|
+
Use the async version for `blob` and `data` URLs.
|
|
217
216
|
However, to prevent memory leaks, it is highly recommended to set the `opt.revokeObjectURL` option to `true` so that the unsupported blob URL is properly revoked before returning `null`.
|
|
218
217
|
|
|
219
218
|
### inspectURL(url, opt)
|
|
@@ -343,8 +342,6 @@ Specifically, it verifies that the input has a correct URI syntax, is not a deni
|
|
|
343
342
|
* The scheme is registered in the default list (e.g., IANA registered schemes, `https`, `mailto`).
|
|
344
343
|
* The scheme has a custom prefix like `web+` or `ext+` (which are always allowed for web applications).
|
|
345
344
|
|
|
346
|
-
**Note:** `isURISync()` is deprecated. Please use `isValidURI()` instead.
|
|
347
|
-
|
|
348
345
|
#### Parameters
|
|
349
346
|
|
|
350
347
|
* uri **string** URI input.
|
|
@@ -445,22 +442,22 @@ Execution times were measured using [mitata](https://github.com/evanwashere/mita
|
|
|
445
442
|
|
|
446
443
|
| URL Type | `url-sanitizer` | [@braintree/sanitize-url](https://www.npmjs.com/package/@braintree/sanitize-url) | [strict-url-sanitise](https://www.npmjs.com/package/strict-url-sanitise) |
|
|
447
444
|
| :--- | :--- | :--- | :--- |
|
|
448
|
-
| **Normal HTTPS URL** |
|
|
449
|
-
| **HTTPS URL
|
|
450
|
-
| **XSS Scheme URL** |
|
|
451
|
-
| **
|
|
452
|
-
| **
|
|
453
|
-
| **Invalid URL** |
|
|
445
|
+
| **Normal HTTPS URL** | 2.06 µs/iter | 4.23 µs/iter | 4.30 µs/iter |
|
|
446
|
+
| **HTTPS XSS URL** | 3.08 µs/iter<br>returns sanitized URL | 5.13 µs/iter<br>returns encoded URL (not fully sanitized against XSS) | 7.06 µs/iter<br>returns encoded URL (not fully sanitized against XSS) |
|
|
447
|
+
| **XSS Scheme URL** | 2.14 µs/iter<br>returns `null` | 1.69 µs/iter<br>returns `about:blank` | 7.67 µs/iter<br>throws `Error` |
|
|
448
|
+
| **Data URL** | 197.24 µs/iter<br>returns sanitized data URL | 2.45 µs/iter<br>returns `about:blank` | 7.62 µs/iter<br>throws `Error` |
|
|
449
|
+
| **Blob URL** | 319.98 µs/iter<br>returns sanitized data URL | 2.31 µs/iter<br>returns blob URL as-is | 7.59 µs/iter<br>throws `Error` |
|
|
450
|
+
| **Invalid URL** | 0.71 µs/iter<br>returns `null` | 1.36 µs/iter<br>returns invalid URL as-is | 16.70 µs/iter<br>throws `Error` |
|
|
454
451
|
|
|
455
452
|
### Characteristics & Trade-offs
|
|
456
453
|
|
|
457
454
|
* **Optimized for Standard Routing & XSS Rejection**
|
|
458
|
-
* For **normal HTTP(S) URLs** and URLs containing common XSS patterns, `url-sanitizer` achieves low latency by leveraging
|
|
455
|
+
* For **normal HTTP(S) URLs** and URLs containing common XSS patterns, `url-sanitizer` achieves low latency by leveraging native `URL` parsing and applying optimized early-return paths when strict inspection is unnecessary.
|
|
459
456
|
* **Exceptional Invalid URL Handling (Fail-Fast)**
|
|
460
|
-
* For **invalid URLs** (such as malformed schemes or unparseable formats), `url-sanitizer`
|
|
457
|
+
* For **invalid URLs** (such as malformed schemes or unparseable formats), `url-sanitizer` achieves sub-microsecond performance (~719.54 ns) via a fail-fast architecture based on native parsing, avoiding unnecessary sanitization steps after validation failure.
|
|
461
458
|
* **Deep Inspection for High-Risk Payloads**
|
|
462
459
|
* For **Data URLs** and **Blob URLs**, `url-sanitizer` prioritizes security over raw execution speed. The following inspection pipeline is applied:
|
|
463
|
-
1. If Blob URL is given, convert it to a Data URL.
|
|
460
|
+
1. If a Blob URL is given, convert it to a Data URL.
|
|
464
461
|
2. Parses and normalizes the Data URL structure.
|
|
465
462
|
3. Decodes Base64 payloads when present.
|
|
466
463
|
4. Decodes URL-encoded content and numeric character references.
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var M="http://dummy.local",w={allow:Object.freeze([]),deny:Object.freeze([]),only:null,allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536,schemes:new Set};var y=n=>Object.prototype.toString.call(n).slice(8,-1),p=n=>typeof n=="string"||n instanceof String,h=(n,e)=>{console.warn(`[URLSanitizer Debug] ${n}`,e)};var ue=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),T=[],S=new Proxy({},{get(n,e,t){let s=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(s){if(T.length){for(let{prop:o,args:a}of T)typeof s[o]=="function"&&s[o](...a);T.length=0}let r=s[e];return typeof r=="function"?r.bind(s):r}return(...r)=>{if(ue.includes(e))return T.push({prop:e,args:r}),t;throw new Error("DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).")}}});var H=/&/gi,j=/&(?=[#a-z0-9])(?![a-z0-9]+=)/i;var F=/#$/,X=/^(?:application\/(?:[\w.-]+\+)?xml|image\/svg\+xml|text\/(?:html|xml))(?:\s*;\s*|$)/i;var V=/\P{ASCII}/u,Y=/&#(x0*[\dA-F]{1,2}|0*\d{1,3});?/gi,q=/(?:%[\dA-F]{2})+/gi,Q=/^((?:[^#%]|%(?!23))*)\?$/,A=/^[a-z][\da-z+.-]*$/,W=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,R=/^(?:java|vb)script$/i,_=/^(?:(?:java|vb)script|blob)$/i,k=/["'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i,Z=/^%[\dA-F]{2}$/i,K=/^(?:\/{2,}|\\|[a-z][a-z\d+\-.]*:[^/])/i;var J=[9,10,13,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255];var P=new Set(J),B=new Map([[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]),ge=()=>{let n=new Map;for(let e=0;e<256;e++)!P.has(e)&&!B.has(e)&&n.set(e,`\\x${e.toString(16).padStart(2,"0").toUpperCase()}`);return n},G=ge();var[E,Re,ye,Ee,xe,we]=["&","#","<",">",'"',"'"].map(n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`),_e={[E]:`${E}amp;`,[ye]:`${E}lt;`,[Ee]:`${E}gt;`,[xe]:`${E}quot;`,[we]:`${E}${Re}39;`},Xe=Array.from({length:256},(n,e)=>`%${e.toString(16).padStart(2,"0").toUpperCase()}`),ne=globalThis.process?.versions?.node!==void 0,oe=`[${[...G.values()].join("")}]`,Ue=new RegExp(oe),Le=new RegExp(oe,"g"),Te=new Uint8Array(65536),Ve=new TextEncoder,Se=new TextDecoder("utf-8",{fatal:!0}),f=(n,e=!1)=>{if(!p(n))return null;let t=V.test(n)?n.normalize("NFKC"):n;return e?t.trim().toLowerCase().replace(/:$/,""):t},d=(n,e,t=!1)=>{if(!p(n)||n==="")return null;let s=n,r=e&&p(e)?e:"";if(t&&(s=f(s),r&&(r=f(r))),typeof URL.parse=="function")return r?URL.parse(s,r):URL.parse(s);try{return r?new URL(s,r):new URL(s)}catch{return null}},ae=n=>p(n)?n.replace(/:$/,"").split("+"):[];var ie=n=>{if(!p(n))return n;let e=Z.test(n)?n.toUpperCase():n;return _e[e]??n},u=n=>{let e=p(n)?n:String(n);return e.length>64?`${e.slice(0,64)}...`:e},ce=n=>p(n)?n.replace(F,"").replace(Q,"$1"):n,Ae=(n,e)=>{let t=/^x/i.test(e)?Number.parseInt(e.slice(1),16):Number.parseInt(e,10);if(!Number.isInteger(t)||t<0||t>=256)return n;if(G.has(t))return"";if(P.has(t))return String.fromCharCode(t);let s=B.get(t);return String.fromCodePoint(s)},le=(n,e=0)=>{if(!p(n))throw new TypeError(`Expected String but got ${y(n)}.`);if(!Number.isInteger(e))throw new TypeError(`Expected Number but got ${y(e)}.`);let t;try{t=decodeURIComponent(n)}catch{t=n.replace(q,r=>{try{return decodeURIComponent(r)}catch{return r}})}t=t.replace(Le,"");let s=0;for(;s+e<16;s++){let r=t;if(t=t.replace(H,"&").replace(Y,Ae),t===r)break}if(s+e>=16&&/&#/.test(t))throw new Error("Character references nested too deeply.");return t},O=(n,e="",t="")=>{if(!p(n))return{mediaType:"",mediaTypes:[],data:"",isBase64:!1};let s=n.indexOf(",");if(s===-1)return{mediaType:n,mediaTypes:n.split(";"),data:"",isBase64:!1};let r=n.substring(0,s),o=n.substring(s+1),a=e===""&&t===""?o:o+e+t,c=r.split(";"),i=c.at(-1),l=i!==void 0&&i.length===6&&i.toLowerCase()==="base64";return{mediaType:r,mediaTypes:c,data:a,isBase64:l}},pe=n=>{if(!p(n))throw new TypeError(`Expected String but got ${y(n)}.`);let e,t=n;try{e=atob(n)}catch{t=n.replace(/\s/g,"");try{e=atob(t)}catch{throw new Error(`Invalid base64 data: ${u(n)}`)}}let s;if(ne&&globalThis.Buffer)s=globalThis.Buffer.from(e,"latin1");else{let r=e.length;r>65536?s=new Uint8Array(r):s=Te.subarray(0,r);for(let o=0;o<r;o++)s[o]=e.charCodeAt(o)}try{let r=Se.decode(s);return Ue.test(r)?t:r}catch{return t}},z=n=>{if(ne&&globalThis.Buffer)return globalThis.Buffer.from(n).toString("base64");let e=new Uint8Array(n);if(typeof e.toBase64=="function")return e.toBase64();let t=[];for(let s=0;s<e.length;s+=8192)t.push(String.fromCharCode(...e.subarray(s,s+8192)));return btoa(t.join(""))},ve=async(n,e,t)=>{let s=n.body.getReader(),r=[],o="Operation aborted";if(t?.aborted)throw await s.cancel(o),s.releaseLock(),t.reason;let a=()=>{s.cancel(o).catch(()=>{})};t?.addEventListener("abort",a,{once:!0});try{let c=0;for(;c<=e;){if(t?.aborted)throw await s.cancel(o),t.reason;let{done:m,value:b}=await s.read();if(t?.aborted)throw t.reason;if(m)break;if(c+=b.byteLength,c>e)throw await s.cancel("Size limit exceeded"),new DOMException(`Payload (${c} bytes) exceeds max (${e} bytes).`,"NotReadableError");r.push(b)}let i=new Uint8Array(c),l=0;for(let m of r)i.set(m,l),l+=m.byteLength;return i.buffer}finally{t?.removeEventListener("abort",a),s.releaseLock()}},D=async(n,e,t)=>{let s=16777216;Number.isInteger(e)&&e>0&&(s=e);let r=await fetch(n,{signal:t});if(!r.ok){let l=`Failed to fetch ${u(n)}`;throw Number.isInteger(r.status)&&(r.statusText?l+=`: ${r.status} ${r.statusText}`:l+=`: ${r.status}`),new Error(l)}let o=r.headers.get("content-length");if(o){let i=Number.parseInt(o,10);if(Number.isInteger(i)&&i>s)throw new DOMException(`Payload (${i} bytes) exceeds max (${s} bytes).`,"NotReadableError")}let a=r.headers.get("content-type")||"",c;if(r.body)c=await ve(r,s,t);else{let i=await r.blob();if(i.size>s)throw new DOMException(`Payload (${i.size} bytes) exceeds max (${s} bytes).`,"NotReadableError");c=await i.arrayBuffer()}return{buffer:c,mimeType:a}};var L=class{constructor(e,t=w){this.domPurify=e,this.nest=0,this.recurse=new Set,this.restrictScheme=!1,this.schemeMap=new Map([["blob",!1],["data",!1],["file",!1],["javascript",!1],["vbscript",!1]]),this.#e(t)}#e(e){let{allowRelative:t,debug:s,allow:r,deny:o,only:a,schemes:c}=e;if(this.allowRelative=!!t,this.debug=!!s,this.schemes=c,Array.isArray(a)){this.restrictScheme=!0;for(let i of a)this.#t(i)}else{if(Array.isArray(r)&&r.length)for(let i of r)this.#t(i);if(Array.isArray(o)&&o.length)for(let i of o){let l=f(i,!0);l&&this.schemeMap.set(l,!1)}}}#t(e){let t=f(e,!0);return!t||_.test(t)||!A.test(t)||t.includes("+")&&t.split("+").some(r=>R.test(r))?!1:(this.schemeMap.set(t,!0),!0)}enter(e){return this.recurse.has(e)?(this.debug&&h(`Circular Data URL detected and skipped: ${u(e)}`),!1):(this.nest++,this.recurse.add(e),!0)}leave(e){this.nest--,this.recurse.delete(e)}},N=class{#e;constructor(e=w){this.#e=e}#t(e,t){if(t.nest>16)throw new Error("Data URLs nested too deeply.");let s=d(e,null,!0);if(!s){if(t.allowRelative&&!K.test(e)){let o=d(e,M);if(o){let{protocol:a,hostname:c,pathname:i,search:l,hash:m}=o;if(a==="http:"&&c==="dummy.local")return this.#n(`${i}${l}${m}`)}}return null}let r=f(s.protocol,!0);if(t.restrictScheme&&!t.schemeMap.has(r))return null;if(r.includes("+")){let o=r.split("+");if(t.schemeMap.get(r)===!1||o.some(a=>t.schemeMap.get(a)===!1))return null}else if(t.schemeMap.has(r)){if(t.schemeMap.get(r)===!1)return null}else if(!t.schemes.has(r))return null;return r==="data"?this.#s(s,t):this.#n(s.href)}#s(e,t){let{mediaType:s,mediaTypes:r,data:o,isBase64:a}=O(e.pathname,e.search,e.hash),c=o;if(a)try{c=pe(o)}catch(i){return t.debug&&h("Failed to parse base64 data.",i),null}try{let i=le(c).trim(),l=d(i,M);if(!l||ae(l.protocol).some(b=>_.test(b)))return null}catch(i){return t.debug&&h("Failed to parse inner data URL protocol.",i),null}return(!s||X.test(s))&&(c=this.#o(c,t)),c?(a&&c!==o&&r.pop(),`data:${r.join(";")},${c}`):null}#o(e,t){let s="";try{s=decodeURIComponent(e)}catch{s=e}let r=(a,c)=>this.#r(a,c,t),o="";try{t.domPurify.addHook("uponSanitizeAttribute",r),o=t.domPurify.sanitize(s)}finally{t.domPurify.removeHook("uponSanitizeAttribute",r)}if(!o)return null;try{return encodeURI(ce(o))}catch{return null}}#r(e,t,s){if(!t.attrValue||!/^\s*data:/i.test(t.attrValue))return;let r=d(t.attrValue,null,!0);if(!r||r.protocol!=="data:")return;let o=t.attrValue;if(!s.enter(o)){t.attrValue="";return}try{let a=this.#t(o,s);t.attrValue=a||""}finally{s.leave(o)}}#n(e){let t=e.length,s=t,r=k.exec(e);if(r&&(s=r.index),e.indexOf("&")>-1){let a=j.exec(e);if(a){let c=a.index;c<s&&(s=c)}}if(s===t)return e;let o=e.charCodeAt(s-1);return(o===63||o===38)&&s--,e.substring(0,s)}sanitize(e,t=this.#e){if(!e||!p(e))return null;let{deny:s,maxLength:r,only:o,allowRelative:a}=t;if(Number.isInteger(r)&&e.length>r){let l=`URL length ${e.length} exceeds max length ${r}.`;throw new RangeError(l)}if(!(Array.isArray(s)&&s.length>0||Array.isArray(o)||a)&&/^https?:\/\//.test(e)&&!k.test(e)&&!e.includes("data:")){let l=d(e,null,!0);return l&&t.schemes.has(l.protocol.replace(/:$/,""))?l.href.replace(/%26/g,ie):null}let i=new L(S,t);return this.#t(e,i)}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=z(e),a=`data:${t?`${t};base64`:"base64"},${r}`;if(Number.isInteger(s.maxLength)&&a.length>s.maxLength)throw new RangeError(`URL length ${a.length} exceeds max length ${s.maxLength}.`);let c=new L(S,s);if(!c.schemeMap.get("data"))return null;let i=d(a);return i?this.#s(i,c):null}async sanitizeDataURL(e,t=this.#e){if(!e||!p(e))return null;let{maxLength:s}=t;if(Number.isInteger(s)&&e.length>s)throw new RangeError(`URL length ${e.length} exceeds max length ${s}.`);let r=d(e);if(!r)return null;let o=f(r.protocol,!0);if(o!=="data")return null;let a=new L(S,t);return a.schemeMap.get(o)?this.#s(r,a):null}};var me=["aaa","aaas","about","acap","acct","acd","ace","aceap","acedp","aceep","acelp","aceup","acr","adiumxtra","adt","aet","afp","afs","agtp","aim","alter","amss","android","appdata","apt","ar","ari","ark","ars","arv","at","attachment","aw","barion","beshare","bitcoin","bitcoincash","bl","blob","bluetooth","bolo","brid","browserext","cabal","caip","calculator","callto","canactid","cap","cast","casts","chrome","chrome-extension","cid","cm","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","gcx","gdsi","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gsos","gtalk","h323","haie","ham","hcap","hcp","hs20","http","https","hxxp","hxxps","hydrazone","hyper","i0","iax","ibi","ibi-","icap","icon","ilstring","im","imap","info","interaction","iotdisco","ipfs","ipn","ipns","ipp","ipps","iqa","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","locus","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","op","opaquelocktoken","openid","openpgp4fpr","opns","ord","ordmail","otpauth","palm","paparazzi","payto","pkcs11","pkg","platform","pop","pres","proxy","psyc","pttp","pwid","qb","query","quic-transport","redis","rediss","reload","res","resource","rmi","rsync","rtmfp","rtmp","rtsp","rtsps","rtspu","rttp","sarif","secondlife","secret-token","seki","service","session","sftp","sgn","shc","shelter","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","sns","soap.beep","soap.beeps","soldat","spacify","spiffe","spotify","ssb","ssh","sss","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","tcxp","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tii","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"];var $=class{#e=new Set(me);get(){return[...this.#e]}getScheme(e){if(!p(e))return null;let t=this.parse(e);return t?this.normalize(t.protocol,!0):null}has(e){let t=this.normalize(e,!0);return t?this.#e.has(t):!1}normalize(e,t=!1){return f(e,t)}normalizeSchemes(e){if(Array.isArray(e)){let t=[];for(let s of e){let r=f(s,!0);r&&t.push(r)}return t}return[]}parse(e,t,s=!1){return d(e,t,s)}verifyURI(e,t){if(!p(e))return!1;let s=this.parse(e);return s?this.verifyScheme(s.protocol,t,!0):!1}verifyScheme(e,t,s=!1){let r=this.normalize(e,!0);if(!r)return!1;let o=t||this.#e;if(!r.includes("+"))return R.test(r)?!1:o.has(r);let a=r.split("+");return a.some(i=>R.test(i))?!1:o.has(r)?!0:s&&W.test(r)||a.every(i=>o.has(i))}};var Oe=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),I=class extends ${#e;#t;#s;constructor(){super(),this.#e=new Set(super.get()),this.#t={...w,schemes:this.#e},this.#s=new N(this.#t)}#o(e){return!p(e)||_.test(e)||e.includes("+")&&e.split("+").some(s=>R.test(s))?!1:A.test(e)}#r(e,t={}){if(!p(e)||e==="")return{isValid:!1};let s={...this.#t,...t,allow:this.normalizeSchemes(t.allow),deny:this.normalizeSchemes(t.deny),only:Object.hasOwn(t,"only")?this.normalizeSchemes(t.only):this.#t.only,schemes:this.#e},r=this.getScheme(e);return r===null&&!s.allowRelative?{isValid:!1}:{options:s,scheme:r}}#n(e,t){let s={input:e,href:null,valid:!1};if(!p(e))return s.reason=`Invalid URL input: ${u(e)}`,s;if(e==="")return s.reason="Invalid URL input: (empty string)",s;let r,o=null;try{r=this.#s.sanitize(e,t),r||(o="Sanitization failed (blocked by allowed schemes or rules).")}catch(a){o=a.message}if(r){let a=d(r);if(s.valid=!0,a){if(f(a.protocol,!0)==="data"){let{mediaTypes:i,data:l,isBase64:m}=O(a.pathname,a.search,a.hash);m&&i.pop(),s.data={mime:i.join(";"),base64:m,data:l}}else s.data=null;for(let i of Oe){let l=a[i];p(l)&&(s[i]=l)}}else s.valid=!1,s.relative=!0,s.href=r,s.reason="Invalid as an absolute URL, but valid as a relative URL."}else s.reason=o;return s}async#i(e,t){try{let{buffer:s,mimeType:r}=await D(e,t.maxBlobSize,t.signal),o=z(s),a=`data:${r?`${r};base64`:"base64"},${o}`,c=this.#n(a,t);return c.input=e,c}catch(s){if(s.name==="AbortError"||t.signal?.aborted)throw s;return t.debug&&h(`Failed to inspect blob URL: ${u(e)}`,s),{input:e,valid:!1,reason:s.message}}finally{t.revokeObjectURL&&URL.revokeObjectURL(e)}}#a(e,t){if(e instanceof TypeError||e.name==="AbortError"||t.signal?.aborted)throw e}get(){return[...this.#e]}has(e){let t=f(e,!0);return t?this.#e.has(t):!1}add(e){if(!p(e))throw new TypeError(`Expected String but got ${y(e)}.`);let t=f(e,!0);if(!this.#o(t))throw new Error(`Invalid scheme: ${e}`);return this.#e.add(t),[...this.#e]}remove(e){if(!p(e))return!1;let t=f(e,!0);return this.#e.delete(t)}reset(){this.#e.clear();for(let e of super.get())this.#e.add(e)}async sanitizeURL(e,t){let{isValid:s,options:r,scheme:o}=this.#r(e,t);if(s===!1)return null;if(o==="blob"){let{allow:a,deny:c,only:i}=r,l=null;try{if(a.includes("blob")&&!c.includes("blob")||Array.isArray(i)&&i.includes("blob")){let m=null,b="";try{let g=await D(e,r.maxBlobSize,r.signal);m=g.buffer,b=g.mimeType}catch(g){if(g.name==="AbortError"||r.signal?.aborted)throw g;r.debug&&h(`Failed to fetch and convert blob URL: ${u(e)}`,g)}m&&(i?.length?i.includes("data")||(r.only=[...i,"data"]):(a.includes("data")||(r.allow=[...a,"data"]),r.deny=c.filter(g=>g!=="data")),l=await this.#s.sanitizeBuffer(m,b,r))}}catch(m){return this.#a(m,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,m),null}finally{r.revokeObjectURL&&URL.revokeObjectURL(e)}return l}try{return o==="data"?this.#s.sanitizeDataURL(e,r):this.#s.sanitize(e,r)}catch(a){return this.#a(a,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,a),null}}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:o}=this.#r(e,t);if(s===!1)return null;if(o==="blob")return r.revokeObjectURL&&URL.revokeObjectURL(e),null;try{return this.#s.sanitize(e,r)}catch(a){return this.#a(a,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,a),null}}async inspectURL(e,t={}){let{options:s,scheme:r}=this.#r(e,t);return r==="blob"?this.#i(e,s):this.#n(e,s)}isValidURI(e){return super.verifyURI(e,this.#e)}};var x=new I,ut=async(n,e)=>x.sanitizeURL(n,e),ht=(n,e)=>x.sanitizeURLSync(n,e),gt=async(n,e={})=>x.inspectURL(n,e),bt=n=>x.isValidURI(n),Rt=n=>x.isValidURI(n),yt=x;export{I as URLSanitizer,yt as default,gt as inspectURL,Rt as isURISync,bt as isValidURI,ut as sanitizeURL,ht as sanitizeURLSync};
|
|
1
|
+
var k="http://dummy.local",_={allow:Object.freeze([]),deny:Object.freeze([]),only:null,allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536,schemes:new Set};var E=n=>Object.prototype.toString.call(n).slice(8,-1),l=n=>typeof n=="string"||n instanceof String,h=(n,e)=>{console.warn(`[URLSanitizer Debug] ${n}`,e)};var he=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),T=[],y=null,A=new Proxy({},{get(n,e,t){let s=globalThis.window??globalThis,r=s.DOMPurify;if(r&&(y||(y=typeof r=="function"?r(s):r),y)){if(T.length){for(let{prop:i,args:c}of T)typeof y[i]=="function"&&y[i](...c);T.length=0}let a=y[e];return typeof a=="function"?a.bind(y):a}return(...a)=>{if(he.includes(e))return T.push({prop:e,args:a}),t;throw new Error("DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).")}}});var j=/&/gi,F=/&(?=[#a-z0-9])(?![a-z0-9]+=)/i;var X=/#$/,V=/^(?:application\/(?:[\w.-]+\+)?xml|image\/svg\+xml|text\/(?:html|xml))(?:\s*;\s*|$)/i;var Y=/\P{ASCII}/u,q=/&#(x0*[\dA-F]{1,2}|0*\d{1,3});?/gi,Q=/(?:%[\dA-F]{2})+/gi,W=/^((?:[^#%]|%(?!23))*)\?$/,v=/^[a-z][\da-z+.-]*$/,Z=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,R=/^(?:java|vb)script$/i,U=/^(?:(?:java|vb)script|blob)$/i,C=/["'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i,K=/^%[\dA-F]{2}$/i,J=/^(?:\/{2,}|\\|[a-z][a-z\d+\-.]*:[^/])/i;var ee=[9,10,13,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231,232,233,234,235,236,237,238,239,240,241,242,243,244,245,246,247,248,249,250,251,252,253,254,255];var B=new Set(ee),G=new Map([[128,8364],[130,8218],[131,402],[132,8222],[133,8230],[134,8224],[135,8225],[136,710],[137,8240],[138,352],[139,8249],[140,338],[142,381],[145,8216],[146,8217],[147,8220],[148,8221],[149,8226],[150,8211],[151,8212],[152,732],[153,8482],[154,353],[155,8250],[156,339],[158,382],[159,376]]),be=()=>{let n=new Map;for(let e=0;e<256;e++)!B.has(e)&&!G.has(e)&&n.set(e,`\\x${e.toString(16).padStart(2,"0").toUpperCase()}`);return n},D=be();var[x,Re,Ee,xe,we,_e]=["&","#","<",">",'"',"'"].map(n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`),Ue={[x]:`${x}amp;`,[Ee]:`${x}lt;`,[xe]:`${x}gt;`,[we]:`${x}quot;`,[_e]:`${x}${Re}39;`},Ve=Array.from({length:256},(n,e)=>`%${e.toString(16).padStart(2,"0").toUpperCase()}`),ae=globalThis.process?.versions?.node!==void 0,ie=`[${[...D.values()].join("")}]`,Le=new RegExp(ie),Se=new RegExp(ie,"g"),Te=new Uint8Array(65536),Ye=new TextEncoder,Ae=new TextDecoder("utf-8",{fatal:!0}),d=(n,e=!1)=>{if(!l(n))return null;let t=Y.test(n)?n.normalize("NFKC"):n;return e?t.trim().toLowerCase().replace(/:$/,""):t},f=(n,e,t=!1)=>{if(!l(n)||n==="")return null;let s=n,r=e&&l(e)?e:"";if(t&&(s=d(s),r&&(r=d(r))),typeof URL.parse=="function")return r?URL.parse(s,r):URL.parse(s);try{return r?new URL(s,r):new URL(s)}catch{return null}},oe=n=>l(n)?n.replace(/:$/,"").split("+"):[];var ce=n=>{if(!l(n))return n;let e=K.test(n)?n.toUpperCase():n;return Ue[e]??n},u=n=>{let e=l(n)?n:String(n);return e.length>64?`${e.slice(0,64)}...`:e},le=n=>l(n)?n.replace(X,"").replace(W,"$1"):n,ve=(n,e)=>{let t=/^x/i.test(e)?Number.parseInt(e.slice(1),16):Number.parseInt(e,10);if(!Number.isInteger(t)||t<0||t>=256)return n;if(D.has(t))return"";if(B.has(t))return String.fromCharCode(t);let s=G.get(t);return String.fromCodePoint(s)},pe=(n,e=0)=>{if(!l(n))throw new TypeError(`Expected String but got ${E(n)}.`);if(!Number.isInteger(e))throw new TypeError(`Expected Number but got ${E(e)}.`);let t;try{t=decodeURIComponent(n)}catch{t=n.replace(Q,r=>{try{return decodeURIComponent(r)}catch{return r}})}t=t.replace(Se,"");let s=0;for(;s+e<16;s++){let r=t;if(t=t.replace(j,"&").replace(q,ve),t===r)break}if(s+e>=16&&/&#/.test(t))throw new Error("Character references nested too deeply.");return t},N=(n,e="",t="")=>{if(!l(n))return{mediaType:"",mediaTypes:[],data:"",isBase64:!1};let s=n.indexOf(",");if(s===-1)return{mediaType:n,mediaTypes:n.split(";"),data:"",isBase64:!1};let r=n.substring(0,s),a=n.substring(s+1),i=e===""&&t===""?a:a+e+t,c=r.split(";"),o=c.at(-1),p=o!==void 0&&o.length===6&&o.toLowerCase()==="base64";return{mediaType:r,mediaTypes:c,data:i,isBase64:p}},me=n=>{if(!l(n))throw new TypeError(`Expected String but got ${E(n)}.`);let e,t=n;try{e=atob(n)}catch{t=n.replace(/\s/g,"");try{e=atob(t)}catch{throw new Error(`Invalid base64 data: ${u(n)}`)}}let s;if(ae&&globalThis.Buffer)s=globalThis.Buffer.from(e,"latin1");else{let r=e.length;r>65536?s=new Uint8Array(r):s=Te.subarray(0,r);for(let a=0;a<r;a++)s[a]=e.charCodeAt(a)}try{let r=Ae.decode(s);return Le.test(r)?t:r}catch{return t}},$=n=>{if(ae&&globalThis.Buffer)return globalThis.Buffer.from(n).toString("base64");let e=new Uint8Array(n);if(typeof e.toBase64=="function")return e.toBase64();let t=[];for(let s=0;s<e.length;s+=8192)t.push(String.fromCharCode(...e.subarray(s,s+8192)));return btoa(t.join(""))},Ce=async(n,e,t)=>{let s=n.body.getReader(),r=[],a="Operation aborted";if(t?.aborted)throw await s.cancel(a),s.releaseLock(),t.reason;let i=()=>{s.cancel(a).catch(()=>{})};t?.addEventListener("abort",i,{once:!0});try{let c=0;for(;c<=e;){if(t?.aborted)throw await s.cancel(a),t.reason;let{done:m,value:b}=await s.read();if(t?.aborted)throw t.reason;if(m)break;if(c+=b.byteLength,c>e)throw await s.cancel("Size limit exceeded"),new DOMException(`Payload (${c} bytes) exceeds max (${e} bytes).`,"NotReadableError");r.push(b)}let o=new Uint8Array(c),p=0;for(let m of r)o.set(m,p),p+=m.byteLength;return o.buffer}finally{t?.removeEventListener("abort",i),s.releaseLock()}},H=async(n,e,t)=>{let s=16777216;Number.isInteger(e)&&e>0&&(s=e);let r=await fetch(n,{signal:t});if(!r.ok){let p=`Failed to fetch ${u(n)}`;throw Number.isInteger(r.status)&&(r.statusText?p+=`: ${r.status} ${r.statusText}`:p+=`: ${r.status}`),new Error(p)}let a=r.headers.get("content-length");if(a){let o=Number.parseInt(a,10);if(Number.isInteger(o)&&o>s)throw new DOMException(`Payload (${o} bytes) exceeds max (${s} bytes).`,"NotReadableError")}let i=r.headers.get("content-type")||"",c;if(r.body)c=await Ce(r,s,t);else{let o=await r.blob();if(o.size>s)throw new DOMException(`Payload (${o.size} bytes) exceeds max (${s} bytes).`,"NotReadableError");c=await o.arrayBuffer()}return{buffer:c,mimeType:i}};var w=class{constructor(e,t=_){this.domPurify=e??null,this.nest=0,this.recurse=new Set,this.restrictScheme=!1,this.schemeMap=new Map([["blob",!1],["data",!1],["file",!1],["javascript",!1],["vbscript",!1]]),this.#e(t)}#e(e){let{allowRelative:t,debug:s,allow:r,deny:a,only:i,schemes:c}=e;if(this.allowRelative=!!t,this.debug=!!s,this.schemes=c,Array.isArray(i)){this.restrictScheme=!0;for(let o of i)this.#t(o)}else{if(Array.isArray(r)&&r.length)for(let o of r)this.#t(o);if(Array.isArray(a)&&a.length)for(let o of a){let p=d(o,!0);p&&this.schemeMap.set(p,!1)}}}#t(e){let t=d(e,!0);return!t||U.test(t)||!v.test(t)||t.includes("+")&&t.split("+").some(r=>R.test(r))?!1:(this.schemeMap.set(t,!0),!0)}enter(e){return this.recurse.has(e)?(this.debug&&h(`Circular Data URL detected and skipped: ${u(e)}`),!1):(this.nest++,this.recurse.add(e),!0)}leave(e){this.nest--,this.recurse.delete(e)}},I=class{#e;constructor(e=_){this.#e=e}#t(e,t){if(t.nest>16)throw new Error("Data URLs nested too deeply.");let s=f(e,null,!0);if(!s){if(t.allowRelative&&!J.test(e)){let a=f(e,k);if(a){let{protocol:i,hostname:c,pathname:o,search:p,hash:m}=a;if(i==="http:"&&c==="dummy.local")return this.#a(`${o}${p}${m}`)}}return null}let r=d(s.protocol,!0);if(t.restrictScheme&&!t.schemeMap.has(r))return null;if(r.includes("+")){let a=r.split("+");if(t.schemeMap.get(r)===!1||a.some(i=>t.schemeMap.get(i)===!1))return null}else if(t.schemeMap.has(r)){if(t.schemeMap.get(r)===!1)return null}else if(!t.schemes.has(r))return null;return r==="data"?this.#s(s,t):this.#a(s.href)}#s(e,t){let{mediaType:s,mediaTypes:r,data:a,isBase64:i}=N(e.pathname,e.search,e.hash),c=a;if(i)try{c=me(a)}catch(o){return t.debug&&h("Failed to parse base64 data.",o),null}try{let o=pe(c).trim(),p=f(o,k);if(!p||oe(p.protocol).some(b=>U.test(b)))return null}catch(o){return t.debug&&h("Failed to parse inner data URL protocol.",o),null}return(!s||V.test(s))&&(c=this.#o(c,t)),c?(i&&c!==a&&r.pop(),`data:${r.join(";")},${c}`):null}#o(e,t){let s="";try{s=decodeURIComponent(e)}catch{s=e}let r=(i,c)=>this.#n(i,c,t),a="";try{t.domPurify.addHook("uponSanitizeAttribute",r),a=t.domPurify.sanitize(s)}finally{t.domPurify.removeHook("uponSanitizeAttribute",r)}return a?encodeURI(le(a)):null}#n(e,t,s){if(!t.attrValue||!/^\s*data:/i.test(t.attrValue))return;let r=f(t.attrValue,null,!0);if(!r||r.protocol!=="data:")return;let a=t.attrValue;if(!s.enter(a)){t.attrValue="";return}try{let i=this.#t(a,s);t.attrValue=i||""}finally{s.leave(a)}}#a(e){let t=e.length,s=t,r=C.exec(e);if(r&&(s=r.index),e.indexOf("&")>-1){let i=F.exec(e);if(i){let c=i.index;c<s&&(s=c)}}if(s===t)return e;let a=e.charCodeAt(s-1);return(a===63||a===38)&&s--,e.substring(0,s)}#i(e,t){if(Number.isInteger(t)&&e.length>t)throw new RangeError(`URL length ${e.length} exceeds max length ${t}.`)}#r(e){let{deny:t,only:s,allowRelative:r}=e;return Array.isArray(t)&&t.length>0||Array.isArray(s)||!!r}#c(e,t){let s=f(e,null,!0);return s&&t.schemes.has(s.protocol.replace(/:$/,""))?s.href.replace(/%26/g,ce):null}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=$(e),i=`data:${t?`${t};base64`:"base64"},${r}`;if(Number.isInteger(s.maxLength)&&i.length>s.maxLength)throw new RangeError(`URL length ${i.length} exceeds max length ${s.maxLength}.`);let c=new w(A,s);if(!c.schemeMap.get("data"))return null;let o=f(i);return o?this.#s(o,c):null}async sanitizeDataURL(e,t=this.#e){if(!e||!l(e))return null;let{maxLength:s}=t;if(Number.isInteger(s)&&e.length>s)throw new RangeError(`URL length ${e.length} exceeds max length ${s}.`);let r=f(e);if(!r)return null;let a=d(r.protocol,!0);if(a!=="data")return null;let i=new w(A,t);return i.schemeMap.get(a)?this.#s(r,i):null}async sanitizeURL(e,t,s=this.#e){if(!e||!l(e))return null;if(this.#i(e,s.maxLength),/^https?$/.test(t)&&!C.test(e)&&!this.#r(s)&&!e.includes("data:"))return this.#c(e,s);let r=new w(A,s);return this.#t(e,r)}sanitizeURLSync(e,t,s=this.#e){if(!e||!l(e)||t==="data")return null;if(this.#i(e,s.maxLength),/^https?$/.test(t)&&!C.test(e)&&!this.#r(s))return this.#c(e,s);let r=new w(null,s);return this.#t(e,r)}};var de=["aaa","aaas","about","acap","acct","acd","ace","aceap","acedp","aceep","acelp","aceup","acr","adiumxtra","adt","aet","afp","afs","agtp","aim","alter","amss","android","appdata","apt","ar","ari","ark","ars","arv","at","attachment","aw","barion","beshare","bitcoin","bitcoincash","bl","blob","bluetooth","bolo","brid","browserext","cabal","caip","calculator","callto","canactid","cap","cast","casts","chrome","chrome-extension","cid","cm","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","gcx","gdsi","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gsos","gtalk","h323","haie","ham","hcap","hcp","hs20","http","https","hxxp","hxxps","hydrazone","hyper","i0","iax","ibi","ibi-","icap","icon","ilstring","im","imap","info","interaction","iotdisco","ipfs","ipn","ipns","ipp","ipps","iqa","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","locus","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","op","opaquelocktoken","openid","openpgp4fpr","opns","ord","ordmail","otpauth","palm","paparazzi","payto","pkcs11","pkg","platform","pop","pres","proxy","psyc","pttp","pwid","qb","query","quic-transport","redis","rediss","reload","res","resource","rmi","rsync","rtmfp","rtmp","rtsp","rtsps","rtspu","rttp","sarif","secondlife","secret-token","seki","service","session","sftp","sgn","shc","shelter","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","sns","soap.beep","soap.beeps","soldat","spacify","spiffe","spotify","ssb","ssh","sss","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","tcxp","teamspeak","teapot","teapots","tel","teliaeid","telnet","tftp","things","thismessage","tii","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"];var M=class{#e=new Set(de);get(){return[...this.#e]}getScheme(e){if(!l(e))return null;let t=this.parse(e);return t?this.normalize(t.protocol,!0):null}has(e){let t=this.normalize(e,!0);return t?this.#e.has(t):!1}normalize(e,t=!1){return d(e,t)}normalizeSchemes(e){if(Array.isArray(e)){let t=[];for(let s of e){let r=d(s,!0);r&&t.push(r)}return t}return[]}parse(e,t,s=!1){return f(e,t,s)}verifyURI(e,t){if(!l(e))return!1;let s=this.parse(e);return s?this.verifyScheme(s.protocol,t,!0):!1}verifyScheme(e,t,s=!1){let r=this.normalize(e,!0);if(!r)return!1;let a=t||this.#e;if(!r.includes("+"))return R.test(r)?!1:a.has(r);let i=r.split("+");return i.some(o=>R.test(o))?!1:a.has(r)?!0:s&&Z.test(r)||i.every(o=>a.has(o))}};var Oe=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),P=class extends M{#e;#t;#s;constructor(){super(),this.#e=new Set(super.get()),this.#t={..._,schemes:this.#e},this.#s=new I(this.#t)}#o(e){return!l(e)||U.test(e)||e.includes("+")&&e.split("+").some(s=>R.test(s))?!1:v.test(e)}#n(e,t={}){if(!l(e)||e==="")return{isValid:!1};let s={...this.#t,...t,allow:this.normalizeSchemes(t.allow),deny:this.normalizeSchemes(t.deny),only:Object.hasOwn(t,"only")?this.normalizeSchemes(t.only):this.#t.only,schemes:this.#e},r=this.getScheme(e);return r===null&&!s.allowRelative?{isValid:!1}:{options:s,scheme:r}}async#a(e,t){let s={input:e,href:null,valid:!1};if(!l(e))return s.reason=`Invalid URL input: ${u(e)}`,s;if(e==="")return s.reason="Invalid URL input: (empty string)",s;let r,a=null;try{let i=this.getScheme(e,!0);r=await this.#s.sanitizeURL(e,i,t),r||(a="Sanitization failed (blocked by allowed schemes or rules).")}catch(i){a=i.message}if(r){let i=f(r);if(s.valid=!0,i){if(d(i.protocol,!0)==="data"){let{mediaTypes:o,data:p,isBase64:m}=N(i.pathname,i.search,i.hash);m&&o.pop(),s.data={mime:o.join(";"),base64:m,data:p}}else s.data=null;for(let o of Oe){let p=i[o];l(p)&&(s[o]=p)}}else s.valid=!1,s.relative=!0,s.href=r,s.reason="Invalid as an absolute URL, but valid as a relative URL."}else s.reason=a;return s}async#i(e,t){try{let{buffer:s,mimeType:r}=await H(e,t.maxBlobSize,t.signal),a=$(s),i=`data:${r?`${r};base64`:"base64"},${a}`,c=await this.#a(i,t);return c.input=e,c}catch(s){if(s.name==="AbortError"||t.signal?.aborted)throw s;return t.debug&&h(`Failed to inspect blob URL: ${u(e)}`,s),{input:e,valid:!1,reason:s.message}}finally{t.revokeObjectURL&&URL.revokeObjectURL(e)}}#r(e,t){if(e instanceof TypeError||e.name==="AbortError"||t.signal?.aborted)throw e}get(){return[...this.#e]}has(e){let t=d(e,!0);return t?this.#e.has(t):!1}add(e){if(!l(e))throw new TypeError(`Expected String but got ${E(e)}.`);let t=d(e,!0);if(!this.#o(t))throw new Error(`Invalid scheme: ${e}`);return this.#e.add(t),[...this.#e]}remove(e){if(!l(e))return!1;let t=d(e,!0);return this.#e.delete(t)}reset(){this.#e.clear();for(let e of super.get())this.#e.add(e)}async sanitizeURL(e,t){let{isValid:s,options:r,scheme:a}=this.#n(e,t);if(s===!1)return null;if(a==="blob"){let{allow:i,deny:c,only:o}=r,p=null;try{if(i.includes("blob")&&!c.includes("blob")||Array.isArray(o)&&o.includes("blob")){let m=null,b="";try{let g=await H(e,r.maxBlobSize,r.signal);m=g.buffer,b=g.mimeType}catch(g){if(g.name==="AbortError"||r.signal?.aborted)throw g;r.debug&&h(`Failed to fetch and convert blob URL: ${u(e)}`,g)}m&&(o?.length?o.includes("data")||(r.only=[...o,"data"]):(i.includes("data")||(r.allow=[...i,"data"]),r.deny=c.filter(g=>g!=="data")),p=await this.#s.sanitizeBuffer(m,b,r))}}catch(m){return this.#r(m,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,m),null}finally{r.revokeObjectURL&&URL.revokeObjectURL(e)}return p}try{return a==="data"?await this.#s.sanitizeDataURL(e,r):await this.#s.sanitizeURL(e,a,r)}catch(i){return this.#r(i,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,i),null}}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:a}=this.#n(e,t);if(s===!1||a==="data")return null;if(a==="blob")return r.revokeObjectURL&&URL.revokeObjectURL(e),null;try{return this.#s.sanitizeURLSync(e,a,r)}catch(i){return this.#r(i,r),r.debug&&h(`Failed to sanitize URL: ${u(e)}`,i),null}}async inspectURL(e,t={}){let{options:s,scheme:r}=this.#n(e,t);return r==="blob"?this.#i(e,s):this.#a(e,s)}isValidURI(e){return super.verifyURI(e,this.#e)}};var S=new P,ht=async(n,e)=>S.sanitizeURL(n,e),gt=(n,e)=>S.sanitizeURLSync(n,e),bt=async(n,e={})=>S.inspectURL(n,e),yt=n=>S.isValidURI(n),Rt=S;export{P as URLSanitizer,Rt as default,bt as inspectURL,yt as isValidURI,ht as sanitizeURL,gt as sanitizeURLSync};
|
|
2
2
|
/*!
|
|
3
3
|
* URL Sanitizer
|
|
4
4
|
*
|