diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 89cab889..28441114 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -33,6 +33,7 @@ from .util import ( afsenc, get_df, humansize, + json_hesc, min_ex, odfusion, read_utf8, @@ -2785,7 +2786,7 @@ class AuthSrv(object): "lifetime": vn.js_ls["lifetime"], "u2sort": self.args.u2sort, } - vn.js_htm = json.dumps(js_htm) + vn.js_htm = json_hesc(json.dumps(js_htm)) vols = list(vfs.all_nodes.values()) if enshare: diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index fa6d7418..bdbaced2 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -79,6 +79,7 @@ from .util import ( hidedir, html_bescape, html_escape, + html_sh_esc, humansize, ipnorm, json_hesc, @@ -4912,11 +4913,8 @@ class HttpCli(object): else: rip = host - # safer than html_escape/quotep since this avoids both XSS and shell-stuff - pw = re.sub(r"[<>&$?`\"']", "_", self.pw or "hunter2") - vp = re.sub(r"[<>&$?`\"']", "_", self.uparam["hc"] or "").lstrip("/") - pw = pw.replace(" ", "%20") - vp = vp.replace(" ", "%20") + vp = (self.uparam["hc"] or "").lstrip("/") + pw = self.pw or "hunter2" if pw in self.asrv.sesa: pw = "hunter2" @@ -4925,14 +4923,14 @@ class HttpCli(object): args=self.args, accs=bool(self.asrv.acct), s="s" if self.is_https else "", - rip=rip, - ep=ep, - vp=vp, - rvp=vjoin(self.args.R, vp), - host=host, - hport=hport, + rip=html_sh_esc(rip), + ep=html_sh_esc(ep), + vp=html_sh_esc(vp), + rvp=html_sh_esc(vjoin(self.args.R, vp)), + host=html_sh_esc(host), + hport=html_sh_esc(hport), aname=aname, - pw=pw, + pw=html_sh_esc(pw), ) self.reply(html.encode("utf-8")) return True diff --git a/copyparty/util.py b/copyparty/util.py index 94e0f249..d081378a 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -244,6 +244,7 @@ except: RE_ANSI = re.compile("\033\\[[^mK]*[mK]") +RE_HTML_SH = re.compile(r"[<>&$?`\"';]") RE_CTYPE = re.compile(r"^content-type: *([^; ]+)", re.IGNORECASE) RE_CDISP = re.compile(r"^content-disposition: *([^; ]+)", re.IGNORECASE) RE_CDISP_FIELD = re.compile( @@ -2253,6 +2254,12 @@ def find_prefix(ips: list[str], cidrs: list[str]) -> list[str]: return ret +def html_sh_esc(s: str) -> str: + s = re.sub(RE_HTML_SH, "_", s).replace(" ", "%20") + s = s.replace("\r", "_").replace("\n", "_") + return s + + def json_hesc(s: str) -> str: return s.replace("<", "\\u003c").replace(">", "\\u003e").replace("&", "\\u0026") diff --git a/copyparty/web/browser.html b/copyparty/web/browser.html index a642e9a9..05bdacbc 100644 --- a/copyparty/web/browser.html +++ b/copyparty/web/browser.html @@ -109,8 +109,8 @@ {%- for f in files %} {{ f.lead }}{{ f.name|e }}{{ f.sz }} {%- if f.tags is defined %} - {%- for k in taglist %}{{ f.tags[k] }}{%- endfor %} -{%- endif %}{{ f.ext }}{{ f.dt }} + {%- for k in taglist %}{{ f.tags[k]|e }}{%- endfor %} +{%- endif %}{{ f.ext|e }}{{ f.dt }} {%- endfor %}