From 6a0aaaf0690a283f7d29777622573705b0b0b8b1 Mon Sep 17 00:00:00 2001 From: ed Date: Tue, 21 Jan 2025 22:51:00 +0000 Subject: [PATCH] md/logue sandbox: custom allow prop add global-option and volflag to specify the value of the iframe's allow-property --- copyparty/__main__.py | 6 ++++-- copyparty/authsrv.py | 3 +++ copyparty/cfg.py | 4 ++++ copyparty/httpcli.py | 2 +- copyparty/web/browser.js | 17 +++++++++-------- tests/util.py | 2 +- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/copyparty/__main__.py b/copyparty/__main__.py index a6685d5f..023eabb4 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1481,8 +1481,10 @@ def add_ui(ap, retry): ap2.add_argument("--ver", action="store_true", help="show version on the control panel (incompatible with \033[33m-nb\033[0m)") ap2.add_argument("--k304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable k304 on the controlpanel (workaround for buggy reverse-proxies); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on") ap2.add_argument("--no304", metavar="NUM", type=int, default=0, help="configure the option to enable/disable no304 on the controlpanel (workaround for buggy caching in browsers); [\033[32m0\033[0m] = hidden and default-off, [\033[32m1\033[0m] = visible and default-off, [\033[32m2\033[0m] = visible and default-on") - ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#attr-sandbox") - ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for prologue/epilogue docs (volflag=lg_sbf)") + ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for README.md docs (volflag=md_sbf); see https://developer.mozilla.org/en-US/docs/Web/HTML/Element/iframe#sandbox") + ap2.add_argument("--lg-sbf", metavar="FLAGS", type=u, default="downloads forms popups scripts top-navigation-by-user-activation", help="list of capabilities to allow in the iframe 'sandbox' attribute for prologue/epilogue docs (volflag=lg_sbf)") + ap2.add_argument("--md-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for README.md docs, for example [\033[32mfullscreen\033[0m] (volflag=md_sba)") + ap2.add_argument("--lg-sba", metavar="TXT", type=u, default="", help="the value of the iframe 'allow' attribute for prologue/epilogue docs (volflag=lg_sba); see https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Permissions-Policy#iframes") ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README/PREADME.md documents (volflags: no_sb_md | sb_md)") ap2.add_argument("--no-sb-lg", action="store_true", help="don't sandbox prologue/epilogue docs (volflags: no_sb_lg | sb_lg); enables non-js support") diff --git a/copyparty/authsrv.py b/copyparty/authsrv.py index 62912cfd..bd997caa 100644 --- a/copyparty/authsrv.py +++ b/copyparty/authsrv.py @@ -2339,6 +2339,7 @@ class AuthSrv(object): "frand": bool(vf.get("rand")), "lifetime": vf.get("lifetime") or 0, "unlist": vf.get("unlist") or "", + "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"), } js_htm = { "s_name": self.args.bname, @@ -2351,6 +2352,8 @@ class AuthSrv(object): "have_unpost": int(self.args.unpost), "have_emp": self.args.emp, "sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"), + "sba_md": vf.get("md_sba") or "", + "sba_lg": vf.get("lg_sba") or "", "txt_ext": self.args.textfiles.replace(",", " "), "def_hcols": list(vf.get("mth") or []), "unlist0": vf.get("unlist") or "", diff --git a/copyparty/cfg.py b/copyparty/cfg.py index 3ad325a1..dac1aa94 100644 --- a/copyparty/cfg.py +++ b/copyparty/cfg.py @@ -74,6 +74,8 @@ def vf_vmap() -> dict[str, str]: "html_head", "lg_sbf", "md_sbf", + "lg_sba", + "md_sba", "nrand", "og_desc", "og_site", @@ -241,6 +243,8 @@ flagcats = { "sb_lg": "enable js sandbox for prologue/epilogue (default)", "md_sbf": "list of markdown-sandbox safeguards to disable", "lg_sbf": "list of *logue-sandbox safeguards to disable", + "md_sba": "value of iframe allow-prop for markdown-sandbox", + "lg_sba": "value of iframe allow-prop for *logue-sandbox", "nohtml": "return html and markdown as text/html", }, "others": { diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index 349fe00b..5a95e8eb 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -5819,7 +5819,7 @@ class HttpCli(object): "taglist": [], "have_tags_idx": int(e2t), "have_b_u": (self.can_write and self.uparam.get("b") == "u"), - "sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"), + "sb_lg": vn.js_ls["sb_lg"], "url_suf": url_suf, "title": html_escape("%s %s" % (self.args.bname, self.vpath), crlf=True), "srv_info": srv_infot, diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 5b6b2013..68deeeac 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -7667,9 +7667,9 @@ var treectl = (function () { if (lg1 === Ls.eng.f_empty) lg1 = L.f_empty; - sandbox(ebi('pro'), sb_lg, '', lg0); + sandbox(ebi('pro'), sb_lg, sba_lg,'', lg0); if (dirchg) - sandbox(ebi('epi'), sb_lg, '', lg1); + sandbox(ebi('epi'), sb_lg, sba_lg, '', lg1); clmod(ebi('pro'), 'mdo'); clmod(ebi('epi'), 'mdo'); @@ -7680,7 +7680,7 @@ var treectl = (function () { if (md1) show_readme(md1, 1); else if (!dirchg) - sandbox(ebi('epi'), sb_lg, '', lg1); + sandbox(ebi('epi'), sb_lg, sba_lg, '', lg1); if (this.hpush && !this.back) { var ofs = ebi('wrap').offsetTop; @@ -9156,7 +9156,7 @@ function show_md(md, name, div, url, depth) { if (!have_emp) md_html = DOMPurify.sanitize(md_html); - if (sandbox(div, sb_md, 'mdo', md_html)) + if (sandbox(div, sb_md, sba_md, 'mdo', md_html)) return; ext = md_plug.post; @@ -9207,7 +9207,7 @@ function show_readme(md, n) { var tgt = ebi(n ? 'epi' : 'pro'); if (!treectl.ireadme) - return sandbox(tgt, '', '', 'a'); + return sandbox(tgt, '', '', '', 'a'); show_md(md, n ? 'README.md' : 'PREADME.md', tgt); } @@ -9216,7 +9216,7 @@ for (var a = 0; a < readmes.length; a++) show_readme(readmes[a], a); -function sandbox(tgt, rules, cls, html) { +function sandbox(tgt, rules, allow, cls, html) { if (!treectl.ireadme) { tgt.innerHTML = html ? L.md_off : ''; return; @@ -9272,6 +9272,7 @@ function sandbox(tgt, rules, cls, html) { var fr = mknod('iframe'); fr.setAttribute('title', 'folder ' + tid + 'logue'); fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : ''); + fr.setAttribute('allow', allow); fr.setAttribute('srcdoc', html); tgt.appendChild(fr); treectl.sb_msg = true; @@ -9321,8 +9322,8 @@ if (sb_lg && logues.length) { if (logues[1] === Ls.eng.f_empty) logues[1] = L.f_empty; - sandbox(ebi('pro'), sb_lg, '', logues[0]); - sandbox(ebi('epi'), sb_lg, '', logues[1]); + sandbox(ebi('pro'), sb_lg, sba_lg, '', logues[0]); + sandbox(ebi('epi'), sb_lg, sba_lg, '', logues[1]); } diff --git a/tests/util.py b/tests/util.py index 4d5d6556..53ee9e16 100644 --- a/tests/util.py +++ b/tests/util.py @@ -147,7 +147,7 @@ class Cfg(Namespace): ex = "db_act k304 loris no304 re_maxage rproxy rsp_jtr rsp_slp s_wr_slp snap_wri theme themes turbo" ka.update(**{k: 0 for k in ex.split()}) - ex = "ah_alg bname chpw_db doctitle df exit favico idp_h_usr ipa html_head lg_sbf log_fk md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i shr tcolor textfiles unlist vname xff_src R RS SR" + ex = "ah_alg bname chpw_db doctitle df exit favico idp_h_usr ipa html_head lg_sba lg_sbf log_fk md_sba md_sbf name og_desc og_site og_th og_title og_title_a og_title_v og_title_i shr tcolor textfiles unlist vname xff_src R RS SR" ka.update(**{k: "" for k in ex.split()}) ex = "ban_403 ban_404 ban_422 ban_pw ban_url"