url-sanitizer 5.2.1 → 5.2.2
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 +10 -12
- 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 +8 -8
- package/src/index.js +43 -18
- package/src/mjs/constant.js +16 -0
- package/src/mjs/dompurify.js +2 -2
- package/src/mjs/filter.js +48 -43
- package/src/mjs/sanitize.js +55 -94
- package/types/index.d.ts +29 -14
- package/types/mjs/constant.d.ts +1 -0
- package/types/mjs/filter.d.ts +11 -7
- package/types/mjs/sanitize.d.ts +7 -41
package/README.md
CHANGED
|
@@ -204,12 +204,6 @@ const res7_2 = await sanitizeURL('https://example.com/"onmouseover="alert(1)"',
|
|
|
204
204
|
only: ['data', 'git', 'https']
|
|
205
205
|
});
|
|
206
206
|
// => 'https://example.com/'
|
|
207
|
-
|
|
208
|
-
// `only` also allows combination of the schemes in the list
|
|
209
|
-
const res8 = await sanitizeURL('git+https://example.com/foo.git?<script>alert(1)</script>', {
|
|
210
|
-
only: ['data', 'git', 'https']
|
|
211
|
-
});
|
|
212
|
-
// => 'git+https://example.com/foo.git'
|
|
213
207
|
```
|
|
214
208
|
|
|
215
209
|
### sanitizeURLSync(url, opt)
|
|
@@ -436,16 +430,20 @@ This strict blocking prevents non-existent or hallucinated schemes from inadvert
|
|
|
436
430
|
|
|
437
431
|
Execution times were measured using [mitata](https://github.com/evanwashere/mitata) on Node.js.
|
|
438
432
|
|
|
433
|
+
## Performance
|
|
434
|
+
|
|
435
|
+
Execution times were measured using [mitata](https://github.com/evanwashere/mitata) on Node.js.
|
|
436
|
+
|
|
439
437
|
### Benchmark Results
|
|
440
438
|
|
|
441
439
|
| 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
440
|
| :--- | :--- | :--- | :--- |
|
|
443
|
-
| **Normal HTTPS URL** | ~2.
|
|
444
|
-
| **HTTPS URL with XSS** | ~
|
|
445
|
-
| **XSS Scheme URL** | ~
|
|
446
|
-
| **XSS Data URL** | ~
|
|
447
|
-
| **XSS Blob URL** | ~
|
|
448
|
-
| **Invalid URL** | ~0.
|
|
441
|
+
| **Normal HTTPS URL** | ~2.12 µs/iter | ~4.74 µs/iter | ~4.92 µs/iter |
|
|
442
|
+
| **HTTPS URL with XSS** | ~3.40 µs/iter<br>returns sanitized URL | ~5.61 µs/iter<br>returns encoded URL (not fully sanitized against XSS) | ~7.97 µs/iter<br>returns encoded URL (not fully sanitized against XSS) |
|
|
443
|
+
| **XSS Scheme URL** | ~2.17 µs/iter<br>returns `null` | ~1.91 µs/iter<br>returns `about:blank` | ~8.75 µs/iter<br>throws `Error` |
|
|
444
|
+
| **XSS Data URL** | ~448.03 µs/iter<br>returns sanitized data URL | ~2.99 µs/iter<br>returns `about:blank` | ~9.58 µs/iter<br>throws `Error` |
|
|
445
|
+
| **XSS Blob URL** | ~626.25 µs/iter<br>returns sanitized data URL | ~2.62 µs/iter<br>returns blob URL as-is | ~9.67 µs/iter<br>throws `Error` |
|
|
446
|
+
| **Invalid URL** | ~0.76 μs/iter<br>returns `null` | ~1.62 µs/iter<br>returns invalid URL as-is | ~18.82 µs/iter<br>throws `Error` |
|
|
449
447
|
|
|
450
448
|
### Characteristics & Trade-offs
|
|
451
449
|
|
|
@@ -1,4 +1,4 @@
|
|
|
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};
|
|
1
|
+
var M="http://dummy.local",x={allow:Object.freeze([]),deny:Object.freeze([]),only:Object.freeze([]),allowRelative:!1,debug:!1,revokeObjectURL:!1,maxBlobSize:16777216,maxLength:65536,schemes:new Set};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=[],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))*)\?$/,C=/^[a-z][\da-z+.-]*$/,W=/^(?:web\+[a-z]+|ext\+[a-z][\da-z.-]*)$/,d=/^(?:java|vb)script$/i,w=/^(?:(?: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[E,Re,ye,Ee,be,xe]=["&","#","<",">",'"',"'"].map(n=>`%${n.charCodeAt(0).toString(16).toUpperCase()}`),we={[E]:`${E}amp;`,[ye]:`${E}lt;`,[Ee]:`${E}gt;`,[be]:`${E}quot;`,[xe]:`${E}${Re}39;`},Fe=Array.from({length:256},(n,e)=>`%${e.toString(16).padStart(2,"0").toUpperCase()}`),re=globalThis.process?.versions?.node!==void 0,ne=`[${[...D.values()].join("")}]`,_e=new RegExp(ne),Ue=new RegExp(ne,"g"),Le=new Uint8Array(65536),Ve=new TextEncoder,Se=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}},oe=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},ae=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)},ce=(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),o=n.substring(s+1),a=e===""&&t===""?o:o+e+t,i=r.split(";"),c=i.at(-1),p=c!==void 0&&c.length===6&&c.toLowerCase()==="base64";return{mediaType:r,mediaTypes:i,data:a,isBase64:p}},le=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(re&&globalThis.Buffer)s=globalThis.Buffer.from(e,"latin1");else{let r=e.length;r>65536?s=new Uint8Array(r):s=Le.subarray(0,r);for(let o=0;o<r;o++)s[o]=e.charCodeAt(o)}try{let r=Se.decode(s);return _e.test(r)?t:r}catch{return t}},O=n=>{if(re&&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 U=class{constructor(e,t=x){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}=e;if(this.allowRelative=!!t,this.debug=!!s,Array.isArray(a)&&a.length){this.restrictScheme=!0;for(let i of a)this.#t(i)}else{if(Array.isArray(r)&&r.length)for(let i of r)this.#t(i);if(Array.isArray(o)&&o.length)for(let i of o){let c=m(i,!0);c&&this.schemeMap.set(c,!1)}}}#t(e){let t=m(e,!0);return!t||w.test(t)||!C.test(t)||t.includes("+")&&t.split("+").some(r=>d.test(r))?!1:(this.schemeMap.set(t,!0),!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)}},z=class{#e;constructor(e=x){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 o=f(e,M);if(o){let{protocol:a,hostname:i,pathname:c,search:p,hash:h}=o;if(a==="http:"&&i==="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 o=r.split("+");if(t.schemeMap.get(r)===!1||o.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:o,isBase64:a}=N(e.pathname,e.search,e.hash),i=o;if(a)try{i=le(o)}catch(c){return t.debug&&y("Failed to parse base64 data.",c),null}try{let c=ce(i).trim(),p=f(c,M);if(!p||oe(p.protocol).some(L=>w.test(L)))return null}catch(c){return t.debug&&y("Failed to parse inner data URL protocol.",c),null}return(!s||F.test(s))&&(i=this.#o(i,t)),i?(a&&i!==o&&r.pop(),`data:${r.join(";")},${i}`):null}#o(e,t){let s="";try{s=decodeURIComponent(e)}catch{s=e}let r=(a,i)=>this.#n(a,i,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(ae(o))}catch{return null}}#n(e,t,s){if(!t.attrValue||!/^\s*data:/i.test(t.attrValue))return;let r=f(t.attrValue,null,!0);if(!r||r.protocol!=="data:")return;let o=t.attrValue;if(!s.enter(o)){t.attrValue="";return}try{let a=this.#t(o,s);t.attrValue=a||""}finally{s.leave(o)}}#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 i=a.index;i<s&&(s=i)}}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||!l(e))return null;let{deny:s,maxLength:r,only:o,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(o)&&o.length>0||a)&&/^https?:\/\//.test(e)&&!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 U(T,t);return this.#t(e,c)}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=O(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 i=new U(T,s);if(!i.schemeMap.get("data"))return null;let c=f(a);return c?this.#s(c,i):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 o=m(r.protocol,!0);if(o!=="data")return null;let a=new U(T,t);return a.schemeMap.get(o)?this.#s(r,a):null}};var pe=["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(pe);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 o=t||this.#e;if(!r.includes("+"))return d.test(r)?!1:o.has(r);let a=r.split("+");return a.some(c=>d.test(c))?!1:o.has(r)?!0:s&&W.test(r)||a.every(c=>o.has(c))}};var ve=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),Ne=async(n,e)=>{let t=n.body.getReader(),s=[];try{let r=0;for(;r<=e;){let{done:i,value:c}=await t.read();if(i)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 o=new Uint8Array(r),a=0;for(let i of s)o.set(i,a),a+=i.byteLength;return o.buffer}finally{t.releaseLock()}},me=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 i=Number.parseInt(r,10);if(Number.isInteger(i)&&i>t)throw new DOMException(`Payload (${i} bytes) exceeds max (${t} bytes).`,"NotReadableError")}let o=s.headers.get("content-type")||"",a;if(s.body)a=await Ne(s,t);else{let i=await s.blob();if(i.size>t)throw new DOMException(`Payload (${i.size} bytes) exceeds max (${t} bytes).`,"NotReadableError");a=await i.arrayBuffer()}return{buffer:a,mimeType:o}},$=class extends I{#e;#t;#s;constructor(){super(),this.#e=new Set(super.get()),this.#t={...x,schemes:this.#e},this.#s=new z(this.#t)}#o(e){return!l(e)||w.test(e)||e.includes("+")&&e.split("+").some(s=>d.test(s))?!1:C.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(o){r=o.message}if(s){let o=f(s);if(t.valid=!0,o){if(m(o.protocol,!0)==="data"){let{mediaTypes:i,data:c,isBase64:p}=N(o.pathname,o.search,o.hash);p&&i.pop(),t.data={mime:i.join(";"),base64:p,data:c}}else t.data=null;for(let i of ve){let c=o[i];l(c)&&(t[i]=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.#o(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:o}=this.#n(e,t);if(s===!1)return null;if(o==="blob"){let{allow:a,deny:i,only:c}=r,p=null;try{if(a.includes("blob")&&!i.includes("blob")||c.includes("blob")){let h=null,L="";try{let g=await me(e,r.maxBlobSize);h=g.buffer,L=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=i.filter(g=>g!=="data")),p=await this.#s.sanitizeBuffer(h,L,r))}}finally{r.revokeObjectURL&&URL.revokeObjectURL(e)}return p}else if(o==="data")return this.#s.sanitizeDataURL(e,r);return this.#s.sanitize(e,r)}sanitizeURLSync(e,t){let{isValid:s,options:r,scheme:o}=this.#n(e,t);return s===!1?null:o==="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 me(t.href),o=O(s),a=`data:${r?`${r};base64`:"base64"},${o}`,i=this.#r(a);return i.input=e,i}catch(s){return{input:e,valid:!1,reason:s.message}}}return this.#r(e)}isValidURI(e){return super.verifyURI(e,this.#e)}};var b=new $,ut=async(n,e)=>b.sanitizeURL(n,e),ht=(n,e)=>b.sanitizeURLSync(n,e),gt=async n=>b.inspectURL(n),Rt=n=>b.isValidURI(n),yt=n=>b.isValidURI(n),Et=b;export{$ as URLSanitizer,Et 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
|
*
|