mirror of
https://github.com/9001/copyparty.git
synced 2026-04-12 15:22:32 -06:00
fix GHSA-m6hv-x64c-27mm: svg nohtml
This commit is contained in:
parent
981a7cd9dd
commit
1c9f894e14
|
|
@ -2907,7 +2907,9 @@ some notes on hardening
|
||||||
* set `--rproxy 0` *if and only if* your copyparty is directly facing the internet (not through a reverse-proxy)
|
* set `--rproxy 0` *if and only 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`
|
* if you allow anonymous uploads or otherwise don't trust the contents of a volume, you can prevent XSS with volflag `nohtml`
|
||||||
* this returns html documents as plaintext, and also disables markdown rendering
|
* this returns html documents and svg images as plaintext, and also disables markdown rendering
|
||||||
|
* the `nohtml` volflag also enables `noscript` which, on its own, prevents *most* javascript from running; enabling just `noscript` without `nohtml` makes it probably-safe (see below) to view html and svg files, but `nohtml` is necessary to block javascript in markdown documents
|
||||||
|
* "probably-safe" because it relies on `Content-Security-Policy` so it depends on the reverseproxy to forward it, and the browser to understand it, but `nohtml` (the nuclear option) always works
|
||||||
* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or [`--help-bind`](https://copyparty.eu/cli/#bind-help-page)
|
* when running behind a reverse-proxy, listen on a unix-socket for tighter access control (and more performance); see [reverse-proxy](#reverse-proxy) or [`--help-bind`](https://copyparty.eu/cli/#bind-help-page)
|
||||||
|
|
||||||
safety profiles:
|
safety profiles:
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ def say_no():
|
||||||
|
|
||||||
|
|
||||||
def main(cli, vn, rem):
|
def main(cli, vn, rem):
|
||||||
cli.send_headers(None, 404, "text/plain")
|
cli.send_headers("oh_f", None, 404, "text/plain")
|
||||||
|
|
||||||
for chunk in say_no():
|
for chunk in say_no():
|
||||||
cli.s.sendall(chunk)
|
cli.s.sendall(chunk)
|
||||||
|
|
|
||||||
|
|
@ -1074,7 +1074,12 @@ class AuthSrv(object):
|
||||||
self.indent = ""
|
self.indent = ""
|
||||||
self.is_lxc = args.c == ["/z/initcfg"]
|
self.is_lxc = args.c == ["/z/initcfg"]
|
||||||
|
|
||||||
|
oh = "X-Content-Type-Options: nosniff\r\n"
|
||||||
|
if self.args.http_vary:
|
||||||
|
oh += "Vary: %s\r\n" % (self.args.http_vary,)
|
||||||
self._vf0b = {
|
self._vf0b = {
|
||||||
|
"oh_g": oh + "\r\n",
|
||||||
|
"oh_f": oh + "\r\n",
|
||||||
"cachectl": self.args.cachectl,
|
"cachectl": self.args.cachectl,
|
||||||
"tcolor": self.args.tcolor,
|
"tcolor": self.args.tcolor,
|
||||||
"du_iwho": self.args.du_iwho,
|
"du_iwho": self.args.du_iwho,
|
||||||
|
|
@ -2634,8 +2639,17 @@ class AuthSrv(object):
|
||||||
if head_s and not head_s.endswith("\n"):
|
if head_s and not head_s.endswith("\n"):
|
||||||
head_s += "\n"
|
head_s += "\n"
|
||||||
|
|
||||||
|
zs = "X-Content-Type-Options: nosniff\r\n"
|
||||||
if "norobots" in vol.flags:
|
if "norobots" in vol.flags:
|
||||||
head_s += META_NOBOTS
|
head_s += META_NOBOTS
|
||||||
|
zs += "X-Robots-Tag: noindex, nofollow\r\n"
|
||||||
|
if self.args.http_vary:
|
||||||
|
zs += "Vary: %s\r\n" % (self.args.http_vary,)
|
||||||
|
vol.flags["oh_g"] = zs + "\r\n"
|
||||||
|
|
||||||
|
if "noscript" in vol.flags:
|
||||||
|
zs += "Content-Security-Policy: script-src 'none';\r\n"
|
||||||
|
vol.flags["oh_f"] = zs + "\r\n"
|
||||||
|
|
||||||
ico_url = vol.flags.get("ufavico")
|
ico_url = vol.flags.get("ufavico")
|
||||||
if ico_url:
|
if ico_url:
|
||||||
|
|
|
||||||
|
|
@ -363,6 +363,7 @@ flagcats = {
|
||||||
"md_sba": "value of iframe allow-prop for markdown-sandbox",
|
"md_sba": "value of iframe allow-prop for markdown-sandbox",
|
||||||
"lg_sba": "value of iframe allow-prop for *logue-sandbox",
|
"lg_sba": "value of iframe allow-prop for *logue-sandbox",
|
||||||
"nohtml": "return html and markdown as text/html",
|
"nohtml": "return html and markdown as text/html",
|
||||||
|
"noscript": "disable most javascript by use of CSP",
|
||||||
"ui_noacci": "hide account-info in the UI",
|
"ui_noacci": "hide account-info in the UI",
|
||||||
"ui_nocpla": "hide cpanel-link in the UI",
|
"ui_nocpla": "hide cpanel-link in the UI",
|
||||||
"ui_nolbar": "hide link-bar in the UI",
|
"ui_nolbar": "hide link-bar in the UI",
|
||||||
|
|
|
||||||
|
|
@ -47,6 +47,7 @@ from .util import (
|
||||||
E_SCK_WR,
|
E_SCK_WR,
|
||||||
HAVE_SQLITE3,
|
HAVE_SQLITE3,
|
||||||
HTTPCODE,
|
HTTPCODE,
|
||||||
|
SAFE_MIMES,
|
||||||
UTC,
|
UTC,
|
||||||
VPTL_MAC,
|
VPTL_MAC,
|
||||||
VPTL_OS,
|
VPTL_OS,
|
||||||
|
|
@ -105,6 +106,7 @@ from .util import (
|
||||||
runhook,
|
runhook,
|
||||||
s2hms,
|
s2hms,
|
||||||
s3enc,
|
s3enc,
|
||||||
|
safe_mime,
|
||||||
sanitize_fn,
|
sanitize_fn,
|
||||||
sanitize_vpath,
|
sanitize_vpath,
|
||||||
sendfile_kern,
|
sendfile_kern,
|
||||||
|
|
@ -332,7 +334,6 @@ class HttpCli(object):
|
||||||
def run(self) -> bool:
|
def run(self) -> bool:
|
||||||
"""returns true if connection can be reused"""
|
"""returns true if connection can be reused"""
|
||||||
self.out_headers = {
|
self.out_headers = {
|
||||||
"Vary": self.args.http_vary,
|
|
||||||
"Cache-Control": "no-store, max-age=0",
|
"Cache-Control": "no-store, max-age=0",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -855,9 +856,6 @@ class HttpCli(object):
|
||||||
|
|
||||||
self.s.settimeout(self.args.s_tbody or None)
|
self.s.settimeout(self.args.s_tbody or None)
|
||||||
|
|
||||||
if "norobots" in vn.flags:
|
|
||||||
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
|
||||||
|
|
||||||
if "html_head_s" in vn.flags:
|
if "html_head_s" in vn.flags:
|
||||||
self.html_head += vn.flags["html_head_s"]
|
self.html_head += vn.flags["html_head_s"]
|
||||||
|
|
||||||
|
|
@ -1072,6 +1070,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
def send_headers(
|
def send_headers(
|
||||||
self,
|
self,
|
||||||
|
oh_k: str,
|
||||||
length: Optional[int],
|
length: Optional[int],
|
||||||
status: int = 200,
|
status: int = 200,
|
||||||
mime: Optional[str] = None,
|
mime: Optional[str] = None,
|
||||||
|
|
@ -1114,7 +1113,11 @@ class HttpCli(object):
|
||||||
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
|
self.cbonk(self.conn.hsrv.gmal, zs, "cc_hdr", "Cc in out-hdr")
|
||||||
raise Pebkac(999)
|
raise Pebkac(999)
|
||||||
|
|
||||||
|
response.append(self.vn.flags[oh_k])
|
||||||
|
|
||||||
if self.args.ohead and self.do_log:
|
if self.args.ohead and self.do_log:
|
||||||
|
zs = response.pop()[:-4]
|
||||||
|
response.extend(zs.split("\r\n"))
|
||||||
keys = self.args.ohead
|
keys = self.args.ohead
|
||||||
if "*" in keys:
|
if "*" in keys:
|
||||||
lines = response[1:]
|
lines = response[1:]
|
||||||
|
|
@ -1126,8 +1129,8 @@ class HttpCli(object):
|
||||||
for zs in lines:
|
for zs in lines:
|
||||||
hk, hv = zs.split(": ")
|
hk, hv = zs.split(": ")
|
||||||
self.log("[O] {}: \033[33m[{}]".format(hk, hv), 5)
|
self.log("[O] {}: \033[33m[{}]".format(hk, hv), 5)
|
||||||
|
response.append("\r\n")
|
||||||
|
|
||||||
response.append("\r\n")
|
|
||||||
try:
|
try:
|
||||||
self.s.sendall("\r\n".join(response).encode("utf-8"))
|
self.s.sendall("\r\n".join(response).encode("utf-8"))
|
||||||
except:
|
except:
|
||||||
|
|
@ -1184,7 +1187,7 @@ class HttpCli(object):
|
||||||
except:
|
except:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
self.send_headers(len(body), status, mime, headers)
|
self.send_headers("oh_g", len(body), status, mime, headers)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if self.mode != "HEAD":
|
if self.mode != "HEAD":
|
||||||
|
|
@ -1389,7 +1392,7 @@ class HttpCli(object):
|
||||||
if res_path in RES:
|
if res_path in RES:
|
||||||
ap = self.E.mod_ + res_path
|
ap = self.E.mod_ + res_path
|
||||||
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
|
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
|
||||||
return self.tx_file(ap)
|
return self.tx_file("oh_g", ap)
|
||||||
else:
|
else:
|
||||||
return self.tx_res(res_path)
|
return self.tx_res(res_path)
|
||||||
|
|
||||||
|
|
@ -1403,7 +1406,7 @@ class HttpCli(object):
|
||||||
# return mimetype matching request extension
|
# return mimetype matching request extension
|
||||||
self.ouparam["dl"] = res_path.split("/")[-1]
|
self.ouparam["dl"] = res_path.split("/")[-1]
|
||||||
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
|
if bos.path.exists(ap) or bos.path.exists(ap + ".gz"):
|
||||||
return self.tx_file(ap)
|
return self.tx_file("oh_g", ap)
|
||||||
else:
|
else:
|
||||||
return self.tx_res(res_path)
|
return self.tx_res(res_path)
|
||||||
|
|
||||||
|
|
@ -1717,7 +1720,10 @@ class HttpCli(object):
|
||||||
if zi.file_size >= maxsz:
|
if zi.file_size >= maxsz:
|
||||||
raise Pebkac(404, "zip bomb defused")
|
raise Pebkac(404, "zip bomb defused")
|
||||||
with zf.open(zi, "r") as fi:
|
with zf.open(zi, "r") as fi:
|
||||||
self.send_headers(length=zi.file_size, mime=guess_mime(inner_path))
|
mime = guess_mime(inner_path)
|
||||||
|
if mime not in SAFE_MIMES and "nohtml" in self.vn.flags:
|
||||||
|
mime = safe_mime(mime)
|
||||||
|
self.send_headers("oh_f", length=zi.file_size, mime=mime)
|
||||||
|
|
||||||
sendfile_py(
|
sendfile_py(
|
||||||
self.log,
|
self.log,
|
||||||
|
|
@ -1913,7 +1919,11 @@ class HttpCli(object):
|
||||||
chunksz = 0x7FF8 # preferred by nginx or cf (dunno which)
|
chunksz = 0x7FF8 # preferred by nginx or cf (dunno which)
|
||||||
|
|
||||||
self.send_headers(
|
self.send_headers(
|
||||||
None, 207, "text/xml; charset=" + enc, {"Transfer-Encoding": "chunked"}
|
"oh_f",
|
||||||
|
None,
|
||||||
|
207,
|
||||||
|
"text/xml; charset=" + enc,
|
||||||
|
{"Transfer-Encoding": "chunked"},
|
||||||
)
|
)
|
||||||
|
|
||||||
ap = ""
|
ap = ""
|
||||||
|
|
@ -2120,7 +2130,7 @@ class HttpCli(object):
|
||||||
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
self.log("%s tried to lock %r" % (self.uname, "/" + self.vpath))
|
||||||
raise Pebkac(401, "authenticate")
|
raise Pebkac(401, "authenticate")
|
||||||
|
|
||||||
self.send_headers(None, 204)
|
self.send_headers("oh_f", None, 204)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def handle_mkcol(self) -> bool:
|
def handle_mkcol(self) -> bool:
|
||||||
|
|
@ -2222,7 +2232,7 @@ class HttpCli(object):
|
||||||
oh["Ms-Author-Via"] = "DAV"
|
oh["Ms-Author-Via"] = "DAV"
|
||||||
|
|
||||||
# winxp-webdav doesnt know what 204 is
|
# winxp-webdav doesnt know what 204 is
|
||||||
self.send_headers(0, 200)
|
self.send_headers("oh_f", 0, 200)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def handle_delete(self) -> bool:
|
def handle_delete(self) -> bool:
|
||||||
|
|
@ -4525,11 +4535,11 @@ class HttpCli(object):
|
||||||
if self.do_log:
|
if self.do_log:
|
||||||
self.log(logmsg)
|
self.log(logmsg)
|
||||||
|
|
||||||
self.send_headers(length=file_sz, status=status, mime=mime)
|
self.send_headers("oh_g", length=file_sz, status=status, mime=mime)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
ret = True
|
ret = True
|
||||||
self.send_headers(length=file_sz, status=status, mime=mime)
|
self.send_headers("oh_g", length=file_sz, status=status, mime=mime)
|
||||||
remains = sendfile_py(
|
remains = sendfile_py(
|
||||||
self.log,
|
self.log,
|
||||||
0,
|
0,
|
||||||
|
|
@ -4554,7 +4564,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
def tx_file(self, req_path: str, ptop: Optional[str] = None) -> bool:
|
def tx_file(self, oh_k: str, req_path: str, ptop: Optional[str] = None) -> bool:
|
||||||
status = 200
|
status = 200
|
||||||
logmsg = "{:4} {} ".format("", self.req)
|
logmsg = "{:4} {} ".format("", self.req)
|
||||||
logtail = ""
|
logtail = ""
|
||||||
|
|
@ -4757,8 +4767,8 @@ class HttpCli(object):
|
||||||
else:
|
else:
|
||||||
mime = guess_mime(cdis)
|
mime = guess_mime(cdis)
|
||||||
|
|
||||||
if "nohtml" in self.vn.flags and "html" in mime:
|
if mime not in SAFE_MIMES and "nohtml" in self.vn.flags:
|
||||||
mime = "text/plain; charset=utf-8"
|
mime = safe_mime(mime)
|
||||||
|
|
||||||
self.out_headers["Accept-Ranges"] = "bytes"
|
self.out_headers["Accept-Ranges"] = "bytes"
|
||||||
logmsg += unicode(status) + logtail
|
logmsg += unicode(status) + logtail
|
||||||
|
|
@ -4767,7 +4777,7 @@ class HttpCli(object):
|
||||||
if self.do_log:
|
if self.do_log:
|
||||||
self.log(logmsg)
|
self.log(logmsg)
|
||||||
|
|
||||||
self.send_headers(length=upper - lower, status=status, mime=mime)
|
self.send_headers(oh_k, length=upper - lower, status=status, mime=mime)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
dls = self.conn.hsrv.dls
|
dls = self.conn.hsrv.dls
|
||||||
|
|
@ -4799,7 +4809,7 @@ class HttpCli(object):
|
||||||
|
|
||||||
ret = True
|
ret = True
|
||||||
with open_func(*open_args) as f:
|
with open_func(*open_args) as f:
|
||||||
self.send_headers(length=upper - lower, status=status, mime=mime)
|
self.send_headers(oh_k, length=upper - lower, status=status, mime=mime)
|
||||||
|
|
||||||
sendfun = sendfile_kern if use_sendfile else sendfile_py
|
sendfun = sendfile_kern if use_sendfile else sendfile_py
|
||||||
remains = sendfun(
|
remains = sendfun(
|
||||||
|
|
@ -4832,7 +4842,7 @@ class HttpCli(object):
|
||||||
mime: str,
|
mime: str,
|
||||||
) -> None:
|
) -> None:
|
||||||
vf = self.vn.flags
|
vf = self.vn.flags
|
||||||
self.send_headers(length=None, status=status, mime=mime)
|
self.send_headers("oh_f", length=None, status=status, mime=mime)
|
||||||
abspath: bytes = open_args[0]
|
abspath: bytes = open_args[0]
|
||||||
sec_rate = vf["tail_rate"]
|
sec_rate = vf["tail_rate"]
|
||||||
sec_max = vf["tail_tmax"]
|
sec_max = vf["tail_tmax"]
|
||||||
|
|
@ -4972,7 +4982,7 @@ class HttpCli(object):
|
||||||
logmsg: str,
|
logmsg: str,
|
||||||
) -> bool:
|
) -> bool:
|
||||||
M = 1048576
|
M = 1048576
|
||||||
self.send_headers(length=upper - lower, status=status, mime=mime)
|
self.send_headers("oh_f", length=upper - lower, status=status, mime=mime)
|
||||||
wr_slp = self.args.s_wr_slp
|
wr_slp = self.args.s_wr_slp
|
||||||
wr_sz = self.args.s_wr_sz
|
wr_sz = self.args.s_wr_sz
|
||||||
file_size = job["size"]
|
file_size = job["size"]
|
||||||
|
|
@ -5185,7 +5195,9 @@ class HttpCli(object):
|
||||||
|
|
||||||
cdis = gen_content_disposition("%s.%s" % (fn, ext))
|
cdis = gen_content_disposition("%s.%s" % (fn, ext))
|
||||||
self.log(repr(cdis))
|
self.log(repr(cdis))
|
||||||
self.send_headers(None, mime=mime, headers={"Content-Disposition": cdis})
|
self.send_headers(
|
||||||
|
"oh_f", None, mime=mime, headers={"Content-Disposition": cdis}
|
||||||
|
)
|
||||||
|
|
||||||
fgen = vn.zipgen(vpath, rem, set(items), self.uname, False, dots, scandir)
|
fgen = vn.zipgen(vpath, rem, set(items), self.uname, False, dots, scandir)
|
||||||
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
# for f in fgen: print(repr({k: f[k] for k in ["vp", "ap"]}))
|
||||||
|
|
@ -5393,7 +5405,7 @@ class HttpCli(object):
|
||||||
if len(html) != 2:
|
if len(html) != 2:
|
||||||
raise Exception("boundary appears in " + tpl)
|
raise Exception("boundary appears in " + tpl)
|
||||||
|
|
||||||
self.send_headers(sz_md + len(html[0]) + len(html[1]), status)
|
self.send_headers("oh_g", sz_md + len(html[0]) + len(html[1]), status)
|
||||||
|
|
||||||
logmsg += unicode(status)
|
logmsg += unicode(status)
|
||||||
if self.mode == "HEAD" or not do_send:
|
if self.mode == "HEAD" or not do_send:
|
||||||
|
|
@ -6766,7 +6778,7 @@ class HttpCli(object):
|
||||||
add_og = True
|
add_og = True
|
||||||
og_fn = ""
|
og_fn = ""
|
||||||
|
|
||||||
if "b" in self.uparam:
|
if "b" in self.uparam and "norobots" not in vn.flags:
|
||||||
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
self.out_headers["X-Robots-Tag"] = "noindex, nofollow"
|
||||||
|
|
||||||
is_dir = stat.S_ISDIR(st.st_mode)
|
is_dir = stat.S_ISDIR(st.st_mode)
|
||||||
|
|
@ -6838,7 +6850,7 @@ class HttpCli(object):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
if thp:
|
if thp:
|
||||||
return self.tx_file(thp)
|
return self.tx_file("oh_f", thp)
|
||||||
|
|
||||||
if th_fmt == "p":
|
if th_fmt == "p":
|
||||||
raise Pebkac(404)
|
raise Pebkac(404)
|
||||||
|
|
@ -6927,9 +6939,9 @@ class HttpCli(object):
|
||||||
|
|
||||||
if not add_og or not og_fn:
|
if not add_og or not og_fn:
|
||||||
if st.st_size or "nopipe" in vn.flags:
|
if st.st_size or "nopipe" in vn.flags:
|
||||||
return self.tx_file(abspath, None)
|
return self.tx_file("oh_f", abspath, None)
|
||||||
else:
|
else:
|
||||||
return self.tx_file(abspath, vn.get_dbv("")[0].realpath)
|
return self.tx_file("oh_f", abspath, vn.get_dbv("")[0].realpath)
|
||||||
|
|
||||||
elif is_dir and not self.can_read:
|
elif is_dir and not self.can_read:
|
||||||
if use_dirkey:
|
if use_dirkey:
|
||||||
|
|
@ -7277,7 +7289,7 @@ class HttpCli(object):
|
||||||
return self.redirect(
|
return self.redirect(
|
||||||
self.vpath + "/", flavor="redirecting to", use302=True
|
self.vpath + "/", flavor="redirecting to", use302=True
|
||||||
)
|
)
|
||||||
return self.tx_file(ap) # is no-cache
|
return self.tx_file("oh_f", ap) # is no-cache
|
||||||
|
|
||||||
if icur:
|
if icur:
|
||||||
mte = vn.flags.get("mte") or {}
|
mte = vn.flags.get("mte") or {}
|
||||||
|
|
|
||||||
|
|
@ -1154,7 +1154,7 @@ class Up2k(object):
|
||||||
ft = "\033[0;32m{}{:.0}"
|
ft = "\033[0;32m{}{:.0}"
|
||||||
ff = "\033[0;35m{}{:.0}"
|
ff = "\033[0;35m{}{:.0}"
|
||||||
fv = "\033[0;36m{}:\033[90m{}"
|
fv = "\033[0;36m{}:\033[90m{}"
|
||||||
zs = "bcasechk du_iwho emb_all emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t rw_edit_set srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
zs = "bcasechk du_iwho emb_all emb_lgs emb_mds ext_th_d html_head html_head_d html_head_s ls_q_m put_name2 mv_re_r mv_re_t rm_re_r rm_re_t oh_f oh_g rw_edit_set srch_re_dots srch_re_nodot zipmax zipmaxn_v zipmaxs_v"
|
||||||
fx = set(zs.split())
|
fx = set(zs.split())
|
||||||
fd = vf_bmap()
|
fd = vf_bmap()
|
||||||
fd.update(vf_cmap())
|
fd.update(vf_cmap())
|
||||||
|
|
|
||||||
|
|
@ -413,6 +413,7 @@ IMPLICATIONS = [
|
||||||
["tftpvv", "tftpv"],
|
["tftpvv", "tftpv"],
|
||||||
["nodupem", "nodupe"],
|
["nodupem", "nodupe"],
|
||||||
["no_dupe_m", "no_dupe"],
|
["no_dupe_m", "no_dupe"],
|
||||||
|
["nohtml", "noscript"],
|
||||||
["sftpvv", "sftpv"],
|
["sftpvv", "sftpv"],
|
||||||
["smbw", "smb"],
|
["smbw", "smb"],
|
||||||
["smb1", "smb"],
|
["smb1", "smb"],
|
||||||
|
|
@ -474,7 +475,7 @@ MIMES = {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def _add_mimes() -> None:
|
def _add_mimes() -> set[str]:
|
||||||
# `mimetypes` is woefully unpopulated on windows
|
# `mimetypes` is woefully unpopulated on windows
|
||||||
# but will be used as fallback on linux
|
# but will be used as fallback on linux
|
||||||
|
|
||||||
|
|
@ -511,8 +512,11 @@ font ttc=collection
|
||||||
ext, mime = em.split("=")
|
ext, mime = em.split("=")
|
||||||
MIMES[ext] = "{}/{}".format(k, mime)
|
MIMES[ext] = "{}/{}".format(k, mime)
|
||||||
|
|
||||||
|
ptn = re.compile("html|script|tension|wasm|xml")
|
||||||
|
return {x for x in MIMES.values() if not ptn.search(x)}
|
||||||
|
|
||||||
_add_mimes()
|
|
||||||
|
SAFE_MIMES = _add_mimes()
|
||||||
|
|
||||||
|
|
||||||
EXTS: dict[str, str] = {v: k for k, v in MIMES.items()}
|
EXTS: dict[str, str] = {v: k for k, v in MIMES.items()}
|
||||||
|
|
@ -3503,6 +3507,13 @@ def guess_mime(
|
||||||
return ret
|
return ret
|
||||||
|
|
||||||
|
|
||||||
|
def safe_mime(mime: str) -> str:
|
||||||
|
if "text/" in mime or "xml" in mime:
|
||||||
|
return "text/plain; charset=utf-8"
|
||||||
|
else:
|
||||||
|
return "application/octet-stream"
|
||||||
|
|
||||||
|
|
||||||
def getalive(pids: list[int], pgid: int) -> list[int]:
|
def getalive(pids: list[int], pgid: int) -> list[int]:
|
||||||
alive = []
|
alive = []
|
||||||
for pid in pids:
|
for pid in pids:
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue