New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

url-sanitizer

Package Overview
Dependencies
Maintainers
1
Versions
76
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

url-sanitizer - npm Package Compare versions

Comparing version 0.5.5 to 0.5.7

54

dist/url-sanitizer.js

@@ -977,14 +977,14 @@ // src/mjs/common.js

* @property {string} data.data - data part of the data URL
* @property {string} href - same as URL API
* @property {string} origin - same as URL API
* @property {string} protocol - same as URL API
* @property {string} username - same as URL API
* @property {string} password - same as URL API
* @property {string} host - same as URL API
* @property {string} hostname - same as URL API
* @property {string} port - same as URL API
* @property {string} pathname - same as URL API
* @property {string} search - same as URL API
* @property {object} searchParams - same as URL API
* @property {string} hash - same as URL API
* @property {string} href - sanitized URL input
* @property {string} origin - scheme, domain and port of the sanitized URL
* @property {string} protocol - protocol scheme of the sanitized URL
* @property {string} username - username specified before the domain name
* @property {string} password - password specified before the domain name
* @property {string} host - domain and port of the sanitized URL
* @property {string} hostname - domain of the sanitized URL
* @property {string} port - port number of the sanitized URL
* @property {string} pathname - path of the sanitized URL
* @property {string} search - query string of the sanitized URL
* @property {object} searchParams - URLSearchParams object
* @property {string} hash - fragment identifier of the sanitized URL
*/

@@ -995,9 +995,10 @@ /**

* @param {string} url - URL input
* @param {object} opt - options
* @returns {ParsedURL} - result with extended props based on URL API
*/
parse(url) {
parse(url, opt) {
if (!isString(url)) {
throw new TypeError(`Expected String but got ${getType(url)}.`);
}
const sanitizedUrl = this.sanitize(url, {
const sanitizedUrl = this.sanitize(url, opt ?? {
allow: ["data", "file"]

@@ -1047,7 +1048,15 @@ });

};
var sanitizeUrl = (url, opt) => urlSanitizer.sanitize(url, opt ?? {
allow: [],
deny: [],
only: []
});
var sanitizeUrl = (url, opt) => {
const parsedUrl = urlSanitizer.parse(url, opt ?? {
allow: [],
deny: [],
only: []
});
let res;
if (parsedUrl) {
const { href } = parsedUrl;
res = href;
}
return res ?? null;
};
var sanitizeURL = async (url, opt) => {

@@ -1057,2 +1066,7 @@ const res = await sanitizeUrl(url, opt);

};
var parseUrl = (url) => urlSanitizer.parse(url);
var parseURL = async (url) => {
const res = await parseUrl(url);
return res;
};
export {

@@ -1062,2 +1076,4 @@ urlSanitizer as default,

isUri as isURISync,
parseURL,
parseUrl as parseURLSync,
sanitizeURL,

@@ -1064,0 +1080,0 @@ sanitizeUrl as sanitizeURLSync

@@ -1,2 +0,2 @@

var g=t=>Object.prototype.toString.call(t).slice(8,-1),d=t=>typeof t=="string"||t instanceof String;var T=[7,8,9,10,11,12,13,27,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,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,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 O=["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","afp","afs","aim","amss","android","appdata","apt","ar","ark","attachment","aw","barion","beshare","bitcoin","bitcoincash","blob","bolo","browserext","cabal","calculator","callto","cap","cast","casts","chrome","chrome-extension","cid","coap","coaps","com-eventbrite-attendee","content","content-type","crid","cstr","cvs","dab","dat","data","dav","diaspora","dict","did","dis","dlna-playcontainer","dlna-playsingle","dns","dntp","doi","dpp","drm","dtmi","dtn","dvb","dvx","dweb","ed2k","eid","elsi","embedded","ens","ethereum","example","facetime","feed","feedready","fido","file","finger","first-run-pen-experience","fish","fm","ftp","fuchsia-pkg","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gtalk","h323","ham","hcap","hcp","http","https","hxxp","hxxps","hydrazone","hyper","iax","icap","icon","im","imap","info","iotdisco","ipfs","ipn","ipns","ipp","ipps","irc","irc6","ircs","iris","iris.beep","iris.lwz","iris.xpc","iris.xpcs","isostore","itms","jabber","jar","jms","keyparc","lastfm","lbry","ldap","ldaps","leaptofrogans","lorawan","lpa","lvlt","magnet","mailto","maps","market","matrix","message","microsoft.windows.camera","microsoft.windows.camera.multipicker","microsoft.windows.camera.picker","mid","mms","mongodb","moz","moz-extension","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-lockscreencomponent-config","ms-media-stream-id","ms-meetnow","ms-mixedrealitycapture","ms-mobileplans","ms-newsandinterests","ms-officeapp","ms-people","ms-powerpoint","ms-project","ms-publisher","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-virtualtouchpad","ms-visio","ms-walk-to","ms-whiteboard","ms-whiteboard-cmd","ms-word","msnim","msrp","msrps","mss","mt","mtqp","mumble","mupdate","mvn","news","nfs","ni","nih","nntp","notes","num","ocf","oid","onenote","onenote-cmd","opaquelocktoken","openpgp4fpr","otpauth","palm","paparazzi","payment","payto","pkcs11","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","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","soap.beep","soap.beeps","soldat","spiffe","spotify","ssb","ssh","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","turn","turns","tv","udp","unreal","urn","ut2004","uuid-in-package","v-event","vemmi","ventrilo","ves","view-source","vnc","vscode","vscode-insiders","vsls","w3","wcr","web3","webcal","wifi","ws","wss","wtai","wyciwyg","xcon","xcon-userid","xfire","xmlrpc.beep","xmlrpc.beeps","xmpp","xri","ymsgr","z39.50r","z39.50s"];var b=16,Q=/^[\da-z+/\-_=]+$/i,V=/data:[^,]*,[^"]+/g,G=/data:[^,]*;?base64,[\da-z+/\-_=]+/i,W=/[<>"'\s]/g,Z=/%(?:2(?:2|7)|3(?:C|E))/g,ee=/&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/ig,te=/^[a-z][\da-z+\-.]*$/,se=/^(?:ext|web)\+[a-z]+$/,x=/(?:java|vb)script/,re=/^%[\dA-F]{2}$/i,ae=/%26/g,L=t=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);let e=[];for(let s of t)e.push(`%${s.charCodeAt(0).toString(b).toUpperCase()}`);return e.join("")},N=t=>{if(d(t))if(re.test(t))t=t.toUpperCase();else throw new Error(`Invalid URL encoded character: ${t}`);else throw new TypeError(`Expected String but got ${g(t)}.`);let[e,s,o,n,l,c]=["&","#","<",">",'"',"'"].map(L),p;return t===e?p=`${e}amp;`:t===o?p=`${e}lt;`:t===n?p=`${e}gt;`:t===l?p=`${e}quot;`:t===c?p=`${e}${s}39;`:p=t,p},ie=t=>{if(d(t)){if(!Q.test(t))throw new Error(`Invalid base64 data: ${t}`)}else throw new TypeError(`Expected String but got ${g(t)}.`);let e=atob(t),s=Uint8Array.from([...e].map(l=>l.charCodeAt(0))),o=new Set(T),n;return s.every(l=>o.has(l))?n=e.replace(/\s/g,L):n=t,n},q=(t,e=0)=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);if(Number.isInteger(e)){if(e>b)throw new Error("Character references nested too deeply.")}else throw new TypeError(`Expected Number but got ${g(e)}.`);let s=decodeURIComponent(t);if(/&#/.test(s)){let o=new Set(T),n=[...s.matchAll(ee)].reverse();for(let l of n){let[c,p]=l,i;if(/^x[\dA-F]+/i.test(p)?i=parseInt(`0${p}`,b):/^[\d]+/.test(p)&&(i=parseInt(p)),Number.isInteger(i)){let{index:m}=l,[r,a]=[s.substring(0,m),s.substring(m+c.length)];o.has(i)?(s=`${r}${String.fromCharCode(i)}${a}`,(/#x?$/.test(r)||/^#(?:x(?:00)?[2-7]|\d)/.test(a))&&(s=q(s,++e))):i<b*b&&(s=`${r}${a}`)}}}return s},j=class{#e;constructor(){this.#e=new Set(O)}get(){return[...this.#e]}has(e){return this.#e.has(e)}add(e){if(d(e)){if(x.test(e)||!te.test(e))throw new Error(`Invalid scheme: ${e}`)}else throw new TypeError(`Expected String but got ${g(e)}.`);return this.#e.add(e),[...this.#e]}remove(e){return this.#e.delete(e)}isURI(e){let s;if(d(e))try{let{protocol:o}=new URL(e),n=o.replace(/:$/,""),l=n.split("+");s=!x.test(n)&&se.test(n)||l.every(c=>this.#e.has(c))}catch{s=!1}return!!s}},A=class extends j{#e;#t;constructor(){super(),this.#e=0,this.#t=new Set}sanitize(e,s={allow:[],deny:[],only:[]}){if(this.#e>b)throw this.#e=0,new Error("Data URLs nested too deeply.");let{allow:o,deny:n,only:l}=s??{},c=new Map([["data",!1],["file",!1],["javascrpt",!1],["vbscript",!1]]),p=!1;if(Array.isArray(l)&&l.length){let m=super.get();for(let a of m)c.set(a,!1);let r=Object.values(l);for(let a of r)if(d(a)&&(a=a.trim(),!x.test(a))){if(super.has(a))c.set(a,!0);else{try{super.add(a)}catch{}super.has(a)&&c.set(a,!0)}!p&&c.has(a)&&(p=c.get(a))}}else{if(Array.isArray(o)&&o.length){let m=Object.values(o);for(let r of m)if(d(r)&&(r=r.trim(),!x.test(r)))if(super.has(r))c.set(r,!0);else{try{super.add(r)}catch{}super.has(r)&&c.set(r,!0)}}if(Array.isArray(n)&&n.length){let m=Object.values(n);for(let r of m)d(r)&&(r=r.trim(),r&&c.set(r,!1))}}let i;if(super.isURI(e)){let{hash:m,href:r,pathname:a,protocol:w,search:v}=new URL(e),$=w.replace(/:$/,""),R=$.split("+"),E;if(p)E=R.every(f=>c.get(f));else for(let[f,y]of c.entries())if(E=y||$!==f&&R.every(S=>S!==f),!E)break;if(E){let f,y=r;if(R.includes("data")){let[S,...B]=a.split(","),_=`${B.join(",")}${v}${m}`,U=S.split(";"),h=_;if(U[U.length-1]==="base64")U.pop(),h=ie(_);else try{let k=q(h),{protocol:z}=new URL(k.trim());z.replace(/:$/,"").split("+").some(u=>x.test(u))&&(y="")}catch{}let M=/data:[^,]*,/.test(h);if(h!==_||M){if(M){let z=[...h.matchAll(V)].reverse();for(let C of z){let[u]=C;G.test(u)&&([u]=G.exec(u)),this.#e++,this.#t.add(u);let P=this.sanitize(u,{allow:["data"]});if(P){let{index:D}=C,[Y,X]=[h.substring(0,D),h.substring(D+u.length)];h=`${Y}${P}${X}`}}this.#t.has(e)?this.#t.delete(e):f=!0}else this.#t.has(e)?this.#t.delete(e):f=!0;y=`${$}:${U.join(";")},${h}`}else this.#t.has(e)?this.#t.delete(e):f=!0}else f=!0;y?(i=y.replace(W,L).replace(ae,N),f&&(i=i.replace(Z,N),this.#e=0)):(i=y,this.#e=0)}}return i||null}parse(e){if(!d(e))throw new TypeError(`Expected String but got ${g(e)}.`);let s=this.sanitize(e,{allow:["data","file"]}),o=new Map([["input",e]]);if(s){let n=new URL(s),{pathname:l,protocol:c}=n,p=c.replace(/:$/,"").split("+");if(o.set("valid",!0),p.includes("data")){let i=new Map,[m,...r]=l.split(","),a=`${r.join(",")}`,w=m.split(";"),v=w[w.length-1]==="base64";v&&w.pop(),i.set("mime",w.join(";")),i.set("base64",v),i.set("data",a),o.set("data",Object.fromEntries(i))}else o.set("data",null);for(let i in n){let m=n[i];typeof m!="function"&&o.set(i,m)}}else o.set("valid",!1);return Object.fromEntries(o)}},I=new A,H=t=>I.isURI(t),oe=async t=>await H(t),F=(t,e)=>I.sanitize(t,e??{allow:[],deny:[],only:[]}),ne=async(t,e)=>await F(t,e);export{I as default,oe as isURI,H as isURISync,ne as sanitizeURL,F as sanitizeURLSync};
var g=t=>Object.prototype.toString.call(t).slice(8,-1),d=t=>typeof t=="string"||t instanceof String;var j=[7,8,9,10,11,12,13,27,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,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,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 O=["aaa","aaas","about","acap","acct","acd","acr","adiumxtra","adt","afp","afs","aim","amss","android","appdata","apt","ar","ark","attachment","aw","barion","beshare","bitcoin","bitcoincash","blob","bolo","browserext","cabal","calculator","callto","cap","cast","casts","chrome","chrome-extension","cid","coap","coaps","com-eventbrite-attendee","content","content-type","crid","cstr","cvs","dab","dat","data","dav","diaspora","dict","did","dis","dlna-playcontainer","dlna-playsingle","dns","dntp","doi","dpp","drm","dtmi","dtn","dvb","dvx","dweb","ed2k","eid","elsi","embedded","ens","ethereum","example","facetime","feed","feedready","fido","file","finger","first-run-pen-experience","fish","fm","ftp","fuchsia-pkg","geo","gg","git","gitoid","gizmoproject","go","gopher","graph","gtalk","h323","ham","hcap","hcp","http","https","hxxp","hxxps","hydrazone","hyper","iax","icap","icon","im","imap","info","iotdisco","ipfs","ipn","ipns","ipp","ipps","irc","irc6","ircs","iris","iris.beep","iris.lwz","iris.xpc","iris.xpcs","isostore","itms","jabber","jar","jms","keyparc","lastfm","lbry","ldap","ldaps","leaptofrogans","lorawan","lpa","lvlt","magnet","mailto","maps","market","matrix","message","microsoft.windows.camera","microsoft.windows.camera.multipicker","microsoft.windows.camera.picker","mid","mms","mongodb","moz","moz-extension","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-lockscreencomponent-config","ms-media-stream-id","ms-meetnow","ms-mixedrealitycapture","ms-mobileplans","ms-newsandinterests","ms-officeapp","ms-people","ms-powerpoint","ms-project","ms-publisher","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-virtualtouchpad","ms-visio","ms-walk-to","ms-whiteboard","ms-whiteboard-cmd","ms-word","msnim","msrp","msrps","mss","mt","mtqp","mumble","mupdate","mvn","news","nfs","ni","nih","nntp","notes","num","ocf","oid","onenote","onenote-cmd","opaquelocktoken","openpgp4fpr","otpauth","palm","paparazzi","payment","payto","pkcs11","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","sieve","simpleledger","simplex","sip","sips","skype","smb","smp","sms","smtp","snmp","soap.beep","soap.beeps","soldat","spiffe","spotify","ssb","ssh","starknet","steam","stun","stuns","submit","svn","swh","swid","swidpath","tag","taler","teamspeak","tel","teliaeid","telnet","tftp","things","thismessage","tip","tn3270","tool","turn","turns","tv","udp","unreal","urn","ut2004","uuid-in-package","v-event","vemmi","ventrilo","ves","view-source","vnc","vscode","vscode-insiders","vsls","w3","wcr","web3","webcal","wifi","ws","wss","wtai","wyciwyg","xcon","xcon-userid","xfire","xmlrpc.beep","xmlrpc.beeps","xmpp","xri","ymsgr","z39.50r","z39.50s"];var x=16,V=/^[\da-z+/\-_=]+$/i,W=/data:[^,]*,[^"]+/g,G=/data:[^,]*;?base64,[\da-z+/\-_=]+/i,Z=/[<>"'\s]/g,ee=/%(?:2(?:2|7)|3(?:C|E))/g,te=/&#(x(?:00)?[\dA-F]{2}|0?\d{1,3});?/ig,se=/^[a-z][\da-z+\-.]*$/,re=/^(?:ext|web)\+[a-z]+$/,v=/(?:java|vb)script/,ae=/^%[\dA-F]{2}$/i,ie=/%26/g,I=t=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);let e=[];for(let r of t)e.push(`%${r.charCodeAt(0).toString(x).toUpperCase()}`);return e.join("")},N=t=>{if(d(t))if(ae.test(t))t=t.toUpperCase();else throw new Error(`Invalid URL encoded character: ${t}`);else throw new TypeError(`Expected String but got ${g(t)}.`);let[e,r,o,a,n,c]=["&","#","<",">",'"',"'"].map(I),p;return t===e?p=`${e}amp;`:t===o?p=`${e}lt;`:t===a?p=`${e}gt;`:t===n?p=`${e}quot;`:t===c?p=`${e}${r}39;`:p=t,p},oe=t=>{if(d(t)){if(!V.test(t))throw new Error(`Invalid base64 data: ${t}`)}else throw new TypeError(`Expected String but got ${g(t)}.`);let e=atob(t),r=Uint8Array.from([...e].map(n=>n.charCodeAt(0))),o=new Set(j),a;return r.every(n=>o.has(n))?a=e.replace(/\s/g,I):a=t,a},q=(t,e=0)=>{if(!d(t))throw new TypeError(`Expected String but got ${g(t)}.`);if(Number.isInteger(e)){if(e>x)throw new Error("Character references nested too deeply.")}else throw new TypeError(`Expected Number but got ${g(e)}.`);let r=decodeURIComponent(t);if(/&#/.test(r)){let o=new Set(j),a=[...r.matchAll(te)].reverse();for(let n of a){let[c,p]=n,m;if(/^x[\dA-F]+/i.test(p)?m=parseInt(`0${p}`,x):/^[\d]+/.test(p)&&(m=parseInt(p)),Number.isInteger(m)){let{index:l}=n,[s,i]=[r.substring(0,l),r.substring(l+c.length)];o.has(m)?(r=`${s}${String.fromCharCode(m)}${i}`,(/#x?$/.test(s)||/^#(?:x(?:00)?[2-7]|\d)/.test(i))&&(r=q(r,++e))):m<x*x&&(r=`${s}${i}`)}}}return r},A=class{#e;constructor(){this.#e=new Set(O)}get(){return[...this.#e]}has(e){return this.#e.has(e)}add(e){if(d(e)){if(v.test(e)||!se.test(e))throw new Error(`Invalid scheme: ${e}`)}else throw new TypeError(`Expected String but got ${g(e)}.`);return this.#e.add(e),[...this.#e]}remove(e){return this.#e.delete(e)}isURI(e){let r;if(d(e))try{let{protocol:o}=new URL(e),a=o.replace(/:$/,""),n=a.split("+");r=!v.test(a)&&re.test(a)||n.every(c=>this.#e.has(c))}catch{r=!1}return!!r}},L=class extends A{#e;#t;constructor(){super(),this.#e=0,this.#t=new Set}sanitize(e,r={allow:[],deny:[],only:[]}){if(this.#e>x)throw this.#e=0,new Error("Data URLs nested too deeply.");let{allow:o,deny:a,only:n}=r??{},c=new Map([["data",!1],["file",!1],["javascrpt",!1],["vbscript",!1]]),p=!1;if(Array.isArray(n)&&n.length){let l=super.get();for(let i of l)c.set(i,!1);let s=Object.values(n);for(let i of s)if(d(i)&&(i=i.trim(),!v.test(i))){if(super.has(i))c.set(i,!0);else{try{super.add(i)}catch{}super.has(i)&&c.set(i,!0)}!p&&c.has(i)&&(p=c.get(i))}}else{if(Array.isArray(o)&&o.length){let l=Object.values(o);for(let s of l)if(d(s)&&(s=s.trim(),!v.test(s)))if(super.has(s))c.set(s,!0);else{try{super.add(s)}catch{}super.has(s)&&c.set(s,!0)}}if(Array.isArray(a)&&a.length){let l=Object.values(a);for(let s of l)d(s)&&(s=s.trim(),s&&c.set(s,!1))}}let m;if(super.isURI(e)){let{hash:l,href:s,pathname:i,protocol:E,search:y}=new URL(e),b=E.replace(/:$/,""),S=b.split("+"),U;if(p)U=S.every(f=>c.get(f));else for(let[f,w]of c.entries())if(U=w||b!==f&&S.every(_=>_!==f),!U)break;if(U){let f,w=s;if(S.includes("data")){let[_,...Y]=i.split(","),k=`${Y.join(",")}${y}${l}`,R=_.split(";"),h=k;if(R[R.length-1]==="base64")R.pop(),h=oe(k);else try{let z=q(h),{protocol:C}=new URL(z.trim());C.replace(/:$/,"").split("+").some(u=>v.test(u))&&(w="")}catch{}let M=/data:[^,]*,/.test(h);if(h!==k||M){if(M){let C=[...h.matchAll(W)].reverse();for(let T of C){let[u]=T;G.test(u)&&([u]=G.exec(u)),this.#e++,this.#t.add(u);let P=this.sanitize(u,{allow:["data"]});if(P){let{index:D}=T,[X,J]=[h.substring(0,D),h.substring(D+u.length)];h=`${X}${P}${J}`}}this.#t.has(e)?this.#t.delete(e):f=!0}else this.#t.has(e)?this.#t.delete(e):f=!0;w=`${b}:${R.join(";")},${h}`}else this.#t.has(e)?this.#t.delete(e):f=!0}else f=!0;w?(m=w.replace(Z,I).replace(ie,N),f&&(m=m.replace(ee,N),this.#e=0)):(m=w,this.#e=0)}}return m||null}parse(e,r){if(!d(e))throw new TypeError(`Expected String but got ${g(e)}.`);let o=this.sanitize(e,r??{allow:["data","file"]}),a=new Map([["input",e]]);if(o){let n=new URL(o),{pathname:c,protocol:p}=n,m=p.replace(/:$/,"").split("+");if(a.set("valid",!0),m.includes("data")){let l=new Map,[s,...i]=c.split(","),E=`${i.join(",")}`,y=s.split(";"),b=y[y.length-1]==="base64";b&&y.pop(),l.set("mime",y.join(";")),l.set("base64",b),l.set("data",E),a.set("data",Object.fromEntries(l))}else a.set("data",null);for(let l in n){let s=n[l];typeof s!="function"&&a.set(l,s)}}else a.set("valid",!1);return Object.fromEntries(a)}},$=new L,H=t=>$.isURI(t),ne=async t=>await H(t),F=(t,e)=>{let r=$.parse(t,e??{allow:[],deny:[],only:[]}),o;if(r){let{href:a}=r;o=a}return o??null},ce=async(t,e)=>await F(t,e),B=t=>$.parse(t),pe=async t=>await B(t);export{$ as default,ne as isURI,H as isURISync,pe as parseURL,B as parseURLSync,ce as sanitizeURL,F as sanitizeURLSync};
//# sourceMappingURL=url-sanitizer.min.js.map

@@ -6,3 +6,9 @@ /**

export {
default, isURI, isURISync, sanitizeURL, sanitizeURLSync
default,
isURI,
isURISync,
parseURL,
parseURLSync,
sanitizeURL,
sanitizeURLSync
} from './src/mjs/uri-util.js';

@@ -32,2 +32,6 @@ {

},
"dependencies": {
"dompurify": "^2.4.3",
"jsdom": "^21.1.0"
},
"devDependencies": {

@@ -54,3 +58,3 @@ "@babel/eslint-parser": "^7.19.1",

},
"version": "0.5.5"
"version": "0.5.7"
}

@@ -125,14 +125,14 @@ # URL Sanitizer

* `data.data` **[string][1]** Data part of the data URL.
* `href` **[string][1]** Same as URL API.
* `origin` **[string][1]** Same as URL API.
* `protocol` **[string][1]** Same as URL API.
* `username` **[string][1]** Same as URL API.
* `password` **[string][1]** Same as URL API.
* `host` **[string][1]** Same as URL API.
* `hostname` **[string][1]** Same as URL API.
* `port` **[string][1]** Same as URL API.
* `pathname` **[string][1]** Same as URL API.
* `search` **[string][1]** Same as URL API.
* `searchParams` **[object][3]** Same as URL API.
* `hash` **[string][1]** Same as URL API.
* `href` **[string][1]** Sanitized URL input.
* `origin` **[string][1]** Scheme, domain and port of the sanitized URL.
* `protocol` **[string][1]** Protocol scheme of the sanitized URL.
* `username` **[string][1]** Username specified before the domain name.
* `password` **[string][1]** Password specified before the domain name.
* `host` **[string][1]** Domain and port of the sanitized URL.
* `hostname` **[string][1]** Domain of the sanitized URL.
* `port` **[string][1]** Port number of the sanitized URL.
* `pathname` **[string][1]** Path of the sanitized URL.
* `search` **[string][1]** Query string of the sanitized URL.
* `searchParams` **[object][3]** [URLSearchParams](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams) object.
* `hash` **[string][1]** Fragment identifier of the sanitized URL.

@@ -241,7 +241,9 @@ ```javascript

Get an array of URI schemes registered at [iana.org](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml "Uniform Resource Identifier (URI) Schemes").
* Historical schemes omitted.
* `moz-extension` scheme added.
Get a list of registered URI schemes.
Returns **[Array][4]<[string][1]>** Array of registered URI schemes.
* Includes schemes registered at [iana.org](https://www.iana.org/assignments/uri-schemes/uri-schemes.xhtml "Uniform Resource Identifier (URI) Schemes") by default.
* Historical schemes omitted.
* `moz-extension` scheme added.
* Also includes custom schemes added via [urlSanitizer.add()](#urlsanitizeraddscheme).

@@ -273,3 +275,3 @@ ```javascript

Add a scheme to the list of URI schemes.
Add a scheme to the list of registered URI schemes.
* `javascript` and `vbscript` schemes can not be registered. It throws.

@@ -296,3 +298,3 @@

Remove a scheme from the list of URI schemes.
Remove a scheme from the list of registered URI schemes.

@@ -322,2 +324,77 @@ #### Parameters

## Reactivate tags and purify DOM
It's outside the scope of URL Sanitizer, but escaped tags in sanitized data URL can be inconvenient.
For example, embedded SVG in a data URL.
For such cases, here is a sample code that reactivate tags and purify the DOM.
* [dompurify](https://www.npmjs.com/package/dompurify) is required.
* If you're using Node.js, [jsdom](https://www.npmjs.com/package/jsdom) is also required.
```shell
npm i dompurify jsdom
```
```javascript
import DOMPurify from 'dompurify';
import { JSDOM } from 'jsdom';
import { parseURL } from 'url-sanitizer';
const { window } = new JSDOM('');
const domPurify = DOMPurify(window);
/**
* get purified DOM from a data URL
*
* @param {string} url - URL input
* @returns {?string} - purified DOM, `null` if the given URL is not a data URL
*/
const getPurifiedDOMFromDataURL = async url => {
const parsedURL = await parseURL(url);
const { data: parsedDataURL } = parsedURL;
let purifiedDOM;
if (parsedDataURL) {
const { base64, data, mime } = parsedDataURL;
if (!base64 &&
/^(?:text\/(?:ht|x)ml|application\/(?:xhtml\+)?xml|image\/svg\+xml)/
.test(mime)) {
let parsedData = data;
const matchedHTMLChars =
parsedData.matchAll(/%26(?:(?:l|g|quo)t|%2339);?/g);
const items = [...matchedHTMLChars].reverse();
const unescapeURLEncodedHTMLChars = ch => {
let unescapedChar;
if (/%26lt;?/.test(ch)) {
unescapedChar = '<';
} else if (/%26gt;?/.test(ch)) {
unescapedChar = '>';
} else if (/%26quot;?/.test(ch)) {
unescapedChar = '"';
} else if (/%26%2339;?/.test(ch)) {
unescapedChar = "'";
} else {
unescapedChar = ch;
}
return unescapedChar;
};
for (const item of items) {
const [htmlChar] = item;
const { index } = item;
const [preHTMLChar, postHTMLChar] = [
parsedData.substring(0, index),
parsedData.substring(index + htmlChar.length)
];
const unescapedHTMLChar = unescapeURLEncodedHTMLChars(htmlChar);
parsedData = `${preHTMLChar}${unescapedHTMLChar}${postHTMLChar}`;
}
purifiedDOM = domPurify.sanitize(decodeURIComponent(parsedData));
}
}
return purifiedDOM ?? null;
};
```
---
## Acknowledgments

@@ -324,0 +401,0 @@

@@ -448,14 +448,14 @@ /**

* @property {string} data.data - data part of the data URL
* @property {string} href - same as URL API
* @property {string} origin - same as URL API
* @property {string} protocol - same as URL API
* @property {string} username - same as URL API
* @property {string} password - same as URL API
* @property {string} host - same as URL API
* @property {string} hostname - same as URL API
* @property {string} port - same as URL API
* @property {string} pathname - same as URL API
* @property {string} search - same as URL API
* @property {object} searchParams - same as URL API
* @property {string} hash - same as URL API
* @property {string} href - sanitized URL input
* @property {string} origin - scheme, domain and port of the sanitized URL
* @property {string} protocol - protocol scheme of the sanitized URL
* @property {string} username - username specified before the domain name
* @property {string} password - password specified before the domain name
* @property {string} host - domain and port of the sanitized URL
* @property {string} hostname - domain of the sanitized URL
* @property {string} port - port number of the sanitized URL
* @property {string} pathname - path of the sanitized URL
* @property {string} search - query string of the sanitized URL
* @property {object} searchParams - URLSearchParams object
* @property {string} hash - fragment identifier of the sanitized URL
*/

@@ -467,9 +467,10 @@

* @param {string} url - URL input
* @param {object} opt - options
* @returns {ParsedURL} - result with extended props based on URL API
*/
parse(url) {
parse(url, opt) {
if (!isString(url)) {
throw new TypeError(`Expected String but got ${getType(url)}.`);
}
const sanitizedUrl = this.sanitize(url, {
const sanitizedUrl = this.sanitize(url, opt ?? {
allow: ['data', 'file']

@@ -544,7 +545,15 @@ });

*/
const sanitizeUrl = (url, opt) => urlSanitizer.sanitize(url, opt ?? {
allow: [],
deny: [],
only: []
});
const sanitizeUrl = (url, opt) => {
const parsedUrl = urlSanitizer.parse(url, opt ?? {
allow: [],
deny: [],
only: []
});
let res;
if (parsedUrl) {
const { href } = parsedUrl;
res = href;
}
return res ?? null;
};

@@ -551,0 +560,0 @@ /**

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc