url-sanitizer 5.2.3 → 5.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +14 -3
- 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 +5 -4
- package/dist/url-sanitizer.min.js.map +3 -3
- package/package.json +6 -6
- package/src/index.js +1 -0
- package/src/lib/iana/uri-schemes.json +1 -1
- package/src/mjs/sanitize.js +47 -113
- package/src/mjs/utility.js +114 -0
- package/types/index.d.ts +1 -0
- package/types/mjs/sanitize.d.ts +2 -1
- package/types/mjs/utility.d.ts +5 -0
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ It also provides built-in utilities to inspect URLs and verify URI schemes.
|
|
|
16
16
|
* [API Reference](#api-reference)
|
|
17
17
|
* [sanitizeURL(url, opt)](#sanitizeurlurl-opt)
|
|
18
18
|
* [sanitizeURLSync(url, opt)](#sanitizeurlsyncurl-opt)
|
|
19
|
-
* [inspectURL(url)](#inspecturlurl)
|
|
19
|
+
* [inspectURL(url, opt)](#inspecturlurl-opt)
|
|
20
20
|
* [isValidURI(uri)](#isvaliduriuri)
|
|
21
21
|
* [urlSanitizer Instance](#urlsanitizer)
|
|
22
22
|
* [Threat Model](#threat-model)
|
|
@@ -216,16 +216,27 @@ Synchronous version of `sanitizeURL()`.
|
|
|
216
216
|
Use the async version for `blob`.
|
|
217
217
|
However, to prevent memory leaks, it is highly recommended to set the `opt.revokeObjectURL` option to `true` so that the unsupported blob URL is properly revoked before returning `null`.
|
|
218
218
|
|
|
219
|
-
### inspectURL(url)
|
|
219
|
+
### inspectURL(url, opt)
|
|
220
220
|
|
|
221
221
|
Sanitizes the given URL and returns its parsed components.
|
|
222
222
|
|
|
223
223
|
* **Data URLs:** The embedded payload is fully decoded and sanitized (e.g., removing malicious HTML/SVG attributes) before being safely re-encoded.
|
|
224
|
-
* **Blob URLs:** Blob URLs are verified after being converted to data URLs.
|
|
224
|
+
* **Blob URLs:** Blob URLs are verified after being converted to data URLs.
|
|
225
|
+
* Note that blob URLs are not revoked by default unless `opt.revokeObjectURL` is explicitly set to `true`.
|
|
225
226
|
|
|
226
227
|
#### Parameters
|
|
227
228
|
|
|
228
229
|
* url **string** The URL string to sanitize and inspect.
|
|
230
|
+
* opt **[object]** Options.
|
|
231
|
+
* opt.allow **[Array<string>]** Array of allowed schemes, e.g., ['data'].
|
|
232
|
+
* opt.deny **[Array<string>]** Array of denied schemes, e.g., ['web+foo'].
|
|
233
|
+
* opt.only **[Array<string>]** Array of specific schemes to allow, e.g., ['git', 'https'].
|
|
234
|
+
`only` takes precedence over `allow` and `deny`.
|
|
235
|
+
* opt.allowRelative **[boolean]** If `true`, allows root-relative paths (e.g. `/foo`) and relative paths (e.g. `./foo`). Default is `false`.
|
|
236
|
+
* opt.debug **[boolean]** If `true`, outputs internal error/warning logs to the console. Default is `false`.
|
|
237
|
+
* opt.revokeObjectURL **[boolean]** Revokes the blob URL after inspection. Default is `false`.
|
|
238
|
+
* opt.maxBlobSize **[number]** Maximum allowed blob size in bytes. Default is `16_777_216` (16MB).
|
|
239
|
+
* opt.maxLength **[number]** Maximum allowed URL length. Default is `65_536` (64KB).
|
|
229
240
|
|
|
230
241
|
**Returns** **Promise<InspectedURLResult>** The parsed components of the sanitized URL.
|
|
231
242
|
|
|
@@ -1,4 +1,4 @@
|
|
|
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","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","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(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};
|
|
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:i}of S)typeof s[o]=="function"&&s[o](...i);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},f=(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),i=e===""&&t===""?o:o+e+t,a=r.split(";"),c=a.at(-1),l=c!==void 0&&c.length===6&&c.toLowerCase()==="base64";return{mediaType:r,mediaTypes:a,data:i,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 i=()=>{s.cancel(o).catch(()=>{})};t?.addEventListener("abort",i,{once:!0});try{let a=0;for(;a<=e;){if(t?.aborted)throw await s.cancel(o),t.reason;let{done:d,value:g}=await s.read();if(t?.aborted)throw t.reason;if(d)break;if(a+=g.byteLength,a>e)throw await s.cancel("Size limit exceeded"),new DOMException(`Payload (${a} bytes) exceeds max (${e} bytes).`,"NotReadableError");r.push(g)}let c=new Uint8Array(a),l=0;for(let d of r)c.set(d,l),l+=d.byteLength;return c.buffer}finally{t?.removeEventListener("abort",i),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 c=Number.parseInt(o,10);if(Number.isInteger(c)&&c>s)throw new DOMException(`Payload (${c} bytes) exceeds max (${s} bytes).`,"NotReadableError")}let i=r.headers.get("content-type")||"",a;if(r.body)a=await ve(r,s,t);else{let c=await r.blob();if(c.size>s)throw new DOMException(`Payload (${c.size} bytes) exceeds max (${s} bytes).`,"NotReadableError");a=await c.arrayBuffer()}return{buffer:a,mimeType:i}};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:i}=e;if(this.allowRelative=!!t,this.debug=!!s,Array.isArray(i)&&i.length){this.restrictScheme=!0;for(let a of i)this.#t(a)}else{if(Array.isArray(r)&&r.length)for(let a of r)this.#t(a);if(Array.isArray(o)&&o.length)for(let a of o){let c=m(a,!0);c&&this.schemeMap.set(c,!1)}}}#t(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}#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:i,hostname:a,pathname:c,search:l,hash:d}=o;if(i==="http:"&&a==="dummy.local")return this.#n(`${c}${l}${d}`)}}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(i=>t.schemeMap.get(i)===!1))return null}else if(t.schemeMap.get(r)===!1)return null;return r==="data"?this.#s(s,t):this.#n(s.href)}#s(e,t){let{mediaType:s,mediaTypes:r,data:o,isBase64:i}=O(e.pathname,e.search,e.hash),a=o;if(i)try{a=pe(o)}catch(c){return t.debug&&b("Failed to parse base64 data.",c),null}try{let c=le(a).trim(),l=f(c,M);if(!l||ae(l.protocol).some(g=>_.test(g)))return null}catch(c){return t.debug&&b("Failed to parse inner data URL protocol.",c),null}return(!s||F.test(s))&&(a=this.#o(a,t)),a?(i&&a!==o&&r.pop(),`data:${r.join(";")},${a}`):null}#o(e,t){let s="";try{s=decodeURIComponent(e)}catch{s=e}let r=(i,a)=>this.#r(i,a,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=f(t.attrValue,null,!0);if(!r||r.protocol!=="data:")return;let o=t.attrValue;if(!s.enter(o)){t.attrValue="";return}try{let i=this.#t(o,s);t.attrValue=i||""}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 i=j.exec(e);if(i){let a=i.index;a<s&&(s=a)}}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:i}=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||i)&&/^https?:\/\//.test(e)&&!k.test(e)&&!e.includes("data:")){let l=f(e,null,!0);return l&&t.schemes.has(l.protocol.replace(/:$/,""))?l.href.replace(/%26/g,ie):null}let c=new L(T,t);return this.#t(e,c)}async sanitizeBuffer(e,t,s=this.#e){if(!(e instanceof ArrayBuffer))return null;let r=N(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 L(T,s);if(!a.schemeMap.get("data"))return null;let c=f(i);return c?this.#s(c,a):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=f(e);if(!r)return null;let o=m(r.protocol,!0);if(o!=="data")return null;let i=new L(T,t);return i.schemeMap.get(o)?this.#s(r,i):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 f(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 i=r.split("+");return i.some(c=>R.test(c))?!1:o.has(r)?!0:s&&W.test(r)||i.every(c=>o.has(c))}};var Oe=Object.freeze(["href","origin","protocol","username","password","host","hostname","port","pathname","search","hash"]),$=class extends I{#e;#t;#s;constructor(){super(),this.#e=new Set(super.get()),this.#t={...w,schemes:this.#e},this.#s=new z(this.#t)}#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.#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}}#n(e){let t={input:e,href:null,valid:!1};if(!p(e))return t.reason=`Invalid URL input: ${h(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:a,data:c,isBase64:l}=O(o.pathname,o.search,o.hash);l&&a.pop(),t.data={mime:a.join(";"),base64:l,data:c}}else t.data=null;for(let a of Oe){let c=o[a];p(c)&&(t[a]=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}async#a(e,t){try{let{buffer:s,mimeType:r}=await D(e,t.maxBlobSize,t.signal),o=N(s),i=`data:${r?`${r};base64`:"base64"},${o}`,a=this.#n(i,t);return a.input=e,a}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:i,deny:a,only:c}=r,l=null;try{if(i.includes("blob")&&!a.includes("blob")||c.includes("blob")){let d=null,g="";try{let u=await D(e,r.maxBlobSize,r.signal);d=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)}d&&(c.length?c.includes("data")||(r.only=[...c,"data"]):(i.includes("data")||(r.allow=[...i,"data"]),r.deny=a.filter(u=>u!=="data")),l=await this.#s.sanitizeBuffer(d,g,r))}}finally{r.revokeObjectURL&&URL.revokeObjectURL(e)}return l}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.#r(e,t);return s===!1?null:o==="blob"?(r.revokeObjectURL&&URL.revokeObjectURL(e),null):this.#s.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=>x.inspectURL(n),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};
|
|
2
2
|
/*!
|
|
3
3
|
* URL Sanitizer
|
|
4
4
|
*
|