mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
volflag "nohtml" to never return html or rendered markdown from potentially unsafe volumes
This commit is contained in:
parent
551d99b71b
commit
50c7bba6ea
|
@ -1537,6 +1537,7 @@ some notes on hardening
|
||||||
|
|
||||||
* set `--rproxy 0` if your copyparty is directly facing the internet (not through a reverse-proxy)
|
* set `--rproxy 0` if your copyparty is directly facing the internet (not through a reverse-proxy)
|
||||||
* cors doesn't work right otherwise
|
* cors doesn't work right otherwise
|
||||||
|
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
|
||||||
|
|
||||||
safety profiles:
|
safety profiles:
|
||||||
|
|
||||||
|
|
|
@ -155,6 +155,7 @@ flagcats = {
|
||||||
"sb_lg": "enable js sandbox for prologue/epilogue (default)",
|
"sb_lg": "enable js sandbox for prologue/epilogue (default)",
|
||||||
"md_sbf": "list of markdown-sandbox safeguards to disable",
|
"md_sbf": "list of markdown-sandbox safeguards to disable",
|
||||||
"lg_sbf": "list of *logue-sandbox safeguards to disable",
|
"lg_sbf": "list of *logue-sandbox safeguards to disable",
|
||||||
|
"nohtml": "return html and markdown as text/html",
|
||||||
},
|
},
|
||||||
"others": {
|
"others": {
|
||||||
"fk=8": 'generates per-file accesskeys,\nwhich will then be required at the "g" permission',
|
"fk=8": 'generates per-file accesskeys,\nwhich will then be required at the "g" permission',
|
||||||
|
|
|
@ -137,6 +137,8 @@ class HttpCli(object):
|
||||||
self.uparam: dict[str, str] = {}
|
self.uparam: dict[str, str] = {}
|
||||||
self.cookies: dict[str, str] = {}
|
self.cookies: dict[str, str] = {}
|
||||||
self.avn: Optional[VFS] = None
|
self.avn: Optional[VFS] = None
|
||||||
|
self.vn = self.asrv.vfs
|
||||||
|
self.rem = " "
|
||||||
self.vpath = " "
|
self.vpath = " "
|
||||||
self.uname = " "
|
self.uname = " "
|
||||||
self.pw = " "
|
self.pw = " "
|
||||||
|
@ -437,6 +439,8 @@ class HttpCli(object):
|
||||||
avn.can_access("", self.uname) if avn else [False] * 6
|
avn.can_access("", self.uname) if avn else [False] * 6
|
||||||
)
|
)
|
||||||
self.avn = avn
|
self.avn = avn
|
||||||
|
self.vn = vn
|
||||||
|
self.rem = rem
|
||||||
|
|
||||||
self.s.settimeout(self.args.s_tbody or None)
|
self.s.settimeout(self.args.s_tbody or None)
|
||||||
|
|
||||||
|
@ -572,9 +576,8 @@ class HttpCli(object):
|
||||||
|
|
||||||
# default to utf8 html if no content-type is set
|
# default to utf8 html if no content-type is set
|
||||||
if not mime:
|
if not mime:
|
||||||
mime = self.out_headers.get("Content-Type", "text/html; charset=utf-8")
|
mime = self.out_headers.get("Content-Type") or "text/html; charset=utf-8"
|
||||||
|
|
||||||
assert mime
|
|
||||||
self.out_headers["Content-Type"] = mime
|
self.out_headers["Content-Type"] = mime
|
||||||
|
|
||||||
for k, zs in list(self.out_headers.items()) + self.out_headerlist:
|
for k, zs in list(self.out_headers.items()) + self.out_headerlist:
|
||||||
|
@ -773,9 +776,8 @@ class HttpCli(object):
|
||||||
t = "@{} has no access to [{}]"
|
t = "@{} has no access to [{}]"
|
||||||
self.log(t.format(self.uname, self.vpath))
|
self.log(t.format(self.uname, self.vpath))
|
||||||
|
|
||||||
if self.avn and "on403" in self.avn.flags:
|
if "on403" in self.vn.flags:
|
||||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
ret = self.on40x(self.vn.flags["on403"], self.vn, self.rem)
|
||||||
ret = self.on40x(vn.flags["on403"], vn, rem)
|
|
||||||
if ret == "true":
|
if ret == "true":
|
||||||
return True
|
return True
|
||||||
elif ret == "false":
|
elif ret == "false":
|
||||||
|
@ -1042,9 +1044,6 @@ class HttpCli(object):
|
||||||
|
|
||||||
from .dxml import mkenod, mktnod, parse_xml
|
from .dxml import mkenod, mktnod, parse_xml
|
||||||
|
|
||||||
self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
|
||||||
# abspath = vn.dcanonical(rem)
|
|
||||||
|
|
||||||
buf = b""
|
buf = b""
|
||||||
for rbuf in self.get_body_reader()[0]:
|
for rbuf in self.get_body_reader()[0]:
|
||||||
buf += rbuf
|
buf += rbuf
|
||||||
|
@ -1101,8 +1100,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
from .dxml import mkenod, mktnod, parse_xml
|
from .dxml import mkenod, mktnod, parse_xml
|
||||||
|
|
||||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
abspath = self.vn.dcanonical(self.rem)
|
||||||
abspath = vn.dcanonical(rem)
|
|
||||||
|
|
||||||
buf = b""
|
buf = b""
|
||||||
for rbuf in self.get_body_reader()[0]:
|
for rbuf in self.get_body_reader()[0]:
|
||||||
|
@ -1316,15 +1314,12 @@ class HttpCli(object):
|
||||||
plain = zb.decode("utf-8", "replace")
|
plain = zb.decode("utf-8", "replace")
|
||||||
if buf.startswith(b"msg="):
|
if buf.startswith(b"msg="):
|
||||||
plain = plain[4:]
|
plain = plain[4:]
|
||||||
vfs, rem = self.asrv.vfs.get(
|
xm = self.vn.flags.get("xm")
|
||||||
self.vpath, self.uname, False, False
|
|
||||||
)
|
|
||||||
xm = vfs.flags.get("xm")
|
|
||||||
if xm:
|
if xm:
|
||||||
runhook(
|
runhook(
|
||||||
self.log,
|
self.log,
|
||||||
xm,
|
xm,
|
||||||
vfs.canonical(rem),
|
self.vn.canonical(self.rem),
|
||||||
self.vpath,
|
self.vpath,
|
||||||
self.host,
|
self.host,
|
||||||
self.uname,
|
self.uname,
|
||||||
|
@ -2731,6 +2726,9 @@ class HttpCli(object):
|
||||||
else:
|
else:
|
||||||
mime = guess_mime(req_path)
|
mime = guess_mime(req_path)
|
||||||
|
|
||||||
|
if "nohtml" in self.vn.flags and "html" in mime:
|
||||||
|
mime = "text/plain; charset=utf-8"
|
||||||
|
|
||||||
self.out_headers["Accept-Ranges"] = "bytes"
|
self.out_headers["Accept-Ranges"] = "bytes"
|
||||||
self.send_headers(length=upper - lower, status=status, mime=mime)
|
self.send_headers(length=upper - lower, status=status, mime=mime)
|
||||||
|
|
||||||
|
@ -3400,7 +3398,8 @@ class HttpCli(object):
|
||||||
|
|
||||||
vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)])
|
vpnodes.append([quotep(vpath) + "/", html_escape(node, crlf=True)])
|
||||||
|
|
||||||
vn, rem = self.asrv.vfs.get(self.vpath, self.uname, False, False)
|
vn = self.vn
|
||||||
|
rem = self.rem
|
||||||
abspath = vn.dcanonical(rem)
|
abspath = vn.dcanonical(rem)
|
||||||
dbv, vrem = vn.get_dbv(rem)
|
dbv, vrem = vn.get_dbv(rem)
|
||||||
|
|
||||||
|
@ -3496,8 +3495,14 @@ class HttpCli(object):
|
||||||
self.log("wrong filekey, want {}, got {}".format(correct, got))
|
self.log("wrong filekey, want {}, got {}".format(correct, got))
|
||||||
return self.tx_404()
|
return self.tx_404()
|
||||||
|
|
||||||
if abspath.endswith(".md") and (
|
if (
|
||||||
"v" in self.uparam or "edit" in self.uparam or "edit2" in self.uparam
|
abspath.endswith(".md")
|
||||||
|
and "nohtml" not in vn.flags
|
||||||
|
and (
|
||||||
|
"v" in self.uparam
|
||||||
|
or "edit" in self.uparam
|
||||||
|
or "edit2" in self.uparam
|
||||||
|
)
|
||||||
):
|
):
|
||||||
return self.tx_md(abspath)
|
return self.tx_md(abspath)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue