diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 476c5ffb..70cb7522 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -804,13 +804,15 @@ class HttpCli(object): path_base = os.path.join(self.E.mod, "web") static_path = absreal(os.path.join(path_base, self.vpath[5:])) + if static_path in self.conn.hsrv.statics: + return self.tx_file(static_path) + if not static_path.startswith(path_base): t = "malicious user; attempted path traversal [{}] => [{}]" self.log(t.format(self.vpath, static_path), 1) - self.tx_404() - return False - return self.tx_file(static_path) + self.tx_404() + return False if "cf_challenge" in self.uparam: self.reply(self.j2s("cf").encode("utf-8", "replace")) diff --git a/copyparty/httpsrv.py b/copyparty/httpsrv.py index a9f55bc1..539238e3 100644 --- a/copyparty/httpsrv.py +++ b/copyparty/httpsrv.py @@ -55,7 +55,6 @@ except SyntaxError: ) sys.exit(1) -from .bos import bos from .httpconn import HttpConn from .u2idx import U2idx from .util import ( @@ -66,6 +65,7 @@ from .util import ( Magician, Netdev, NetMap, + absreal, ipnorm, min_ex, shut_socket, @@ -139,6 +139,9 @@ class HttpSrv(object): zs = os.path.join(self.E.mod, "web", "deps", "prism.js.gz") self.prism = os.path.exists(zs) + self.statics: set[str] = set() + self._build_statics() + self.ptn_cc = re.compile(r"[\x00-\x1f]") self.mallow = "GET HEAD POST PUT DELETE OPTIONS".split() @@ -171,6 +174,14 @@ class HttpSrv(object): except: pass + def _build_statics(self) -> None: + for dp, _, df in os.walk(os.path.join(self.E.mod, "web")): + for fn in df: + ap = absreal(os.path.join(dp, fn)) + self.statics.add(ap) + if ap.endswith(".gz") or ap.endswith(".br"): + self.statics.add(ap[:-3]) + def set_netdevs(self, netdevs: dict[str, Netdev]) -> None: ips = set() for ip, _ in self.bound: