From b8adeb824a2291d6351408ded2bb7762d7bc5438 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 31 Aug 2023 21:51:58 +0000 Subject: [PATCH] misc http correctness; some of this looks shady af but appears to have been harmless (decent amount of testing came out ok) * some location normalization happened before unquoting; however vfs handled this correctly so the outcome was just confusing messages * some url parameters were double-decoded (unpost filter, move destinations), causing some operations to fail unexpectedly * invalid cache-control headers could be generated, but not in a maliciously-beneficial way (there are safeguards stripping newlines and control-characters) also adds an exception-message cleanup step to strip away the filesystem path that copyparty's python files are located at, in case that could be interesting knowledge --- copyparty/httpcli.py | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index bf6e7747..f87d0ec1 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -333,10 +333,12 @@ class HttpCli(object): # split req into vpath + uparam uparam = {} if "?" not in self.req: - self.trailing_slash = self.req.endswith("/") - vpath = undot(self.req) + vpath = unquotep(self.req) # not query, so + means + + self.trailing_slash = vpath.endswith("/") + vpath = undot(vpath) else: vpath, arglist = self.req.split("?", 1) + vpath = unquotep(vpath) self.trailing_slash = vpath.endswith("/") vpath = undot(vpath) @@ -351,6 +353,8 @@ class HttpCli(object): for k in arglist.split("&"): if "=" in k: k, zs = k.split("=", 1) + # x-www-form-urlencoded (url query part) uses + # either + or %20 for 0x20 so handle both uparam[k.lower()] = unquotep(zs.strip().replace("+", " ")) else: uparam[k.lower()] = "" @@ -385,7 +389,7 @@ class HttpCli(object): self.uparam = uparam self.cookies = cookies - self.vpath = unquotep(vpath) # not query, so + means + + self.vpath = vpath self.vpaths = ( self.vpath + "/" if self.trailing_slash and self.vpath else self.vpath ) @@ -564,8 +568,8 @@ class HttpCli(object): self.out_headers.update(NO_CACHE) return - n = "604869" if cache == "i" else cache or "69" - self.out_headers["Cache-Control"] = "max-age=" + n + n = 69 if not cache else 604869 if cache == "i" else int(cache) + self.out_headers["Cache-Control"] = "max-age=" + str(n) def k304(self) -> bool: k304 = self.cookies.get("k304") @@ -668,6 +672,11 @@ class HttpCli(object): if volsan: vols = list(self.asrv.vfs.all_vols.values()) body = vol_san(vols, body) + try: + zs = absreal(__file__).rsplit(os.path.sep, 2)[0] + body = body.replace(zs.encode("utf-8"), b"PP") + except: + pass self.send_headers(len(body), status, mime, headers) @@ -3350,8 +3359,7 @@ class HttpCli(object): if not idx or not hasattr(idx, "p_end"): raise Pebkac(500, "sqlite3 is not available on the server; cannot unpost") - filt = self.uparam.get("filter") - filt = unquotep(filt or "") + filt = self.uparam.get("filter") or "" lm = "ups [{}]".format(filt) self.log(lm) @@ -3449,9 +3457,6 @@ class HttpCli(object): if not dst: raise Pebkac(400, "need dst vpath") - # x-www-form-urlencoded (url query part) uses - # either + or %20 for 0x20 so handle both - dst = unquotep(dst.replace("+", " ")) return self._mv(self.vpath, dst.lstrip("/")) def _mv(self, vsrc: str, vdst: str) -> bool: