url-sanitizer 5.0.0 → 5.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 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,30 +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
+ * `href` — **string?** The sanitized URL, or null.
246
+ * `relative` — **[boolean]** Indicates whether the input is a valid relative URL.
245
247
  * `reason` — **[string]** The reason why the URL is invalid. Omitted if `valid` is `true`.
246
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.
247
249
  * `data.mime` — **string** The MIME type of the data.
248
250
  * `data.base64` — **boolean** Indicates whether the data is base64-encoded.
249
251
  * `data.data` — **string** The actual data part of the data URL.
250
- * `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).
251
253
 
252
254
  ``` javascript
253
255
  const res1 = await inspectURL('javascript:alert(1)');
254
256
  /* => {
255
257
  input: 'javascript:alert(1)',
256
- valid: false
258
+ valid: false,
259
+ href: null,
260
+ reason: 'Sanitization failed (blocked by allowed schemes or rules).'
257
261
  } */
258
262
 
259
263
  const res2 = await inspectURL('https://www.example.com/?foo=bar#baz');
260
264
  /* => {
261
265
  input: 'https://www.example.com/?foo=bar#baz',
262
266
  valid: true,
263
- data: null,
264
267
  href: 'https://www.example.com/?foo=bar#baz',
268
+ data: null,
265
269
  origin: 'https://www.example.com',
266
270
  protocol: 'https:',
267
271
  hostname: 'www.example.com',
@@ -276,12 +280,12 @@ const res3 = await inspectURL('data:image/svg+xml;base64,PHN2Zz48ZyBvbmNsaWNrPSJ
276
280
  /* => {
277
281
  input: 'data:image/svg+xml;base64,PHN2Zz48ZyBvbmNsaWNrPSJhbGVydCgxKSIvPjwvc3ZnPg==',
278
282
  valid: true,
283
+ href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
279
284
  data: {
280
285
  mime: 'image/svg+xml',
281
286
  base64: false,
282
287
  data: '%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E'
283
288
  },
284
- href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
285
289
  origin: 'null',
286
290
  protocol: 'data:',
287
291
  pathname: 'image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
@@ -293,12 +297,12 @@ const res4 = await inspectURL('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAU
293
297
  /* => {
294
298
  input: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
295
299
  valid: true,
300
+ href: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
296
301
  data: {
297
302
  mime: 'image/png',
298
303
  base64: true,
299
304
  data: 'iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg=='
300
305
  },
301
- href: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
302
306
  origin: 'null',
303
307
  protocol: 'data:',
304
308
  pathname: 'image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAHElEQVQI12P4//8/w38GIAXDIBKE0DHxgljNBAAO9TXL0Y4OHwAAAABJRU5ErkJggg==',
@@ -313,12 +317,12 @@ const res5 = await inspectURL(url5);
313
317
  /* => {
314
318
  input: 'blob:nodedata:82ecc5a4-aea8-48d7-a407-64e2ef0913da',
315
319
  valid: true,
320
+ href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
316
321
  data: {
317
322
  mime: 'image/svg+xml',
318
323
  base64: false,
319
324
  data: '%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E'
320
325
  },
321
- href: 'data:image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
322
326
  origin: 'null',
323
327
  protocol: 'data:',
324
328
  pathname: 'image/svg+xml,%3Csvg%3E%3Cg%3E%3C/g%3E%3C/svg%3E',
@@ -411,7 +415,7 @@ This library validates the *syntax and safe construction* of a URL, but it does
411
415
  * **Server-Side Request Forgery (SSRF):** We do not check if a URL points to an internal IP address (e.g., `http://localhost`, `http://169.254.169.254`) or a malicious external server.
412
416
  * **Phishing & Open Redirects:** A perfectly valid HTTP URL pointing to a phishing site (e.g., `https://evil-example.com/login`) will be considered safe. You must implement your own domain allow-listing if you need to restrict destinations.
413
417
  * **General HTML Sanitization:** While we sanitize HTML/SVG *inside* data URLs, this library is not a general-purpose HTML body sanitizer. Do not use it to sanitize arbitrary user-generated DOM content.
414
- * **Compromised Host Environment:** We assume the native `URL` API is intact. We do not protect against runtime attacks where the global `URL` object has been monkey-patched or tampered with. Additionally, when using the lightweight build (`url-sanitizer-wo-dompurify.min.js`), ensuring a secure and untampered global `DOMPurify` instance is the responsibility of the host environment.
418
+ * **Compromised Host Environment:** We assume the native `URL` and `fetch` APIs are intact. We do not protect against runtime attacks where these global objects have been monkey-patched or tampered with. Additionally, when using the lightweight build (`url-sanitizer-wo-dompurify.min.js`), ensuring a secure and untampered global `DOMPurify` instance is the responsibility of the host environment.
415
419
 
416
420
  ## AI / LLM Application Security
417
421
 
@@ -435,12 +439,13 @@ Execution times were measured using [mitata](https://github.com/evanwashere/mita
435
439
  ### Benchmark Results
436
440
 
437
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) |
438
- | :--- | :---: | :---: | :---: |
439
- | **Normal HTTP URL** | ~1.25 µs/iter | ~4.33 µs/iter | ~4.68 µs/iter |
440
- | **XSS URL** | ~5.68 µs/iter | ~1.63 µs/iter | ~9.44 µs/iter |
441
- | **Data URL** | ~371.03 µs/iter | ~2.85 µs/iter | ~9.44 µs/iter |
442
- | **Blob URL** | ~552.58 µs/iter | ~2.18 µs/iter | ~9.11 µs/iter |
443
- | **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` |
444
449
 
445
450
  ### Characteristics & Trade-offs
446
451
 
@@ -1,4 +1,4 @@
1
- var me=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),w=[],T=new Proxy({},{get(s,t,e){let r=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(r){if(w.length){for(let{prop:o,args:n}of w)typeof r[o]=="function"&&r[o](...n);w.length=0}let i=r[t];return typeof i=="function"?i.bind(r):i}return(...i)=>{if(me.includes(t))return w.push({prop:t,args:i}),e;throw new Error("DOMPurify is not available. Ensure DOMPurify is exposed globally (e.g., window.DOMPurify).")}}});var h=s=>Object.prototype.toString.call(s).slice(8,-1),p=s=>typeof s=="string"||s instanceof String;var k=["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 G=/#$/,j=/^(?:application\/(?:[\w.-]+\+)?xml|image\/svg\+xml|text\/(?:html|xml))(?:\s*;\s*|$)/i;var B=/&#(x0*[\dA-F]{1,2}|0*\d{1,3});?/gi,H=/^((?:[^#%]|%(?!23))*)\?$/,N=/^[a-z][\da-z+.-]*$/,F=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,y=/^(?:java|vb)script$/i,z=/^(?:(?:java|vb)script|blob)$/i,L=/["'<>]|%(?: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 $=new Set(W),A=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 s=new Map;for(let t=0;t<256;t++)!$.has(t)&&!A.has(t)&&s.set(t,`\\x${t.toString(16).padStart(2,"0").toUpperCase()}`);return s},O=Ee();var[g,ye,be,Re,xe,we]=["&","#","<",">",'"',"'"].map(s=>`%${s.charCodeAt(0).toString(16).toUpperCase()}`),Te={[g]:`${g}amp;`,[be]:`${g}lt;`,[Re]:`${g}gt;`,[xe]:`${g}quot;`,[we]:`${g}${ye}39;`},st=Array.from({length:256},(s,t)=>`%${t.toString(16).padStart(2,"0").toUpperCase()}`),_e=globalThis.process?.versions?.node!==void 0,K=`[${[...O.values()].join("")}]`,Se=new RegExp(K),Ue=new RegExp(K,"g"),rt=new TextEncoder,ve=new TextDecoder("utf-8",{fatal:!0}),U=class{#e=new Set(k);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 r=t.normalize("NFKC");return e?r.trim().toLowerCase():r}parse(t,e){if(!p(t))return null;let r=this.normalize(t);return e&&p(e)?URL.parse(r,this.normalize(e)):URL.parse(r)}verify(t,e){if(!p(t))return!1;let r=this.parse(t);return this.verifyParsed(r,e)}verifyParsed(t,e){if(!t||!t.protocol)return!1;let r=t.protocol.toLowerCase().replace(/:$/,""),i=r.split("+");return i.some(n=>y.test(n))?!1:(e||(e=this.#e),e.has(r)||F.test(r)||i.every(n=>e.has(n)))}},I=s=>p(s)?s.replace(/:$/,"").split("+"):[];var J=s=>{if(!p(s))return s;let t=X.test(s)?s.toUpperCase():s;return Te[t]??s},E=s=>{let t=p(s)?s:String(s);return t.length>64?`${t.slice(0,64)}...`:t},ee=s=>p(s)?s.replace(G,"").replace(H,"$1"):s,Ce=(s,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 s;if(O.has(e))return"";if($.has(e))return String.fromCharCode(e);let r=A.get(e);return String.fromCodePoint(r)},te=(s,t=0)=>{if(!p(s))throw new TypeError(`Expected String but got ${h(s)}.`);if(!Number.isInteger(t))throw new TypeError(`Expected Number but got ${h(t)}.`);let e;try{e=decodeURIComponent(s)}catch{e=s.replace(V,i=>{try{return decodeURIComponent(i)}catch{return i}})}e=e.replace(Ue,"");let r=0;for(;r+t<16;r++){let i=e;if(e=e.replace(Y,"&").replace(B,Ce),e===i)break}if(r+t>=16&&/&#/.test(e))throw new Error("Character references nested too deeply.");return e},se=(s,t="",e="")=>{if(!p(s))return{mediaType:"",mediaTypes:[],data:"",isBase64:!1};let r=s.indexOf(",");if(r===-1)return{mediaType:s,mediaTypes:s.split(";"),data:"",isBase64:!1};let i=s.slice(0,r),o=`${s.slice(r+1)}${t}${e}`,n=i.split(";"),a=/^base64$/i.test(n.at(-1));return{mediaType:i,mediaTypes:n,data:o,isBase64:a}},re=s=>{if(!p(s))throw new TypeError(`Expected String but got ${h(s)}.`);let t=s.replace(/\s/g,""),e;try{e=atob(t)}catch{throw new Error(`Invalid base64 data: ${E(s)}`)}let r;if(_e&&globalThis.Buffer)r=globalThis.Buffer.from(e,"latin1");else{let i=e.length;r=new Uint8Array(i);for(let o=0;o<i;o++)r[o]=e.charCodeAt(o)}try{let i=ve.decode(r);return Se.test(i)?t:i}catch{return t}};var oe="http://dummy.local",ze=globalThis.process?.versions?.node!==void 0,Le=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),$e=Object.freeze({RETURN_DOM:!1,RETURN_DOM_FRAGMENT:!1,RETURN_TRUSTED_TYPE:!1}),v=Object.freeze({allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536}),C=(s,t)=>{console.warn(`[URLSanitizer Debug] ${s}`,t)},M=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&&C(`Circular Data URL detected and skipped: ${E(t)}`),!1):(this.nest++,this.recurse.add(t),!0)}leave(t){this.nest--,this.recurse.delete(t)}},D=class s extends U{#e;static#t=null;static#s=null;static#i(t,e){if(!e.attrValue||!/^\s*data:/i.test(e.attrValue))return;let r=s.#t,i=s.#s;if(!r||!i)return;let o=URL.parse(e.attrValue);if(o&&o.protocol==="data:"){let n=e.attrValue;if(!i.enter(n)){e.attrValue="";return}try{let a=r.#n(n,{allow:["data"],deny:[],only:[],allowRelative:!1},i);e.attrValue=a||""}finally{i.leave(n)}}}static{T.setConfig($e),T.addHook("uponSanitizeAttribute",s.#i)}constructor(){super(),this.#e=new Set(super.get())}#r(t,e,r,i,o){let n=this.normalize(t,!0);return z.test(n)||n.split("+").some(l=>y.test(l))||!N.test(n)?!1:(i.set(n,!0),r.add(n),!0)}#a(t,e){let r=t;try{r=decodeURIComponent(t)}catch{}let i=s.#t,o=s.#s;s.#t=this,s.#s=e;let n;try{n=e.domPurify.sanitize(r)}finally{s.#t=i,s.#s=o}n=ee(n);try{return encodeURI(n)}catch{return n}}#n(t,{allow:e,allowRelative:r,deny:i,only:o},n){if(n.nest>16)throw new Error("Data URLs nested too deeply.");let{allowedSchemes:a,restrictScheme:c,schemeMap:l}=this.#c({allow:e,deny:i,only:o},n),{isDataUrl:d,isRelative:m,isVerified:R,scheme:u,schemeParts:x,urlObj:pe,urlToSanitize:de}=this.#l(t,r,a,n);return!R||!this.#p(u,x,c,l,m)?null:d?this.#d(pe,u,n):this.#m(de)}#c({allow:t,deny:e,only:r},i){let o=this.#e,n=!1,a=new Map(i.schemeMap);if(Array.isArray(r)&&r.length){o=new Set;for(let c of r)p(c)&&this.#r(c.trim(),"only",o,a,i)&&!n&&(n=!0)}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,i)}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:n,schemeMap:a}}#l(t,e,r,i){let o=this.parse(t),n=this.verifyParsed(o,r),a=!1,c="";if(!n&&e&&!Q.test(t)){let m=this.parse(t,oe);m&&m.protocol==="http:"&&m.hostname==="dummy.local"&&(n=!0,a=!0,c=`${m.pathname}${m.search}${m.hash}`)}if(!n)return{isVerified:!1};if(a)return{isRelative:a,isVerified:n,isDataUrl:!1,scheme:"",schemeParts:[],urlToSanitize:c};let l=o.protocol.replace(/:$/,""),d=l.split("+");return{isRelative:a,isVerified:n,scheme:l,schemeParts:d,urlObj:o,isDataUrl:l==="data",urlToSanitize:o.href}}#p(t,e,r,i,o){if(o)return!0;if(r)return i.has(t)||e.every(n=>i.get(n));for(let[n,a]of i.entries())if(!(a||t!==n&&e.every(l=>l!==n)))return!1;return!0}#d(t,e,r){let{mediaType:i,mediaTypes:o,data:n,isBase64:a}=se(t.pathname,t.search,t.hash),c=n;if(a)try{c=re(n)}catch(l){return r.debug&&C("Failed to parse base64 data.",l),null}try{let l=te(c).trim(),d=this.parse(l,oe);if(!d)return null;let m=d.protocol;if(I(m).some(u=>z.test(u)))return null}catch(l){return r.debug&&C("Failed to parse inner data URL protocol.",l),null}return(!i||j.test(i))&&(c=this.#a(c,r)),c?(a&&c!==n&&o.pop(),`${e}:${o.join(";")},${c}`):null}#m(t){let e=t,r=[L,q];for(let i of r){let o=i.exec(e);o&&(e=e.substring(0,o.index).replace(/[?&]$/,""))}return e}#o(t,e){if(!t||!p(t))return null;let{allow:r,allowRelative:i,deny:o,maxLength:n,only:a}=e;if(Number.isInteger(n)&&t.length>n){let d=`URL length ${t.length} exceeds max length ${n}.`;throw new RangeError(d)}if(!(Array.isArray(o)&&o.length||Array.isArray(a)&&a.length||i)&&(t.startsWith("https://")||t.startsWith("http://"))&&!L.test(t)&&!t.includes("data:")){let d=URL.parse(t);return d?d.href.replace(/%26/g,J):null}let l=new M(e,T);return this.#n(t,{allow:r,allowRelative:i,deny:o,only:a},l)}sanitize(t,e={}){let r={...v,...e};return this.#o(t,r)}inspect(t){let e={input:t,valid:!1};if(!p(t))return e.reason=`Invalid URL input: ${E(t)}`,e;if(t==="")return e.reason="Invalid URL input: (empty string)",e;let r,i=null;try{r=this.#o(t,{...v,allow:["data"],allowRelative:!0}),r||(i="Sanitization failed (blocked by allowed schemes or rules).")}catch(o){i=o.message}if(r){let o=URL.parse(r);if(e.valid=!0,o){let{pathname:n,protocol:a}=o;if(I(a).includes("data")){let[d,...m]=n.split(","),R=`${m.join(",")}${o.search}${o.hash}`,u=d.split(";"),x=u[u.length-1]==="base64";x&&u.pop(),e.data={mime:u.join(";"),base64:x,data:R}}else e.data=null;for(let d of Le){let m=o[d];p(m)&&(e[d]=m)}}else e.data=null,e.href=r}else e.valid=!1,e.reason=i;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 ${h(t)}.`);let e=this.normalize(t,!0);if(e.split("+").some(o=>y.test(o))||!N.test(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)}},Ae=async s=>{let t=s.type?`${s.type};base64`:"base64",e=await s.arrayBuffer(),r=globalThis.Buffer.from(e).toString("base64");return`data:${t},${r}`},Oe=s=>new Promise((t,e)=>{let r=new globalThis.FileReader;r.addEventListener("error",()=>{let i=r.error||new DOMException("Failed to read Blob via FileReader.","NotReadableError");e(i)}),r.addEventListener("abort",()=>t(null)),r.addEventListener("load",()=>t(r.result)),r.readAsDataURL(s)}),Pe=async s=>{let t=s.type?`${s.type};base64`:"base64",e=await s.arrayBuffer(),r=new Uint8Array(e),i=[];for(let o=0;o<r.length;o+=8192)i.push(String.fromCharCode(...r.subarray(o,o+8192)));return`data:${t},${btoa(i.join(""))}`},ae=async(s,t,e,r=0)=>{let{done:i,value:o}=await s.read();if(i)return;let n=r+o.byteLength;if(n>t){await s.cancel("Size limit exceeded");let a=`Payload (${n} bytes) exceeds max (${t} bytes).`;throw new DOMException(a,"NotReadableError")}return e.push(o),ae(s,t,e,n)},ce=async(s,t)=>{let e=16777216;Number.isInteger(t)&&t>0&&(e=t);let r=await fetch(s);if(!r.ok){let a=`Failed to fetch ${E(s)}`;throw Number.isInteger(r.status)&&(r.statusText?a+=`: ${r.status} ${r.statusText}`:a+=`: ${r.status}`),new Error(a)}let i=r.headers.get("content-length");if(i){let n=Number.parseInt(i,10);if(Number.isInteger(n)&&n>e){let a=`Payload (${n} bytes) exceeds max (${e} bytes).`;throw new DOMException(a,"NotReadableError")}}let o;if(r.body){let n=r.body.getReader(),a=[];try{await ae(n,e,a,0)}finally{n.releaseLock()}let c=r.headers.get("content-type");c?o=new Blob(a,{type:c}):o=new Blob(a)}else if(o=await r.blob(),o.size>e){let n=`Payload (${o.size} bytes) exceeds max (${e} bytes).`;throw new DOMException(n,"NotReadableError")}return ze&&globalThis.Buffer?Ae(o):globalThis.FileReader?Oe(o):Pe(o)},f=new D,Ie=async(s,t={})=>{if(!s||!p(s))return null;let e={...v,...t},r=f.getScheme(s);if(r===null)return e.allowRelative?f.sanitize(s,e):null;if(r==="blob"){let{allow:i,deny:o,only:n}=e,a=null;if(i.includes("blob")&&!o.includes("blob")||n.includes("blob")){let c;try{c=await ce(s,e.maxBlobSize)}catch(l){e.debug&&C(`Failed to fetch and convert blob URL: ${E(s)}`,l)}c&&(n.length?n.includes("data")||(e.only=[...n,"data"]):(i.includes("data")||(e.allow=[...i,"data"]),e.deny=e.deny.filter(l=>l!=="data")),a=f.sanitize(c,e))}return e.revokeObjectURL&&URL.revokeObjectURL(s),a}return f.sanitize(s,e)},Me=(s,t={})=>{if(!s||!p(s))return null;let e={...v,...t},r=f.getScheme(s);return r===null?e.allowRelative?f.sanitize(s,e):null:r==="blob"?(e.revokeObjectURL&&URL.revokeObjectURL(s),null):f.sanitize(s,e)},De=async s=>{if(p(s)){let t=URL.parse(s);if(t?.protocol==="blob:")try{let e=await ce(t.href),r=f.inspect(e);return r.input=s,r}catch(e){return{input:s,valid:!1,reason:e.message}}}return f.inspect(s)},le=s=>f.verify(s),ke=s=>le(s),Ge=f;export{Ge as default,De as inspectURL,ke as isURISync,le as isValidURI,Ie as sanitizeURL,Me as sanitizeURLSync};
1
+ var I="http://dummy.local";var R=n=>Object.prototype.toString.call(n).slice(8,-1),p=n=>typeof n=="string"||n instanceof String,b=(n,e)=>{console.warn(`[URLSanitizer Debug] ${n}`,e)};var ye=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),T=[],L=new Proxy({},{get(n,e,t){let s=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(s){if(T.length){for(let{prop:i,args:a}of T)typeof s[i]=="function"&&s[i](...a);T.length=0}let r=s[e];return typeof r=="function"?r.bind(s):r}return(...r)=>{if(ye.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 X=/#$/,F=/^(?:application\/(?:[\w.-]+\+)?xml|image\/svg\+xml|text\/(?:html|xml))(?:\s*;\s*|$)/i;var Y=/&#(x0*[\dA-F]{1,2}|0*\d{1,3});?/gi,q=/^((?:[^#%]|%(?!23))*)\?$/,v=/^[a-z][\da-z+.-]*$/,Q=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,y=/^(?:java|vb)script$/i,w=/^(?:(?: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,W=/^%[\dA-F]{2}$/i,Z=/(?:%[\dA-F]{2})+/gi,K=/&amp;/gi,J=/&(?=[#a-z0-9])(?![a-z0-9]+=)/i,ee=/^(?:\/{2,}|\\|[a-z][a-z\d+\-.]*:[^/])/i;var te=[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 D=new Set(te),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]]),xe=()=>{let n=new Map;for(let e=0;e<256;e++)!D.has(e)&&!G.has(e)&&n.set(e,`\\x${e.toString(16).padStart(2,"0").toUpperCase()}`);return n},B=xe();var[E,we,Se,_e,Ue,Te]=["&","#","<",">",'"',"'"].map(n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`),Le={[E]:`${E}amp;`,[Se]:`${E}lt;`,[_e]:`${E}gt;`,[Ue]:`${E}quot;`,[Te]:`${E}${we}39;`},qe=Array.from({length:256},(n,e)=>`%${e.toString(16).padStart(2,"0").toUpperCase()}`),ne=globalThis.process?.versions?.node!==void 0,ie=`[${[...B.values()].join("")}]`,ve=new RegExp(ie),ze=new RegExp(ie,"g"),Qe=new TextEncoder,Ce=new TextDecoder("utf-8",{fatal:!0}),f=(n,e=!1)=>{if(!p(n))return null;let t=n.normalize("NFKC");return e?t.trim().toLowerCase().replace(/:$/,""):t},u=(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}},oe=n=>p(n)?n.replace(/:$/,"").split("+"):[];var ae=n=>{if(!p(n))return n;let e=W.test(n)?n.toUpperCase():n;return Le[e]??n},g=n=>{let e=p(n)?n:String(n);return e.length>64?`${e.slice(0,64)}...`:e},ce=n=>p(n)?n.replace(X,"").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(B.has(t))return"";if(D.has(t))return String.fromCharCode(t);let s=G.get(t);return String.fromCodePoint(s)},le=(n,e=0)=>{if(!p(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(Z,r=>{try{return decodeURIComponent(r)}catch{return r}})}t=t.replace(ze,"");let s=0;for(;s+e<16;s++){let r=t;if(t=t.replace(K,"&").replace(Y,Ae),t===r)break}if(s+e>=16&&/&#/.test(t))throw new Error("Character references nested too deeply.");return t},A=(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.slice(0,s),i=`${n.slice(s+1)}${e}${t}`,a=r.split(";"),o=/^base64$/i.test(a.at(-1));return{mediaType:r,mediaTypes:a,data:i,isBase64:o}},pe=n=>{if(!p(n))throw new TypeError(`Expected String but got ${R(n)}.`);let e=n.replace(/\s/g,""),t;try{t=atob(e)}catch{throw new Error(`Invalid base64 data: ${g(n)}`)}let s;if(ne&&globalThis.Buffer)s=globalThis.Buffer.from(t,"latin1");else{let r=t.length;s=new Uint8Array(r);for(let i=0;i<r;i++)s[i]=t.charCodeAt(i)}try{let r=Ce.decode(s);return ve.test(r)?e:r}catch{return e}},O=n=>{if(ne&&globalThis.Buffer)return globalThis.Buffer.from(n).toString("base64");let e=new Uint8Array(n),t=[];for(let s=0;s<e.length;s+=8192)t.push(String.fromCharCode(...e.subarray(s,s+8192)));return btoa(t.join(""))};var H=Object.freeze({allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536}),_=class{constructor(e,t){this.debug=!!e?.debug,this.schemes=e?.schemes instanceof Set?e.schemes:new Set(e?.schemes||[]),this.domPurify=t,this.nest=0,this.recurse=new Set,this.schemeMap=new Map([["blob",!1],["data",!1],["file",!1],["javascript",!1],["vbscript",!1]])}enter(e){return this.recurse.has(e)?(this.debug&&b(`Circular Data URL detected and skipped: ${g(e)}`),!1):(this.nest++,this.recurse.add(e),!0)}leave(e){this.nest--,this.recurse.delete(e)}},$=class{#e(e,{allow:t,allowRelative:s,deny:r,only:i},a){if(a.nest>16)throw new Error("Data URLs nested too deeply.");let{allowedSchemes:o,restrictScheme:c,schemeMap:l}=this.#t({allow:t,deny:r,only:i},a),{isDataURL:m,isRelative:h,isVerified:d,scheme:U,schemeParts:ue,urlObj:he,urlToSanitize:ge}=this.#n(e,s,o,a);return!d||!this.#o(U,ue,c,l,h)?null:m?this.#i(he,U,a):this.#l(ge)}#t({allow:e,deny:t,only:s},r){let i=new Set(r.schemes),a=!1,o=new Map(r.schemeMap);if(Array.isArray(s)&&s.length){i=new Set,a=!0;for(let c of s)p(c)&&this.#s(c,i,o)}else{if(Array.isArray(e)&&e.length)for(let c of e)p(c)&&this.#s(c,i,o);if(Array.isArray(t)&&t.length){for(let c of t)if(p(c)){let l=f(c,!0);l&&o.set(l,!1)}}}return{allowedSchemes:i,restrictScheme:a,schemeMap:o}}#s(e,t,s){let r=f(e,!0);return this.#r(r)?(s.set(r,!0),t.add(r),!0):!1}#r(e){return w.test(e)?!1:!e.split("+").some(r=>y.test(r))&&v.test(e)}#n(e,t,s,r){let i=u(e,null,!0),a=s.has(f(i?.protocol,!0)),o=!1,c="";if(!a&&t&&!ee.test(e)){let m=u(e,I);m&&m.protocol==="http:"&&m.hostname==="dummy.local"&&(a=!0,o=!0,c=`${m.pathname}${m.search}${m.hash}`)}if(!a)return{isVerified:!1};if(o)return{isRelative:o,isVerified:a,isDataURL:!1,scheme:"",schemeParts:[],urlToSanitize:c};let l=f(i.protocol,!0);return{isRelative:o,isVerified:a,scheme:l,schemeParts:l.split("+"),urlObj:i,isDataURL:l==="data",urlToSanitize:i.href}}#o(e,t,s,r,i){if(i)return!0;if(s)return r.has(e);for(let[a,o]of r.entries())if(!o&&(e===a||t.includes(a)))return!1;return!0}#i(e,t,s){let{mediaType:r,mediaTypes:i,data:a,isBase64:o}=A(e.pathname,e.search,e.hash),c=a;if(o)try{c=pe(a)}catch(l){return s.debug&&b("Failed to parse base64 data.",l),null}try{let l=le(c).trim(),m=u(l,I);if(!m)return null;let h=m.protocol;if(oe(h).some(U=>w.test(U)))return null}catch(l){return s.debug&&b("Failed to parse inner data URL protocol.",l),null}return(!r||F.test(r))&&(c=this.#a(c,s)),c?(o&&c!==a&&i.pop(),`${t}:${i.join(";")},${c}`):null}#a(e,t){let s=e;try{s=decodeURIComponent(e)}catch{}let r,i=(a,o)=>this.#c(a,o,t);t.domPurify.addHook("uponSanitizeAttribute",i);try{r=t.domPurify.sanitize(s)}finally{t.domPurify.removeHook("uponSanitizeAttribute")}r=ce(r);try{return encodeURI(r)}catch{return r}}#c(e,t,s){if(!t.attrValue||!/^\s*data:/i.test(t.attrValue))return;let r=u(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.#e(i,{allow:["data"],deny:[],only:[],allowRelative:!1},s);t.attrValue=a||""}finally{s.leave(i)}}#l(e){let t=e,s=[k,J];for(let r of s){let i=r.exec(t);i&&(t=t.substring(0,i.index).replace(/[?&]$/,""))}return t}sanitize(e,t,s={}){if(!e||!p(e))return null;let r={...H,...s,schemes:t instanceof Set?t:new Set(t||[])},{allow:i,allowRelative:a,deny:o,maxLength:c,only:l}=r;if(Number.isInteger(c)&&e.length>c){let d=`URL length ${e.length} exceeds max length ${c}.`;throw new RangeError(d)}if(!(Array.isArray(o)&&o.length>0||Array.isArray(l)&&l.length>0||a)&&(e.startsWith("https://")||e.startsWith("http://"))&&!k.test(e)&&!e.includes("data:")){let d=u(e,null,!0);return d?d.href.replace(/%26/g,ae):null}let h=new _(r,L);return this.#e(e,{allow:i,allowRelative:a,deny:o,only:l},h)}async sanitizeBuffer(e,t,s,r={}){if(!(e instanceof ArrayBuffer))return null;let i={...H,...r,schemes:s instanceof Set?s:new Set(s||[])},a=new _(i,L),{schemeMap:o}=this.#t(i,a);if(!o.get("data"))return null;let c=O(e),l=`data:${t?`${t};base64`:"base64"},${c}`;if(Number.isInteger(i.maxLength)&&l.length>i.maxLength)throw new RangeError(`URL length ${l.length} exceeds max length ${i.maxLength}.`);let m=u(l);return m?this.#i(m,"data",a):null}async sanitizeDataURL(e,t,s={}){if(!e||!p(e))return null;let r={...H,...s,schemes:t instanceof Set?t:new Set(t||[])},{maxLength:i}=r;if(Number.isInteger(i)&&e.length>i)throw new RangeError(`URL length ${e.length} exceeds max length ${i}.`);let a=u(e);if(!a)return null;let o=f(a.protocol,!0);if(o!=="data")return null;let c=new _(r,L),{schemeMap:l}=this.#t(r,c);return l.get(o)?this.#i(a,o,c):null}};var me=["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","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 P=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 u(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){if(!p(e))return!1;let r=this.normalize(e,!0),i=r.split("+");return i.some(o=>y.test(o))?!1:(t||(t=this.#e),t.has(r)?!0:i.length>1?s&&Q.test(r)||i.every(o=>t.has(o)):!1)}};var $e=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),fe=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()}},de=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 ${g(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}},M=class extends P{#e;#t;constructor(){super(),this.#e=new Set(super.get()),this.#t=new $}#s(e){return!p(e)||w.test(e)?!1:!e.split("+").some(r=>y.test(r))&&v.test(e)}#r(e,t={}){if(!e||!p(e))return{isValid:!1};let{allow:s,deny:r,only:i,...a}=t,o={...fe,...a,allow:this.normalizeSchemes(s),deny:this.normalizeSchemes(r),only:this.normalizeSchemes(i)},c=this.getScheme(e);return c===null&&!o.allowRelative?{isValid:!1}:{options:o,scheme:c}}#n(e){let t={input:e,href:null,valid:!1};if(!p(e))return t.reason=`Invalid URL input: ${g(e)}`,t;if(e==="")return t.reason="Invalid URL input: (empty string)",t;let s,r=null;try{s=this.#t.sanitize(e,[...this.#e],{...fe,allow:["data"],allowRelative:!0}),s||(r="Sanitization failed (blocked by allowed schemes or rules).")}catch(i){r=i.message}if(s){let i=u(s);if(t.valid=!0,i){if(f(i.protocol,!0)==="data"){let{mediaTypes:o,data:c,isBase64:l}=A(i.pathname,i.search,i.hash);l&&o.pop(),t.data={mime:o.join(";"),base64:l,data:c}}else t.data=null;for(let o of $e){let c=i[o];p(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=f(e,!0);return t?this.#e.has(t):!1}add(e){if(!p(e))throw new TypeError(`Expected String but got ${R(e)}.`);let t=f(e,!0);if(!this.#s(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=new Set(super.get())}async sanitizeURL(e,t){let{isValid:s,options:r,scheme:i}=this.#r(e,t);if(s===!1)return null;if(i==="blob"){let{allow:a,deny:o,only:c}=r,l=null;if(a.includes("blob")&&!o.includes("blob")||c.includes("blob")){let m=null,h="";try{let d=await de(e,r.maxBlobSize);m=d.buffer,h=d.mimeType}catch(d){r.debug&&b(`Failed to fetch and convert blob URL: ${g(e)}`,d)}m&&(c.length?c.includes("data")||(r.only=[...c,"data"]):(a.includes("data")||(r.allow=[...a,"data"]),r.deny=o.filter(d=>d!=="data")),l=await this.#t.sanitizeBuffer(m,h,this.#e,r))}return r.revokeObjectURL&&URL.revokeObjectURL(e),l}else if(i==="data")return this.#t.sanitizeDataURL(e,this.#e,r);return this.#t.sanitize(e,this.#e,r)}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:i}=this.#r(e,t);return s===!1?null:i==="blob"?(r.revokeObjectURL&&URL.revokeObjectURL(e),null):this.#t.sanitize(e,this.#e,r)}async inspectURL(e){if(p(e)){let t=u(e,null,!0);if(t?.protocol==="blob:")try{let{buffer:s,mimeType:r}=await de(t.href),i=O(s),a=`data:${r?`${r};base64`:"base64"},${i}`,o=this.#n(a);return o.input=e,o}catch(s){return{input:e,valid:!1,reason:s.message}}}return this.#n(e)}isValidURI(e){return super.verifyURI(e,this.#e)}};var x=new M,Rt=async(n,e)=>x.sanitizeURL(n,e),bt=(n,e)=>x.sanitizeURLSync(n,e),yt=async n=>x.inspectURL(n),Et=n=>x.isValidURI(n),xt=n=>x.isValidURI(n),wt=x;export{wt as default,yt as inspectURL,xt as isURISync,Et as isValidURI,Rt as sanitizeURL,bt as sanitizeURLSync};
2
2
  /*!
3
3
  * URL Sanitizer
4
4
  *