url-sanitizer 5.3.3 → 5.3.5
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 +6 -6
- package/dist/url-sanitizer-wo-dompurify.min.js +1 -1
- package/dist/url-sanitizer-wo-dompurify.min.js.map +3 -3
- package/dist/url-sanitizer.min.js +4 -4
- package/dist/url-sanitizer.min.js.map +3 -3
- package/package.json +16 -15
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/sanitize.js +44 -4
- package/src/mjs/utility.js +3 -0
- package/types/mjs/sanitize.d.ts +1 -0
package/README.md
CHANGED
|
@@ -449,12 +449,12 @@ Execution times were measured using [mitata](https://github.com/evanwashere/mita
|
|
|
449
449
|
|
|
450
450
|
| 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) |
|
|
451
451
|
| :--- | :--- | :--- | :--- |
|
|
452
|
-
| **Normal HTTPS URL** | ~2.
|
|
453
|
-
| **HTTPS URL with XSS** | ~
|
|
454
|
-
| **XSS Scheme URL** | ~2.
|
|
455
|
-
| **XSS Data URL** | ~
|
|
456
|
-
| **XSS Blob URL** | ~
|
|
457
|
-
| **Invalid URL** | ~0.
|
|
452
|
+
| **Normal HTTPS URL** | ~2.00 µs/iter | ~4.47 µs/iter | ~4.36 µs/iter |
|
|
453
|
+
| **HTTPS URL with XSS** | ~2.97 µs/iter<br>returns sanitized URL | ~5.16 µs/iter<br>returns encoded URL (not fully sanitized against XSS) | ~7.26 µs/iter<br>returns encoded URL (not fully sanitized against XSS) |
|
|
454
|
+
| **XSS Scheme URL** | ~2.14 µs/iter<br>returns `null` | ~1.70 µs/iter<br>returns `about:blank` | ~7.71 µs/iter<br>throws `Error` |
|
|
455
|
+
| **XSS Data URL** | ~384.94 µs/iter<br>returns sanitized data URL | ~2.67 µs/iter<br>returns `about:blank` | ~7.83 µs/iter<br>throws `Error` |
|
|
456
|
+
| **XSS Blob URL** | ~528.22 µs/iter<br>returns sanitized data URL | ~2.31 µs/iter<br>returns blob URL as-is | ~7.77 µs/iter<br>throws `Error` |
|
|
457
|
+
| **Invalid URL** | ~0.67 µs/iter<br>returns `null` | ~1.38 µs/iter<br>returns invalid URL as-is | ~16.01 µs/iter<br>throws `Error` |
|
|
458
458
|
|
|
459
459
|
### Characteristics & Trade-offs
|
|
460
460
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
var M="http://dummy.local",w={allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),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,b=(n,e)=>{console.warn(`[URLSanitizer Debug] ${n}`,e)};var ue=Object.freeze(["addHook","removeHook","removeAllHooks","setConfig"]),S=[],T=new Proxy({},{get(n,e,t){let s=globalThis.DOMPurify||globalThis.window?.DOMPurify;if(s){if(S.length){for(let{prop:o,args:a}of S)typeof s[o]=="function"&&s[o](...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=/&/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))*)\?$/,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;`},Fe=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"),Se=new Uint8Array(65536),Ve=new TextEncoder,Te=new TextDecoder("utf-8",{fatal:!0}),m=(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=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}},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},h=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(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: ${h(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=Se.subarray(0,r);for(let o=0;o<r;o++)s[o]=e.charCodeAt(o)}try{let r=Te.decode(s);return Ue.test(r)?t:r}catch{return t}},N=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:f,value:g}=await s.read();if(t?.aborted)throw t.reason;if(f)break;if(c+=g.byteLength,c>e)throw await s.cancel("Size limit exceeded"),new DOMException(`Payload (${c} bytes) exceeds max (${e} bytes).`,"NotReadableError");r.push(g)}let i=new Uint8Array(c),l=0;for(let f of r)i.set(f,l),l+=f.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 ${h(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)&&a.length){this.restrictScheme=!0;for(let i of a)this.#s(i)}else{if(Array.isArray(r)&&r.length)for(let i of r)this.#s(i);if(Array.isArray(o)&&o.length)for(let i of o){let l=m(i,!0);l&&this.schemeMap.set(l,!1)}}}#s(e){let t=m(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&&b(`Circular Data URL detected and skipped: ${h(e)}`),!1):(this.nest++,this.recurse.add(e),!0)}leave(e){this.nest--,this.recurse.delete(e)}},z=class{#e;constructor(e=w){this.#e=e}#s(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:f}=o;if(a==="http:"&&c==="dummy.local")return this.#n(`${i}${l}${f}`)}}return null}let r=m(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.#t(s,t):this.#n(s.href)}#t(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&&b("Failed to parse base64 data.",i),null}try{let i=le(c).trim(),l=d(i,M);if(!l||ae(l.protocol).some(g=>_.test(g)))return null}catch(i){return t.debug&&b("Failed to parse inner data URL protocol.",i),null}return(!s||F.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.#s(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)&&o.length>0||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(T,t);return this.#s(e,i)}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=N(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(T,s);if(!c.schemeMap.get("data"))return null;let i=d(a);return i?this.#t(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=m(r.protocol,!0);if(o!=="data")return null;let a=new L(T,t);return a.schemeMap.get(o)?this.#t(r,a):null}};var me=["aaa","aaas","about","acap","acct","acd","ace","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","rttp","sarif","secondlife","secret-token","seki","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","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 I=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 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 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"]),$=class extends I{#e;#s;#t;constructor(){super(),this.#e=new Set(super.get()),this.#s={...w,schemes:this.#e},this.#t=new z(this.#s)}#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.#s,...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}}#n(e,t){let s={input:e,href:null,valid:!1};if(!p(e))return s.reason=`Invalid URL input: ${h(e)}`,s;if(e==="")return s.reason="Invalid URL input: (empty string)",s;let r,o=null;try{r=this.#t.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(m(a.protocol,!0)==="data"){let{mediaTypes:i,data:l,isBase64:f}=O(a.pathname,a.search,a.hash);f&&i.pop(),s.data={mime:i.join(";"),base64:f,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#a(e,t){try{let{buffer:s,mimeType:r}=await D(e,t.maxBlobSize,t.signal),o=N(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&&b(`Failed to inspect blob URL: ${h(e)}`,s),{input:e,valid:!1,reason:s.message}}finally{t.revokeObjectURL&&URL.revokeObjectURL(e)}}get(){return[...this.#e]}has(e){let t=m(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=m(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=m(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")||i.includes("blob")){let f=null,g="";try{let u=await D(e,r.maxBlobSize,r.signal);f=u.buffer,g=u.mimeType}catch(u){if(u.name==="AbortError"||r.signal?.aborted)throw u;r.debug&&b(`Failed to fetch and convert blob URL: ${h(e)}`,u)}f&&(i.length?i.includes("data")||(r.only=[...i,"data"]):(a.includes("data")||(r.allow=[...a,"data"]),r.deny=c.filter(u=>u!=="data")),l=await this.#t.sanitizeBuffer(f,g,r))}}finally{r.revokeObjectURL&&URL.revokeObjectURL(e)}return l}else if(o==="data")return this.#t.sanitizeDataURL(e,r);return this.#t.sanitize(e,r)}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:o}=this.#r(e,t);return s===!1?null:o==="blob"?(r.revokeObjectURL&&URL.revokeObjectURL(e),null):this.#t.sanitize(e,r)}async inspectURL(e,t={}){let{options:s,scheme:r}=this.#r(e,t);return r==="blob"?this.#a(e,s):this.#n(e,s)}isValidURI(e){return super.verifyURI(e,this.#e)}};var x=new $,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{$ as URLSanitizer,yt as default,gt as inspectURL,Rt as isURISync,bt as isValidURI,ut as sanitizeURL,ht as sanitizeURLSync};
|
|
1
|
+
var M="http://dummy.local",w={allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),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)&&a.length){this.restrictScheme=!0;for(let i of a)this.#s(i)}else{if(Array.isArray(r)&&r.length)for(let i of r)this.#s(i);if(Array.isArray(o)&&o.length)for(let i of o){let l=f(i,!0);l&&this.schemeMap.set(l,!1)}}}#s(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}#s(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.#t(s,t):this.#n(s.href)}#t(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.#s(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)&&o.length>0||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.#s(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.#t(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.#t(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","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","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;#s;#t;constructor(){super(),this.#e=new Set(super.get()),this.#s={...w,schemes:this.#e},this.#t=new N(this.#s)}#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.#s,...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}}#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.#t.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")||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.#t.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.#t.sanitizeDataURL(e,r):this.#t.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.#t.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};
|
|
2
2
|
/*!
|
|
3
3
|
* URL Sanitizer
|
|
4
4
|
*
|