diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index c20c9d09..2c8f23d8 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -498,8 +498,8 @@ class VFS(object): t = "{} has no {} in [{}] => [{}] => [{}]" self.log("vfs", t.format(uname, msg, vpath, cvpath, ap), 6) - t = "you don't have {}-access for this location" - raise Pebkac(err, t.format(msg)) + t = 'you don\'t have %s-access in "/%s"' + raise Pebkac(err, t % (msg, cvpath)) return vn, rem diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 23c816da..0c2a76e2 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -2175,26 +2175,30 @@ class HttpCli(object): new_dir = self.parser.require("name", 512) self.parser.drop() - sanitized = sanitize_fn(new_dir, "", []) - return self._mkdir(vjoin(self.vpath, sanitized)) + return self._mkdir(vjoin(self.vpath, new_dir)) def _mkdir(self, vpath: str, dav: bool = False) -> bool: nullwrite = self.args.nw + self.gctx = vpath + vpath = undot(vpath) vfs, rem = self.asrv.vfs.get(vpath, self.uname, False, True) - self._assert_safe_rem(rem) + rem = sanitize_vpath(rem, "/", []) fn = vfs.canonical(rem) + if not fn.startswith(vfs.realpath): + self.log("invalid mkdir [%s] [%s]" % (self.gctx, vpath), 1) + raise Pebkac(422) if not nullwrite: fdir = os.path.dirname(fn) - if not bos.path.isdir(fdir): + if dav and not bos.path.isdir(fdir): raise Pebkac(409, "parent folder does not exist") if bos.path.isdir(fn): - raise Pebkac(405, "that folder exists already") + raise Pebkac(405, 'folder "/%s" already exists' % (vpath,)) try: - bos.mkdir(fn) + bos.makedirs(fn) except OSError as ex: if ex.errno == errno.EACCES: raise Pebkac(500, "the server OS denied write-access") @@ -2203,7 +2207,7 @@ class HttpCli(object): except: raise Pebkac(500, min_ex()) - self.out_headers["X-New-Dir"] = quotep(vpath.split("/")[-1]) + self.out_headers["X-New-Dir"] = quotep(vpath) if dav: self.reply(b"", 201) diff --git a/copyparty/util.py b/copyparty/util.py index d2ea0db2..297972a2 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -1773,6 +1773,12 @@ def sanitize_fn(fn: str, ok: str, bad: list[str]) -> str: return fn.strip() +def sanitize_vpath(vp: str, ok: str, bad: list[str]) -> str: + parts = vp.replace(os.sep, "/").split("/") + ret = [sanitize_fn(x, ok, bad) for x in parts] + return "/".join(ret) + + def relchk(rp: str) -> str: if "\x00" in rp: return "[nul]" diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 878a5f73..aad8b0e3 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -7200,8 +7200,8 @@ var msel = (function () { sf.textContent = ''; var dn = this.getResponseHeader('X-New-Dir'); - dn = dn || uricom_enc(this.dn); - treectl.goto(this.vp + dn + '/', true); + dn = dn ? '/' + dn + '/' : uricom_enc(this.dn); + treectl.goto(dn, true); } })();