url-sanitizer 5.0.1 → 5.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 CHANGED
@@ -112,7 +112,7 @@ Sanitizes the given URL asynchronously.
112
112
  * opt.maxBlobSize **\[number\]** Maximum allowed blob size in bytes. Default is `16_777_216` (16MB).
113
113
  * opt.maxLength **\[number\]** Maximum allowed URL length. Default is `65_536` (64KB).
114
114
 
115
- **Returns** **Promise<string?>** Sanitized URL, nullable.
115
+ **Returns** **Promise<string?>** Sanitized URL, or null.
116
116
 
117
117
  #### Samples
118
118
 
@@ -238,31 +238,34 @@ Sanitizes the given URL and returns its parsed components.
238
238
  #### InspectedURLResult
239
239
 
240
240
  An object extending the standard [URL](https://developer.mozilla.org/ja/docs/Web/API/URL) API with additional properties.
241
- The properties except for `input` and `valid` are omitted from the object for invalid URLs.
241
+ The properties except for `input`, `valid`, and `href` are omitted from the object for invalid URLs.
242
242
 
243
243
  * `input` — **string** The original URL input.
244
244
  * `valid` — **boolean** Indicates whether the URI is valid.
245
- * `relative` - **[boolean]** Indicates whether the input is a valid relative URL.
245
+ * `href` — **string?** The sanitized URL, or null.
246
+ * `relative` — **[boolean]** Indicates whether the input is a valid relative URL.
246
247
  * `reason` — **[string]** The reason why the URL is invalid. Omitted if `valid` is `true`.
247
248
  * `data` — **[{ mime: string; base64: boolean; data: string; } | null | undefined]** The parsed result of a data URL. Returns `null` if the URL is valid but not a data URL, and is omitted if the URL is invalid.
248
249
  * `data.mime` — **string** The MIME type of the data.
249
250
  * `data.base64` — **boolean** Indicates whether the data is base64-encoded.
250
251
  * `data.data` — **string** The actual data part of the data URL.
251
- * `href`, `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, `pathname`, `search`, `hash` — **[string | undefined]** Properties identical to the standard URL API (omitted if the URL is invalid).
252
+ * `origin`, `protocol`, `username`, `password`, `host`, `hostname`, `port`, `pathname`, `search`, `hash` — **[string | undefined]** Properties identical to the standard URL API (omitted if the URL is invalid).
252
253
 
253
254
  ``` javascript
254
255
  const res1 = await inspectURL('javascript:alert(1)');
255
256
  /* => {
256
257
  input: 'javascript:alert(1)',
257
- valid: false
258
+ valid: false,
259
+ href: null,
260
+ reason: 'Sanitization failed (blocked by allowed schemes or rules).'
258
261
  } */
259
262
 
260
263
  const res2 = await inspectURL('https://www.example.com/?foo=bar#baz');
261
264
  /* => {
262
265
  input: 'https://www.example.com/?foo=bar#baz',
263
266
  valid: true,
264
- data: null,
265
267
  href: 'https://www.example.com/?foo=bar#baz',
268
+ data: null,
266
269
  origin: 'https://www.example.com',
267
270
  protocol: 'https:',
268
271
  hostname: 'www.example.com',
@@ -277,12 +280,12 @@ const res3 = await inspectURL('data:image/svg+xml;base64,PHN2Zz48ZyBvbmNsaWNrPSJ
277
280
  /* => {
278
281
  input: 'data:image/svg+xml;base64,PHN2Zz48ZyBvbmNsaWNrPSJhbGVydCgxKSIvPjwvc3ZnPg==',
279
282
  valid: true,
283
+ href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
280
284
  data: {
281
285
  mime: 'image/svg+xml',
282
286
  base64: false,
283
287
  data: '%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E'
284
288
  },
285
- href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
286
289
  origin: 'null',
287
290
  protocol: 'data:',
288
291
  pathname: 'image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
@@ -294,12 +297,12 @@ const res4 = await inspectURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAU
294
297
  /* => {
295
298
  input: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
296
299
  valid: true,
300
+ href: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
297
301
  data: {
298
302
  mime: 'image/png',
299
303
  base64: true,
300
304
  data: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
301
305
  },
302
- href: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
303
306
  origin: 'null',
304
307
  protocol: 'data:',
305
308
  pathname: 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
@@ -314,12 +317,12 @@ const res5 = await inspectURL(url5);
314
317
  /* => {
315
318
  input: 'blob:nodedata:82ecc5a4-aea8-48d7-a407-64e2ef0913da',
316
319
  valid: true,
320
+ href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
317
321
  data: {
318
322
  mime: 'image/svg+xml',
319
323
  base64: false,
320
324
  data: '%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E'
321
325
  },
322
- href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
323
326
  origin: 'null',
324
327
  protocol: 'data:',
325
328
  pathname: 'image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
@@ -436,12 +439,13 @@ Execution times were measured using [mitata](https://github.com/evanwashere/mita
436
439
  ### Benchmark Results
437
440
 
438
441
  | 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) |
439
- | :--- | :---: | :---: | :---: |
440
- | **Normal HTTP URL** | ~1.25 µs/iter | ~4.33 µs/iter | ~4.68 µs/iter |
441
- | **XSS URL** | ~5.68 µs/iter | ~1.63 µs/iter | ~9.44 µs/iter |
442
- | **Data URL** | ~371.03 µs/iter | ~2.85 µs/iter | ~9.44 µs/iter |
443
- | **Blob URL** | ~552.58 µs/iter | ~2.18 µs/iter | ~9.11 µs/iter |
444
- | **Invalid URL** | ~0.28 µs/iter | ~1.35 µs/iter | ~21.22 µs/iter |
442
+ | :--- | :--- | :--- | :--- |
443
+ | **Normal HTTPS URL** | ~1.41 µs/iter | ~4.00 µs/iter | ~4.65 µs/iter |
444
+ | **HTTPS URL with XSS** | ~6.44 µs/iter<br>returns sanitized URL | ~4.50 µs/iter<br>returns encoded URL (not fully sanitized against XSS) | ~6.56 µs/iter<br>returns encoded URL (not fully sanitized against XSS) |
445
+ | **XSS Scheme URL** | ~5.78 µs/iter<br>returns `null` | ~1.51 µs/iter<br>returns `about:blank` | ~6.33 µs/iter<br>throws `Error` |
446
+ | **XSS Data URL** | ~393.24 µs/iter<br>returns sanitized data URL | ~2.34 µs/iter<br>returns `about:blank` | ~9.83 µs/iter<br>throws `Error` |
447
+ | **XSS Blob URL** | ~606.15 µs/iter<br>returns sanitized data URL | ~2.14 µs/iter<br>returns blob URL as-is | ~9.98 µs/iter<br>throws `Error` |
448
+ | **Invalid URL** | ~0.22 µs/iter<br>returns `null` | ~1.22 µs/iter<br>returns invalid URL as-is | ~20.50 µs/iter<br>throws `Error` |
445
449
 
446
450
  ### Characteristics & Trade-offs
447
451
 
@@ -1,4 +1,4 @@
1
- var me=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),b=[],I=new Proxy({},{get(r,t,e){let s=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(s){if(b.length){for(let{prop:o,args:i}of b)typeof s[o]=="function"&&s[o](...i);b.length=0}let n=s[t];return typeof n=="function"?n.bind(s):n}return(...n)=>{if(me.includes(t))return b.push({prop:t,args:n}),e;throw new Error("DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).")}}});var u=r=>Object.prototype.toString.call(r).slice(8,-1),p=r=>typeof r=="string"||r instanceof String;var M=["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","aet","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","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","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","pkg","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"];var D=/#$/,G=/^(?:application\/(?:[\w.-]+\+)?xml|image\/svg\+xml|text\/(?:html|xml))(?:\s*;\s*|$)/i;var H=/&#(x0*[\dA-F]{1,2}|0*\d{1,3});?/gi,B=/^((?:[^#%]|%(?!23))*)\?$/,j=/^[a-z][\da-z+.-]*$/,F=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,x=/^(?:java|vb)script$/i,v=/^(?:(?:java|vb)script|blob)$/i,U=/["'<>]|%(?:22|27|3C|3E)|&(?:apos|lt|gt|quot|#0*(?:34|39|60|62)|#x0*(?:22|27|3C|3E));/i,X=/^%[\dA-F]{2}$/i,V=/(?:%[\dA-F]{2})+/gi,Y=/&amp;/gi,q=/&(?=[#a-z0-9])(?![a-z0-9]+=)/i,Q=/^(?:\/{2,}|\\|[a-z][a-z\d+\-.]*:[^/])/i;var W=[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 C=new Set(W),z=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]]),Ee=()=>{let r=new Map;for(let t=0;t<256;t++)!C.has(t)&&!z.has(t)&&r.set(t,`\\x${t.toString(16).padStart(2,"0").toUpperCase()}`);return r},L=Ee();var[h,ye,be,xe,Re,we]=["&","#","<",">",'"',"'"].map(r=>`%${r.charCodeAt(0).toString(16).toUpperCase()}`),_e={[h]:`${h}amp;`,[be]:`${h}lt;`,[xe]:`${h}gt;`,[Re]:`${h}quot;`,[we]:`${h}${ye}39;`},tt=Array.from({length:256},(r,t)=>`%${t.toString(16).padStart(2,"0").toUpperCase()}`),Se=globalThis.process?.versions?.node!==void 0,K=`[${[...L.values()].join("")}]`,Te=new RegExp(K),ve=new RegExp(K,"g"),st=new TextEncoder,Ue=new TextDecoder("utf-8",{fatal:!0}),_=class{#e=new Set(M);get(){return[...this.#e]}getScheme(t){if(!p(t))return null;let e=this.parse(t);return e?e.protocol.replace(/:$/,""):null}has(t){let e=this.normalize(t,!0);return e?this.#e.has(e):!1}normalize(t,e=!1){if(!p(t))return null;let s=t.normalize("NFKC");return e?s.trim().toLowerCase():s}parse(t,e){if(!p(t))return null;let s=this.normalize(t);return e&&p(e)?URL.parse(s,this.normalize(e)):URL.parse(s)}verify(t,e){if(!p(t))return!1;let s=this.parse(t);return this.verifyParsed(s,e,!0)}verifyParsed(t,e,s=!1){if(!t||!t.protocol)return!1;let n=t.protocol.toLowerCase().replace(/:$/,""),o=n.split("+");return o.some(a=>x.test(a))?!1:(e||(e=this.#e),e.has(n)?!0:o.length>1?s&&F.test(n)||o.every(a=>e.has(a)):!1)}},$=r=>p(r)?r.replace(/:$/,"").split("+"):[];var J=r=>{if(!p(r))return r;let t=X.test(r)?r.toUpperCase():r;return _e[t]??r},g=r=>{let t=p(r)?r:String(r);return t.length>64?`${t.slice(0,64)}...`:t},ee=r=>p(r)?r.replace(D,"").replace(B,"$1"):r,Ce=(r,t)=>{let e=/^x/i.test(t)?Number.parseInt(t.slice(1),16):Number.parseInt(t,10);if(!Number.isInteger(e)||e<0||e>=256)return r;if(L.has(e))return"";if(C.has(e))return String.fromCharCode(e);let s=z.get(e);return String.fromCodePoint(s)},te=(r,t=0)=>{if(!p(r))throw new TypeError(`Expected String but got ${u(r)}.`);if(!Number.isInteger(t))throw new TypeError(`Expected Number but got ${u(t)}.`);let e;try{e=decodeURIComponent(r)}catch{e=r.replace(V,n=>{try{return decodeURIComponent(n)}catch{return n}})}e=e.replace(ve,"");let s=0;for(;s+t<16;s++){let n=e;if(e=e.replace(Y,"&").replace(H,Ce),e===n)break}if(s+t>=16&&/&#/.test(e))throw new Error("Character references nested too deeply.");return e},A=(r,t="",e="")=>{if(!p(r))return{mediaType:"",mediaTypes:[],data:"",isBase64:!1};let s=r.indexOf(",");if(s===-1)return{mediaType:r,mediaTypes:r.split(";"),data:"",isBase64:!1};let n=r.slice(0,s),o=`${r.slice(s+1)}${t}${e}`,i=n.split(";"),a=/^base64$/i.test(i.at(-1));return{mediaType:n,mediaTypes:i,data:o,isBase64:a}},se=r=>{if(!p(r))throw new TypeError(`Expected String but got ${u(r)}.`);let t=r.replace(/\s/g,""),e;try{e=atob(t)}catch{throw new Error(`Invalid base64 data: ${g(r)}`)}let s;if(Se&&globalThis.Buffer)s=globalThis.Buffer.from(e,"latin1");else{let n=e.length;s=new Uint8Array(n);for(let o=0;o<n;o++)s[o]=e.charCodeAt(o)}try{let n=Ue.decode(s);return Te.test(n)?t:n}catch{return t}};var ne="http://dummy.local",Le=globalThis.process?.versions?.node!==void 0,Ne=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),S=Object.freeze({allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536}),T=(r,t)=>{console.warn(`[URLSanitizer Debug] ${r}`,t)},O=class{constructor(t,e){this.debug=!!t?.debug,this.domPurify=e,this.nest=0,this.recurse=new Set,this.schemeMap=new Map([["blob",!1],["data",!1],["file",!1],["javascript",!1],["vbscript",!1]])}enter(t){return this.recurse.has(t)?(this.debug&&T(`Circular Data URL detected and skipped: ${g(t)}`),!1):(this.nest++,this.recurse.add(t),!0)}leave(t){this.nest--,this.recurse.delete(t)}},P=class extends _{#e;constructor(){super(),this.#e=new Set(super.get())}#t(t,e){if(!t||!p(t))return null;let{allow:s,allowRelative:n,deny:o,maxLength:i,only:a}=e;if(Number.isInteger(i)&&t.length>i){let d=`URL length ${t.length} exceeds max length ${i}.`;throw new RangeError(d)}if(!(Array.isArray(o)&&o.length||Array.isArray(a)&&a.length||n)&&(t.startsWith("https://")||t.startsWith("http://"))&&!U.test(t)&&!t.includes("data:")){let d=this.parse(t);return d?d.href.replace(/%26/g,J):null}let l=new O(e,I);return this.#s(t,{allow:s,allowRelative:n,deny:o,only:a},l)}#s(t,{allow:e,allowRelative:s,deny:n,only:o},i){if(i.nest>16)throw new Error("Data URLs nested too deeply.");let{allowedSchemes:a,restrictScheme:c,schemeMap:l}=this.#o({allow:e,deny:n,only:o},i),{isDataUrl:d,isRelative:f,isVerified:k,scheme:y,schemeParts:le,urlObj:pe,urlToSanitize:de}=this.#i(t,s,a,i);return!k||!this.#a(y,le,c,l,f)?null:d?this.#c(pe,y,i):this.#d(de)}#o({allow:t,deny:e,only:s},n){let o=this.#e,i=!1,a=new Map(n.schemeMap);if(Array.isArray(s)&&s.length){o=new Set,i=!0;for(let c of s)p(c)&&this.#r(c,"only",o,a,n)}else{if(Array.isArray(t)&&t.length){o=new Set(this.#e);for(let c of t)p(c)&&this.#r(c,"allow",o,a,n)}if(Array.isArray(e)&&e.length){for(let c of e)if(p(c)){let l=this.normalize(c,!0);l&&a.set(l,!1)}}}return{allowedSchemes:o,restrictScheme:i,schemeMap:a}}#r(t,e,s,n,o){let i=this.normalize(t,!0);return this.#n(i)?(n.set(i,!0),s.add(i),!0):!1}#n(t){return v.test(t)?!1:!t.split("+").some(n=>x.test(n))&&j.test(t)}#i(t,e,s,n){let o=this.parse(t),i=this.verifyParsed(o,s),a=!1,c="";if(!i&&e&&!Q.test(t)){let f=this.parse(t,ne);f&&f.protocol==="http:"&&f.hostname==="dummy.local"&&(i=!0,a=!0,c=`${f.pathname}${f.search}${f.hash}`)}if(!i)return{isVerified:!1};if(a)return{isRelative:a,isVerified:i,isDataUrl:!1,scheme:"",schemeParts:[],urlToSanitize:c};let l=o.protocol.replace(/:$/,""),d=l.split("+");return{isRelative:a,isVerified:i,scheme:l,schemeParts:d,urlObj:o,isDataUrl:l==="data",urlToSanitize:o.href}}#a(t,e,s,n,o){if(o)return!0;if(s)return n.has(t)||e.every(i=>n.get(i));for(let[i,a]of n.entries())if(!(a||t!==i&&e.every(l=>l!==i)))return!1;return!0}#c(t,e,s){let{mediaType:n,mediaTypes:o,data:i,isBase64:a}=A(t.pathname,t.search,t.hash),c=i;if(a)try{c=se(i)}catch(l){return s.debug&&T("Failed to parse base64 data.",l),null}try{let l=te(c).trim(),d=this.parse(l,ne);if(!d)return null;let f=d.protocol;if($(f).some(y=>v.test(y)))return null}catch(l){return s.debug&&T("Failed to parse inner data URL protocol.",l),null}return(!n||G.test(n))&&(c=this.#l(c,s)),c?(a&&c!==i&&o.pop(),`${e}:${o.join(";")},${c}`):null}#l(t,e){let s=t;try{s=decodeURIComponent(t)}catch{}let n,o=(i,a)=>this.#p(i,a,e);e.domPurify.addHook("uponSanitizeAttribute",o);try{n=e.domPurify.sanitize(s)}finally{e.domPurify.removeHook("uponSanitizeAttribute")}n=ee(n);try{return encodeURI(n)}catch{return n}}#p(t,e,s){if(!e.attrValue||!/^\s*data:/i.test(e.attrValue))return;let n=this.parse(e.attrValue);if(!n||n.protocol!=="data:")return;let o=e.attrValue;if(!s.enter(o)){e.attrValue="";return}try{let i=this.#s(o,{allow:["data"],deny:[],only:[],allowRelative:!1},s);e.attrValue=i||""}finally{s.leave(o)}}#d(t){let e=t,s=[U,q];for(let n of s){let o=n.exec(e);o&&(e=e.substring(0,o.index).replace(/[?&]$/,""))}return e}sanitize(t,e={}){let s={...S,...e};return this.#t(t,s)}inspect(t){let e={input:t,valid:!1};if(!p(t))return e.reason=`Invalid URL input: ${g(t)}`,e;if(t==="")return e.reason="Invalid URL input: (empty string)",e;let s,n=null;try{s=this.#t(t,{...S,allow:["data"],allowRelative:!0}),s||(n="Sanitization failed (blocked by allowed schemes or rules).")}catch(o){n=o.message}if(s){let o=this.parse(s);if(e.valid=!0,o){if($(o.protocol).includes("data")){let{mediaTypes:c,data:l,isBase64:d}=A(o.pathname,o.search,o.hash);d&&c.pop(),e.data={mime:c.join(";"),base64:d,data:l}}else e.data=null;for(let c of Ne){let l=o[c];p(l)&&(e[c]=l)}}else e.valid=!1,e.relative=!0,e.href=s,e.reason="Invalid as an absolute URL, but valid as a relative URL."}else e.valid=!1,e.reason=n;return e}get(){return[...this.#e]}has(t){let e=this.normalize(t,!0);return e?this.#e.has(e):!1}add(t){if(!p(t))throw new TypeError(`Expected String but got ${u(t)}.`);let e=this.normalize(t,!0);if(!this.#n(e))throw new Error(`Invalid scheme: ${t}`);return this.#e.add(e),[...this.#e]}remove(t){if(!p(t))return!1;let e=this.normalize(t,!0);return this.#e.delete(e)}reset(){this.#e=new Set(super.get())}verify(t,e){return super.verify(t,e??this.#e)}},$e=async r=>{let t=r.type?`${r.type};base64`:"base64",e=await r.arrayBuffer(),s=globalThis.Buffer.from(e).toString("base64");return`data:${t},${s}`},Ae=r=>new Promise((t,e)=>{let s=new globalThis.FileReader;s.addEventListener("error",()=>{let n=s.error||new DOMException("Failed to read Blob via FileReader.","NotReadableError");e(n)}),s.addEventListener("abort",()=>t(null)),s.addEventListener("load",()=>t(s.result)),s.readAsDataURL(r)}),Oe=async r=>{let t=r.type?`${r.type};base64`:"base64",e=await r.arrayBuffer(),s=new Uint8Array(e),n=[];for(let o=0;o<s.length;o+=8192)n.push(String.fromCharCode(...s.subarray(o,o+8192)));return`data:${t},${btoa(n.join(""))}`},ie=async(r,t,e,s=0)=>{let{done:n,value:o}=await r.read();if(n)return;let i=s+o.byteLength;if(i>t){await r.cancel("Size limit exceeded");let a=`Payload (${i} bytes) exceeds max (${t} bytes).`;throw new DOMException(a,"NotReadableError")}return e.push(o),ie(r,t,e,i)},ae=async(r,t)=>{let e=16777216;Number.isInteger(t)&&t>0&&(e=t);let s=await fetch(r);if(!s.ok){let a=`Failed to fetch ${g(r)}`;throw Number.isInteger(s.status)&&(s.statusText?a+=`: ${s.status} ${s.statusText}`:a+=`: ${s.status}`),new Error(a)}let n=s.headers.get("content-length");if(n){let i=Number.parseInt(n,10);if(Number.isInteger(i)&&i>e){let a=`Payload (${i} bytes) exceeds max (${e} bytes).`;throw new DOMException(a,"NotReadableError")}}let o;if(s.body){let i=s.body.getReader(),a=[];try{await ie(i,e,a,0)}finally{i.releaseLock()}let c=s.headers.get("content-type");c?o=new Blob(a,{type:c}):o=new Blob(a)}else if(o=await s.blob(),o.size>e){let i=`Payload (${o.size} bytes) exceeds max (${e} bytes).`;throw new DOMException(i,"NotReadableError")}return Le&&globalThis.Buffer?$e(o):globalThis.FileReader?Ae(o):Oe(o)},m=new P,Pe=async(r,t={})=>{if(!r||!p(r))return null;let e={...S,...t},s=m.getScheme(r);if(s===null)return e.allowRelative?m.sanitize(r,e):null;if(s==="blob"){let{allow:n,deny:o,only:i}=e,a=null;if(n.includes("blob")&&!o.includes("blob")||i.includes("blob")){let c;try{c=await ae(r,e.maxBlobSize)}catch(l){e.debug&&T(`Failed to fetch and convert blob URL: ${g(r)}`,l)}c&&(i.length?i.includes("data")||(e.only=[...i,"data"]):(n.includes("data")||(e.allow=[...n,"data"]),e.deny=e.deny.filter(l=>l!=="data")),a=m.sanitize(c,e))}return e.revokeObjectURL&&URL.revokeObjectURL(r),a}return m.sanitize(r,e)},ke=(r,t={})=>{if(!r||!p(r))return null;let e={...S,...t},s=m.getScheme(r);return s===null?e.allowRelative?m.sanitize(r,e):null:s==="blob"?(e.revokeObjectURL&&URL.revokeObjectURL(r),null):m.sanitize(r,e)},Ie=async r=>{if(p(r)){let t=m.parse(r);if(t?.protocol==="blob:")try{let e=await ae(t.href),s=m.inspect(e);return s.input=r,s}catch(e){return{input:r,valid:!1,reason:e.message}}}return m.inspect(r)},ce=r=>m.verify(r),Me=r=>ce(r),De=m;export{De as default,Ie as inspectURL,Me as isURISync,ce as isValidURI,Pe as sanitizeURL,ke as sanitizeURLSync};
1
+ var M="http://dummy.local";var R=n=>Object.prototype.toString.call(n).slice(8,-1),l=n=>typeof n=="string"||n instanceof String,y=(n,e)=>{console.warn(`[URLSanitizer Debug] ${n}`,e)};var ue=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),S=[],L=new Proxy({},{get(n,e,t){let s=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(s){if(S.length){for(let{prop:i,args:a}of S)typeof s[i]=="function"&&s[i](...a);S.length=0}let r=s[e];return typeof r=="function"?r.bind(s):r}return(...r)=>{if(ue.includes(e))return S.push({prop:e,args:r}),t;throw new Error("DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).")}}});var H=/&amp;/gi,j=/&(?=[#a-z0-9])(?![a-z0-9]+=)/i;var X=/#$/,F=/^(?: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))*)\?$/,T=/^[a-z][\da-z+.-]*$/,W=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,d=/^(?:java|vb)script$/i,x=/^(?:(?:java|vb)script|blob)$/i,P=/["'<>]|%(?: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 k=new Set(J),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]]),ge=()=>{let n=new Map;for(let e=0;e<256;e++)!k.has(e)&&!G.has(e)&&n.set(e,`\\x${e.toString(16).padStart(2,"0").toUpperCase()}`);return n},D=ge();var[b,Re,ye,be,Ee,xe]=["&","#","<",">",'"',"'"].map(n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`),we={[b]:`${b}amp;`,[ye]:`${b}lt;`,[be]:`${b}gt;`,[Ee]:`${b}quot;`,[xe]:`${b}${Re}39;`},Fe=Array.from({length:256},(n,e)=>`%${e.toString(16).padStart(2,"0").toUpperCase()}`),se=globalThis.process?.versions?.node!==void 0,re=`[${[...D.values()].join("")}]`,_e=new RegExp(re),Ue=new RegExp(re,"g"),Se=new Uint8Array(65536),Ve=new TextEncoder,Le=new TextDecoder("utf-8",{fatal:!0}),m=(n,e=!1)=>{if(!l(n))return null;let t=V.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=m(s),r&&(r=m(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}},ne=n=>l(n)?n.replace(/:$/,"").split("+"):[];var ie=n=>{if(!l(n))return n;let e=Z.test(n)?n.toUpperCase():n;return we[e]??n},u=n=>{let e=l(n)?n:String(n);return e.length>64?`${e.slice(0,64)}...`:e},oe=n=>l(n)?n.replace(X,"").replace(Q,"$1"):n,Te=(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(k.has(t))return String.fromCharCode(t);let s=G.get(t);return String.fromCodePoint(s)},ae=(n,e=0)=>{if(!l(n))throw new TypeError(`Expected String but got ${R(n)}.`);if(!Number.isInteger(e))throw new TypeError(`Expected Number but got ${R(e)}.`);let t;try{t=decodeURIComponent(n)}catch{t=n.replace(q,r=>{try{return decodeURIComponent(r)}catch{return r}})}t=t.replace(Ue,"");let s=0;for(;s+e<16;s++){let r=t;if(t=t.replace(H,"&").replace(Y,Te),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),i=n.substring(s+1),a=e===""&&t===""?i:i+e+t,o=r.split(";"),c=o.at(-1),p=c!==void 0&&c.length===6&&c.toLowerCase()==="base64";return{mediaType:r,mediaTypes:o,data:a,isBase64:p}},ce=n=>{if(!l(n))throw new TypeError(`Expected String but got ${R(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(se&&globalThis.Buffer)s=globalThis.Buffer.from(e,"latin1");else{let r=e.length;r>65536?s=new Uint8Array(r):s=Se.subarray(0,r);for(let i=0;i<r;i++)s[i]=e.charCodeAt(i)}try{let r=Le.decode(s);return _e.test(r)?t:r}catch{return t}},z=n=>{if(se&&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(""))};var _=class{constructor(e,t){this.domPurify=t,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(e)}#e(e={}){let{allowRelative:t,debug:s,schemes:r,allow:i,deny:a,only:o}=e;if(this.allowRelative=!!t,this.debug=!!s,Array.isArray(o)&&o.length){this.schemes=new Set,this.restrictScheme=!0;for(let c of o)this.#t(c)}else{if(this.schemes=new Set(r||[]),Array.isArray(i)&&i.length)for(let c of i)this.#t(c);if(Array.isArray(a)&&a.length)for(let c of a){let p=m(c,!0);p&&this.schemeMap.set(p,!1)}}}#t(e){let t=m(e,!0);return!t||x.test(t)||!T.test(t)||t.includes("+")&&t.split("+").some(r=>d.test(r))?!1:(this.schemeMap.set(t,!0),this.schemes.add(t),!0)}enter(e){return this.recurse.has(e)?(this.debug&&y(`Circular Data URL detected and skipped: ${u(e)}`),!1):(this.nest++,this.recurse.add(e),!0)}leave(e){this.nest--,this.recurse.delete(e)}},O=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&&!K.test(e)){let i=f(e,M);if(i){let{protocol:a,hostname:o,pathname:c,search:p,hash:h}=i;if(a==="http:"&&o==="dummy.local")return this.#r(`${c}${p}${h}`)}}return null}let r=m(s.protocol,!0);if(t.restrictScheme&&!t.schemeMap.has(r))return null;if(r.includes("+")){let i=r.split("+");if(t.schemeMap.get(r)===!1||i.some(a=>t.schemeMap.get(a)===!1))return null}else if(t.schemeMap.get(r)===!1)return null;return r==="data"?this.#s(s,t):this.#r(s.href)}#s(e,t){let{mediaType:s,mediaTypes:r,data:i,isBase64:a}=N(e.pathname,e.search,e.hash),o=i;if(a)try{o=ce(i)}catch(c){return t.debug&&y("Failed to parse base64 data.",c),null}try{let c=ae(o).trim(),p=f(c,M);if(!p||ne(p.protocol).some(U=>x.test(U)))return null}catch(c){return t.debug&&y("Failed to parse inner data URL protocol.",c),null}return(!s||F.test(s))&&(o=this.#i(o,t)),o?(a&&o!==i&&r.pop(),`data:${r.join(";")},${o}`):null}#i(e,t){let s=e;try{s=decodeURIComponent(e)}catch{}let r;try{t.domPurify.addHook("uponSanitizeAttribute",(i,a)=>this.#n(i,a,t)),r=t.domPurify.sanitize(s)}finally{t.domPurify.removeHook("uponSanitizeAttribute")}r=oe(r);try{return encodeURI(r)}catch{return r}}#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 i=t.attrValue;if(!s.enter(i)){t.attrValue="";return}try{let a=this.#t(i,s);t.attrValue=a||""}finally{s.leave(i)}}#r(e){let t=e.length,s=t,r=P.exec(e);if(r&&(s=r.index),e.indexOf("&")>-1){let a=j.exec(e);if(a){let o=a.index;o<s&&(s=o)}}if(s===t)return e;let i=e.charCodeAt(s-1);return(i===63||i===38)&&s--,e.substring(0,s)}sanitize(e,t=this.#e){if(!e||!l(e))return null;let{deny:s,maxLength:r,only:i,allowRelative:a}=t;if(Number.isInteger(r)&&e.length>r){let p=`URL length ${e.length} exceeds max length ${r}.`;throw new RangeError(p)}if(!(Array.isArray(s)&&s.length>0||Array.isArray(i)&&i.length>0||a)&&(e.startsWith("https://")||e.startsWith("http://"))&&!P.test(e)&&!e.includes("data:")){let p=f(e,null,!0);return p&&t.schemes.has(p.protocol.replace(/:$/,""))?p.href.replace(/%26/g,ie):null}let c=new _(t,L);return this.#t(e,c)}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=z(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 a=new _(s,L);if(!a.schemeMap.get("data"))return null;let o=f(i);return o?this.#s(o,a):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 i=m(r.protocol,!0);if(i!=="data")return null;let a=new _(t,L);return a.schemeMap.get(i)?this.#s(r,a):null}};var le=["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","aet","afp","afs","agtp","aim","amss","android","appdata","apt","ar","ari","ark","ars","at","attachment","aw","barion","beshare","bitcoin","bitcoincash","bl","blob","bluetooth","bolo","brid","browserext","cabal","caip","calculator","callto","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","gtalk","h323","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","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","pkg","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"];var I=class{#e=new Set(le);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 m(e,t)}normalizeSchemes(e){if(Array.isArray(e)){let t=[];for(let s of e){let r=m(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){if(!l(e))return!1;let r=this.normalize(e,!0),i=t||this.#e;if(!r.includes("+"))return d.test(r)?!1:i.has(r);let a=r.split("+");return a.some(c=>d.test(c))?!1:i.has(r)?!0:s&&W.test(r)||a.every(c=>i.has(c))}};var ve=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),Ae=Object.freeze({allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536}),Ne=async(n,e)=>{let t=n.body.getReader(),s=[];try{let r=0;for(;r<=e;){let{done:o,value:c}=await t.read();if(o)break;if(r+=c.byteLength,r>e)throw await t.cancel("Size limit exceeded"),new DOMException(`Payload (${r} bytes) exceeds max (${e} bytes).`,"NotReadableError");s.push(c)}let i=new Uint8Array(r),a=0;for(let o of s)i.set(o,a),a+=o.byteLength;return i.buffer}finally{t.releaseLock()}},pe=async(n,e)=>{let t=16777216;Number.isInteger(e)&&e>0&&(t=e);let s=await fetch(n);if(!s.ok){let c=`Failed to fetch ${u(n)}`;throw Number.isInteger(s.status)&&(s.statusText?c+=`: ${s.status} ${s.statusText}`:c+=`: ${s.status}`),new Error(c)}let r=s.headers.get("content-length");if(r){let o=Number.parseInt(r,10);if(Number.isInteger(o)&&o>t)throw new DOMException(`Payload (${o} bytes) exceeds max (${t} bytes).`,"NotReadableError")}let i=s.headers.get("content-type")||"",a;if(s.body)a=await Ne(s,t);else{let o=await s.blob();if(o.size>t)throw new DOMException(`Payload (${o.size} bytes) exceeds max (${t} bytes).`,"NotReadableError");a=await o.arrayBuffer()}return{buffer:a,mimeType:i}},$=class extends I{#e;#t;#s;constructor(){super(),this.#e=new Set(super.get()),this.#t={...Ae,schemes:this.#e},this.#s=new O(this.#t)}#i(e){return!l(e)||x.test(e)||e.includes("+")&&e.split("+").some(s=>d.test(s))?!1:T.test(e)}#n(e,t={}){if(!e||!l(e))return{isValid:!1};let s={...this.#t,...t,allow:this.normalizeSchemes(t.allow),deny:this.normalizeSchemes(t.deny),only:this.normalizeSchemes(t.only),schemes:this.#e},r=this.getScheme(e);return r===null&&!s.allowRelative?{isValid:!1}:{options:s,scheme:r}}#r(e){let t={input:e,href:null,valid:!1};if(!l(e))return t.reason=`Invalid URL input: ${u(e)}`,t;if(e==="")return t.reason="Invalid URL input: (empty string)",t;let s,r=null;try{s=this.#s.sanitize(e,{...this.#t,allow:["data"],allowRelative:!0}),s||(r="Sanitization failed (blocked by allowed schemes or rules).")}catch(i){r=i.message}if(s){let i=f(s);if(t.valid=!0,i){if(m(i.protocol,!0)==="data"){let{mediaTypes:o,data:c,isBase64:p}=N(i.pathname,i.search,i.hash);p&&o.pop(),t.data={mime:o.join(";"),base64:p,data:c}}else t.data=null;for(let o of ve){let c=i[o];l(c)&&(t[o]=c)}}else t.valid=!1,t.relative=!0,t.href=s,t.reason="Invalid as an absolute URL, but valid as a relative URL."}else t.reason=r;return t}get(){return[...this.#e]}has(e){let t=m(e,!0);return t?this.#e.has(t):!1}add(e){if(!l(e))throw new TypeError(`Expected String but got ${R(e)}.`);let t=m(e,!0);if(!this.#i(t))throw new Error(`Invalid scheme: ${e}`);return this.#e.add(t),[...this.#e]}remove(e){if(!l(e))return!1;let t=m(e,!0);return this.#e.delete(t)}reset(){this.#e=new Set(super.get())}async sanitizeURL(e,t){let{isValid:s,options:r,scheme:i}=this.#n(e,t);if(s===!1)return null;if(i==="blob"){let{allow:a,deny:o,only:c}=r,p=null;if(a.includes("blob")&&!o.includes("blob")||c.includes("blob")){let h=null,U="";try{let g=await pe(e,r.maxBlobSize);h=g.buffer,U=g.mimeType}catch(g){r.debug&&y(`Failed to fetch and convert blob URL: ${u(e)}`,g)}h&&(c.length?c.includes("data")||(r.only=[...c,"data"]):(a.includes("data")||(r.allow=[...a,"data"]),r.deny=o.filter(g=>g!=="data")),p=await this.#s.sanitizeBuffer(h,U,r))}return r.revokeObjectURL&&URL.revokeObjectURL(e),p}else if(i==="data")return this.#s.sanitizeDataURL(e,r);return this.#s.sanitize(e,r)}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:i}=this.#n(e,t);return s===!1?null:i==="blob"?(r.revokeObjectURL&&URL.revokeObjectURL(e),null):this.#s.sanitize(e,r)}async inspectURL(e){if(l(e)){let t=f(e,null,!0);if(t?.protocol==="blob:")try{let{buffer:s,mimeType:r}=await pe(t.href),i=z(s),a=`data:${r?`${r};base64`:"base64"},${i}`,o=this.#r(a);return o.input=e,o}catch(s){return{input:e,valid:!1,reason:s.message}}}return this.#r(e)}isValidURI(e){return super.verifyURI(e,this.#e)}};var E=new $,ut=async(n,e)=>E.sanitizeURL(n,e),ht=(n,e)=>E.sanitizeURLSync(n,e),gt=async n=>E.inspectURL(n),Rt=n=>E.isValidURI(n),yt=n=>E.isValidURI(n),bt=E;export{$ as URLSanitizer,bt as default,gt as inspectURL,yt as isURISync,Rt as isValidURI,ut as sanitizeURL,ht as sanitizeURLSync};
2
2
  /*!
3
3
  * URL Sanitizer
4
4
  *