url-sanitizer 5.1.0 → 5.2.1
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 +3 -3
- package/dist/url-sanitizer.min.js.map +3 -3
- package/package.json +4 -4
- package/src/index.js +2 -0
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/filter.js +149 -275
- package/src/mjs/regexp.js +24 -18
- package/src/mjs/sanitize.js +38 -24
- package/src/mjs/scheme.js +14 -13
- package/src/mjs/text-chars.js +1 -1
- package/src/mjs/utility.js +30 -8
- package/types/index.d.ts +1 -0
- package/types/mjs/filter.d.ts +11 -17
- package/types/mjs/regexp.d.ts +4 -3
- package/types/mjs/sanitize.d.ts +1 -1
package/README.md
CHANGED
|
@@ -440,12 +440,12 @@ Execution times were measured using [mitata](https://github.com/evanwashere/mita
|
|
|
440
440
|
|
|
441
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) |
|
|
442
442
|
| :--- | :--- | :--- | :--- |
|
|
443
|
-
| **Normal HTTPS URL** | ~
|
|
444
|
-
| **HTTPS URL with XSS** | ~6.
|
|
445
|
-
| **XSS Scheme URL** | ~5.
|
|
446
|
-
| **XSS Data URL** | ~
|
|
447
|
-
| **XSS Blob URL** | ~
|
|
448
|
-
| **Invalid URL** | ~0.
|
|
443
|
+
| **Normal HTTPS URL** | ~2.01 µs/iter | ~4.39 µs/iter | ~4.65 µs/iter |
|
|
444
|
+
| **HTTPS URL with XSS** | ~6.40 µs/iter<br>returns sanitized URL | ~5.44 µs/iter<br>returns encoded URL (not fully sanitized against XSS) | ~7.39 µs/iter<br>returns encoded URL (not fully sanitized against XSS) |
|
|
445
|
+
| **XSS Scheme URL** | ~5.64 µs/iter<br>returns `null` | ~1.84 µs/iter<br>returns `about:blank` | ~7.77 µs/iter<br>throws `Error` |
|
|
446
|
+
| **XSS Data URL** | ~422.61 µs/iter<br>returns sanitized data URL | ~2.58 µs/iter<br>returns `about:blank` | ~7.94 µs/iter<br>throws `Error` |
|
|
447
|
+
| **XSS Blob URL** | ~591.66 µs/iter<br>returns sanitized data URL | ~2.85 µs/iter<br>returns blob URL as-is | ~8.41 µs/iter<br>throws `Error` |
|
|
448
|
+
| **Invalid URL** | ~0.73 µs/iter<br>returns `null` | ~1.72 µs/iter<br>returns invalid URL as-is | ~17.94 µs/iter<br>throws `Error` |
|
|
449
449
|
|
|
450
450
|
### Characteristics & Trade-offs
|
|
451
451
|
|
|
@@ -1,4 +1,4 @@
|
|
|
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=/&/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};
|
|
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=/&/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){let r=this.normalize(e,!0);if(!r)return!1;let 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(!l(e)||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,schemes:this.#e}),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.clear();for(let e of super.get())this.#e.add(e)}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
|
*
|