mirror of
https://github.com/9001/copyparty.git
synced 2025-08-17 09:02:15 -06:00
sandbox readme.md / prologue / epilogue
This commit is contained in:
parent
75cea4f684
commit
d4c5fca15b
10
README.md
10
README.md
|
@ -281,7 +281,7 @@ server-os-specific:
|
||||||
|
|
||||||
upgrade notes
|
upgrade notes
|
||||||
|
|
||||||
* `1.6.0`:
|
* `1.6.0` (2023-01-28):
|
||||||
* http-api: delete/move is now `POST` instead of `GET`
|
* http-api: delete/move is now `POST` instead of `GET`
|
||||||
* everything other than `GET` and `HEAD` must pass [cors validation](#cors)
|
* everything other than `GET` and `HEAD` must pass [cors validation](#cors)
|
||||||
* `1.5.0` (2022-12-03): [new chunksize formula](https://github.com/9001/copyparty/commit/54e1c8d261df) for files larger than 128 GiB
|
* `1.5.0` (2022-12-03): [new chunksize formula](https://github.com/9001/copyparty/commit/54e1c8d261df) for files larger than 128 GiB
|
||||||
|
@ -1252,7 +1252,6 @@ safety profiles:
|
||||||
* `--no-robots` and `--force-js` makes life harder for crawlers, see [hiding from google](#hiding-from-google)
|
* `--no-robots` and `--force-js` makes life harder for crawlers, see [hiding from google](#hiding-from-google)
|
||||||
|
|
||||||
* option `-ss` is a shortcut for the above plus:
|
* option `-ss` is a shortcut for the above plus:
|
||||||
* `--no-logues` and `--no-readme` disables support for readme's and prologues / epilogues in directory listings, which otherwise lets people upload arbitrary `<script>` tags
|
|
||||||
* `--unpost 0`, `--no-del`, `--no-mv` disables all move/delete support
|
* `--unpost 0`, `--no-del`, `--no-mv` disables all move/delete support
|
||||||
* `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance
|
* `--hardlink` creates hardlinks instead of symlinks when deduplicating uploads, which is less maintenance
|
||||||
* however note if you edit one file it will also affect the other copies
|
* however note if you edit one file it will also affect the other copies
|
||||||
|
@ -1263,6 +1262,7 @@ safety profiles:
|
||||||
|
|
||||||
* option `-sss` is a shortcut for the above plus:
|
* option `-sss` is a shortcut for the above plus:
|
||||||
* `--no-dav` disables webdav support
|
* `--no-dav` disables webdav support
|
||||||
|
* `--no-logues` and `--no-readme` disables support for readme's and prologues / epilogues in directory listings, which otherwise lets people upload arbitrary (but sandboxed) `<script>` tags
|
||||||
* `-lo cpp-%Y-%m%d-%H%M%S.txt.xz` enables logging to disk
|
* `-lo cpp-%Y-%m%d-%H%M%S.txt.xz` enables logging to disk
|
||||||
* `-ls **,*,ln,p,r` does a scan on startup for any dangerous symlinks
|
* `-ls **,*,ln,p,r` does a scan on startup for any dangerous symlinks
|
||||||
|
|
||||||
|
@ -1278,8 +1278,10 @@ other misc notes:
|
||||||
behavior that might be unexpected
|
behavior that might be unexpected
|
||||||
|
|
||||||
* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
|
* users without read-access to a folder can still see the `.prologue.html` / `.epilogue.html` / `README.md` contents, for the purpose of showing a description on how to use the uploader for example
|
||||||
* anyone with write access can upload a `README.md` with a `<script>` which runs for all visitors unless `--no-readme`
|
* users can submit `<script>`s which autorun for other visitors in a few ways;
|
||||||
* anyone with move access can rename `some.html` to `.epilogue.html` so it'll run for all visitors unless either `--no-logues` or `--no-dot-ren`
|
* uploading a `README.md` -- avoid with `--no-readme`
|
||||||
|
* renaming `some.html` to `.epilogue.html` -- avoid with either `--no-logues` or `--no-dot-ren`
|
||||||
|
* the directory-listing embed is sandboxed (so any malicious scripts can't do any damage) but the markdown editor is not
|
||||||
|
|
||||||
|
|
||||||
## cors
|
## cors
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
<!--
|
<!--
|
||||||
save this as .epilogue.html inside a write-only folder to declutter the UI, makes it look like
|
save this as .epilogue.html inside a write-only folder to declutter the UI, makes it look like
|
||||||
https://user-images.githubusercontent.com/241032/118311195-dd6ca380-b4ef-11eb-86f3-75a3ff2e1332.png
|
https://user-images.githubusercontent.com/241032/118311195-dd6ca380-b4ef-11eb-86f3-75a3ff2e1332.png
|
||||||
|
|
||||||
|
only works if you disable the prologue/epilogue sandbox with --no-sb-lg
|
||||||
|
which should probably be combined with --no-dot-ren to prevent damage
|
||||||
|
(`no_sb_lg` can also be set per-volume with volflags)
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
|
|
|
@ -841,8 +841,8 @@ def add_optouts(ap):
|
||||||
def add_safety(ap, fk_salt):
|
def add_safety(ap, fk_salt):
|
||||||
ap2 = ap.add_argument_group('safety options')
|
ap2 = ap.add_argument_group('safety options')
|
||||||
ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js")
|
ap2.add_argument("-s", action="count", default=0, help="increase safety: Disable thumbnails / potentially dangerous software (ffmpeg/pillow/vips), hide partial uploads, avoid crawlers.\n └─Alias of\033[32m --dotpart --no-thumb --no-mtag-ff --no-robots --force-js")
|
||||||
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --no-dot-mv --no-dot-ren --unpost=0 --no-del --no-mv --hardlink --vague-403 --ban-404=50,60,1440 -nih")
|
ap2.add_argument("-ss", action="store_true", help="further increase safety: Prevent js-injection, accidental move/delete, broken symlinks, webdav, 404 on 403, ban on excessive 404s.\n └─Alias of\033[32m -s --unpost=0 --no-del --no-mv --hardlink --vague-403 --ban-404=50,60,1440 -nih")
|
||||||
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
ap2.add_argument("-sss", action="store_true", help="further increase safety: Enable logging to disk, scan for dangerous symlinks.\n └─Alias of\033[32m -ss --no-dav --no-logues --no-readme -lo=cpp-%%Y-%%m%%d-%%H%%M%%S.txt.xz --ls=**,*,ln,p,r")
|
||||||
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m; example [\033[32m**,*,ln,p,r\033[0m]")
|
ap2.add_argument("--ls", metavar="U[,V[,F]]", type=u, help="do a sanity/safety check of all volumes on startup; arguments \033[33mUSER\033[0m,\033[33mVOL\033[0m,\033[33mFLAGS\033[0m; example [\033[32m**,*,ln,p,r\033[0m]")
|
||||||
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt; used to generate unpredictable internal identifiers for uploads -- doesn't really matter")
|
ap2.add_argument("--salt", type=u, default="hunter2", help="up2k file-hash salt; used to generate unpredictable internal identifiers for uploads -- doesn't really matter")
|
||||||
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files -- this one DOES matter")
|
ap2.add_argument("--fk-salt", metavar="SALT", type=u, default=fk_salt, help="per-file accesskey salt; used to generate unpredictable URLs for hidden files -- this one DOES matter")
|
||||||
|
@ -979,6 +979,10 @@ def add_ui(ap, retry):
|
||||||
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
ap2.add_argument("--txt-max", metavar="KiB", type=int, default=64, help="max size of embedded textfiles on ?doc= (anything bigger will be lazy-loaded by JS)")
|
||||||
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
|
ap2.add_argument("--doctitle", metavar="TXT", type=u, default="copyparty", help="title / service-name to show in html documents")
|
||||||
ap2.add_argument("--pb-url", metavar="URL", type=u, default="https://github.com/9001/copyparty", help="powered-by link; disable with -np")
|
ap2.add_argument("--pb-url", metavar="URL", type=u, default="https://github.com/9001/copyparty", help="powered-by link; disable with -np")
|
||||||
|
ap2.add_argument("--md-sbf", metavar="FLAGS", type=u, default="downloads forms modals 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 modals popups scripts top-navigation-by-user-activation", help="list of capabilities to ALLOW for prologue/epilogue docs (volflag=lg_sbf)")
|
||||||
|
ap2.add_argument("--no-sb-md", action="store_true", help="don't sandbox README.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")
|
||||||
|
|
||||||
|
|
||||||
def add_debug(ap):
|
def add_debug(ap):
|
||||||
|
|
|
@ -1119,6 +1119,8 @@ class AuthSrv(object):
|
||||||
vol.flags[k] = True
|
vol.flags[k] = True
|
||||||
|
|
||||||
for ga, vf in (
|
for ga, vf in (
|
||||||
|
("no_sb_md", "no_sb_md"),
|
||||||
|
("no_sb_lg", "no_sb_lg"),
|
||||||
("no_forget", "noforget"),
|
("no_forget", "noforget"),
|
||||||
("no_dupe", "nodupe"),
|
("no_dupe", "nodupe"),
|
||||||
("hardlink", "hardlink"),
|
("hardlink", "hardlink"),
|
||||||
|
@ -1130,6 +1132,20 @@ class AuthSrv(object):
|
||||||
if getattr(self.args, ga):
|
if getattr(self.args, ga):
|
||||||
vol.flags[vf] = True
|
vol.flags[vf] = True
|
||||||
|
|
||||||
|
for ve, vd in (
|
||||||
|
("sb_md", "no_sb_md"),
|
||||||
|
("sb_lg", "no_sb_lg"),
|
||||||
|
):
|
||||||
|
if ve in vol.flags:
|
||||||
|
vol.flags.pop(vd, None)
|
||||||
|
|
||||||
|
for ga, vf in (
|
||||||
|
("md_sbf", "md_sbf"),
|
||||||
|
("lg_sbf", "lg_sbf"),
|
||||||
|
):
|
||||||
|
if vf not in vol.flags:
|
||||||
|
vol.flags[vf] = getattr(self.args, ga)
|
||||||
|
|
||||||
for k1, k2 in IMPLICATIONS:
|
for k1, k2 in IMPLICATIONS:
|
||||||
if k1 in vol.flags:
|
if k1 in vol.flags:
|
||||||
vol.flags[k2] = True
|
vol.flags[k2] = True
|
||||||
|
|
|
@ -649,6 +649,10 @@ class HttpCli(object):
|
||||||
ih = self.headers
|
ih = self.headers
|
||||||
origin = ih.get("origin")
|
origin = ih.get("origin")
|
||||||
if not origin:
|
if not origin:
|
||||||
|
sfsite = ih.get("sec-fetch-site")
|
||||||
|
if sfsite and sfsite.lower().startswith("cross"):
|
||||||
|
origin = ":|" # sandboxed iframe
|
||||||
|
else:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
oh = self.out_headers
|
oh = self.out_headers
|
||||||
|
@ -3356,6 +3360,7 @@ class HttpCli(object):
|
||||||
readme = f.read().decode("utf-8")
|
readme = f.read().decode("utf-8")
|
||||||
break
|
break
|
||||||
|
|
||||||
|
vf = vn.flags
|
||||||
ls_ret = {
|
ls_ret = {
|
||||||
"dirs": [],
|
"dirs": [],
|
||||||
"files": [],
|
"files": [],
|
||||||
|
@ -3388,6 +3393,8 @@ class HttpCli(object):
|
||||||
"have_zip": (not self.args.no_zip),
|
"have_zip": (not self.args.no_zip),
|
||||||
"have_unpost": int(self.args.unpost),
|
"have_unpost": int(self.args.unpost),
|
||||||
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
"have_b_u": (self.can_write and self.uparam.get("b") == "u"),
|
||||||
|
"sb_md": "" if "no_sb_md" in vf else (vf.get("md_sbf") or "y"),
|
||||||
|
"sb_lg": "" if "no_sb_lg" in vf else (vf.get("lg_sbf") or "y"),
|
||||||
"url_suf": url_suf,
|
"url_suf": url_suf,
|
||||||
"logues": logues,
|
"logues": logues,
|
||||||
"readme": readme,
|
"readme": readme,
|
||||||
|
|
|
@ -97,13 +97,13 @@ class SvcHub(object):
|
||||||
if args.sss or args.s >= 3:
|
if args.sss or args.s >= 3:
|
||||||
args.ss = True
|
args.ss = True
|
||||||
args.no_dav = True
|
args.no_dav = True
|
||||||
|
args.no_logues = True
|
||||||
|
args.no_readme = True
|
||||||
args.lo = args.lo or "cpp-%Y-%m%d-%H%M%S.txt.xz"
|
args.lo = args.lo or "cpp-%Y-%m%d-%H%M%S.txt.xz"
|
||||||
args.ls = args.ls or "**,*,ln,p,r"
|
args.ls = args.ls or "**,*,ln,p,r"
|
||||||
|
|
||||||
if args.ss or args.s >= 2:
|
if args.ss or args.s >= 2:
|
||||||
args.s = True
|
args.s = True
|
||||||
args.no_logues = True
|
|
||||||
args.no_readme = True
|
|
||||||
args.unpost = 0
|
args.unpost = 0
|
||||||
args.no_del = True
|
args.no_del = True
|
||||||
args.no_mv = True
|
args.no_mv = True
|
||||||
|
|
|
@ -793,6 +793,13 @@ html.y #path a:hover {
|
||||||
.logue:empty {
|
.logue:empty {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
.logue>iframe {
|
||||||
|
background: var(--bgg);
|
||||||
|
visibility: hidden;
|
||||||
|
border: none;
|
||||||
|
width: 100%;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
#pro.logue {
|
#pro.logue {
|
||||||
margin-bottom: .8em;
|
margin-bottom: .8em;
|
||||||
}
|
}
|
||||||
|
@ -817,6 +824,9 @@ html.y #path a:hover {
|
||||||
.mdo {
|
.mdo {
|
||||||
max-width: 52em;
|
max-width: 52em;
|
||||||
}
|
}
|
||||||
|
.mdo.sb {
|
||||||
|
max-width: unset;
|
||||||
|
}
|
||||||
.mdo,
|
.mdo,
|
||||||
.mdo * {
|
.mdo * {
|
||||||
line-height: 1.4em;
|
line-height: 1.4em;
|
||||||
|
|
|
@ -85,7 +85,7 @@
|
||||||
<div id="bdoc"></div>
|
<div id="bdoc"></div>
|
||||||
{%- endif %}
|
{%- endif %}
|
||||||
|
|
||||||
<div id="pro" class="logue">{{ logues[0] }}</div>
|
<div id="pro" class="logue">{{ "" if sb_lg else logues[0] }}</div>
|
||||||
|
|
||||||
<table id="files">
|
<table id="files">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -119,7 +119,7 @@
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
|
||||||
<div id="epi" class="logue">{{ logues[1] }}</div>
|
<div id="epi" class="logue">{{ "" if sb_lg else logues[1] }}</div>
|
||||||
|
|
||||||
<h2><a href="{{ r }}/?h" id="goh">control-panel</a></h2>
|
<h2><a href="{{ r }}/?h" id="goh">control-panel</a></h2>
|
||||||
|
|
||||||
|
@ -150,12 +150,14 @@
|
||||||
have_del = {{ have_del|tojson }},
|
have_del = {{ have_del|tojson }},
|
||||||
have_unpost = {{ have_unpost }},
|
have_unpost = {{ have_unpost }},
|
||||||
have_zip = {{ have_zip|tojson }},
|
have_zip = {{ have_zip|tojson }},
|
||||||
|
sb_md = "{{ sb_md }}",
|
||||||
|
sb_lg = "{{ sb_lg }}",
|
||||||
lifetime = {{ lifetime }},
|
lifetime = {{ lifetime }},
|
||||||
turbolvl = {{ turbolvl }},
|
turbolvl = {{ turbolvl }},
|
||||||
u2sort = "{{ u2sort }}",
|
u2sort = "{{ u2sort }}",
|
||||||
have_emp = {{ have_emp|tojson }},
|
have_emp = {{ have_emp|tojson }},
|
||||||
txt_ext = "{{ txt_ext }}",
|
txt_ext = "{{ txt_ext }}",
|
||||||
{% if no_prism %}no_prism = 1,{% endif %}
|
logues = {{ logues|tojson if sb_lg else "[]" }},
|
||||||
readme = {{ readme|tojson }},
|
readme = {{ readme|tojson }},
|
||||||
ls0 = {{ ls0|tojson }};
|
ls0 = {{ ls0|tojson }};
|
||||||
|
|
||||||
|
|
|
@ -5420,8 +5420,8 @@ var treectl = (function () {
|
||||||
despin('#files');
|
despin('#files');
|
||||||
despin('#gfiles');
|
despin('#gfiles');
|
||||||
|
|
||||||
ebi('pro').innerHTML = res.logues ? res.logues[0] || "" : "";
|
sandbox(ebi('pro'), sb_lg, '', res.logues ? res.logues[0] || "" : "");
|
||||||
ebi('epi').innerHTML = res.logues ? res.logues[1] || "" : "";
|
sandbox(ebi('epi'), sb_lg, '', res.logues ? res.logues[1] || "" : "");
|
||||||
|
|
||||||
clmod(ebi('epi'), 'mdo');
|
clmod(ebi('epi'), 'mdo');
|
||||||
if (res.readme)
|
if (res.readme)
|
||||||
|
@ -6587,7 +6587,7 @@ function show_md(md, name, div, url, depth) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
clmod(div, 'mdo', 1);
|
clmod(div, 'mdo', 1);
|
||||||
div.innerHTML = marked.parse(md, marked_opts);
|
sandbox(div, sb_md, 'mdo', marked.parse(md, marked_opts));
|
||||||
|
|
||||||
ext = md_plug.post;
|
ext = md_plug.post;
|
||||||
ext = ext ? [ext[0].render, ext[0].render2] : [];
|
ext = ext ? [ext[0].render, ext[0].render2] : [];
|
||||||
|
@ -6641,6 +6641,63 @@ if (readme)
|
||||||
show_readme(readme);
|
show_readme(readme);
|
||||||
|
|
||||||
|
|
||||||
|
function sandbox(tgt, rules, cls, html) {
|
||||||
|
if (!rules || (html || '').indexOf('<') == -1) {
|
||||||
|
tgt.innerHTML = html;
|
||||||
|
clmod(tgt, 'sb');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
clmod(tgt, 'sb', 1);
|
||||||
|
var tid = tgt.getAttribute('id'),
|
||||||
|
dcs = document.styleSheets,
|
||||||
|
hash = location.hash,
|
||||||
|
want = '';
|
||||||
|
|
||||||
|
if (hash.startsWith('#md-'))
|
||||||
|
want = hash.slice(1);
|
||||||
|
|
||||||
|
var h2 = '<html class="' + document.documentElement.className + '"><head><base target="_parent">';
|
||||||
|
for (var a = 0; a < dcs.length; a++)
|
||||||
|
if (dcs[a].href)
|
||||||
|
h2 += '<link rel="stylesheet" media="screen" href="' + dcs[a].href + '">';
|
||||||
|
|
||||||
|
html = h2 + '</head><body class="logue ' + cls + '">' + html +
|
||||||
|
'<script>setTimeout(function(){var pih=-1;function f(){var d=document.documentElement,ih=2+Math.min(parseInt(getComputedStyle(d).height),d.scrollHeight);if(ih==pih)return;pih=ih;window.parent.postMessage("iheight #' + tid + '>iframe "+ih,"*")};setInterval(f,100);f();var el="' + want + '"&&document.getElementById("' + want + '");if(el)window.parent.postMessage("iscroll #' + tid + ' "+el.offsetTop,"*")},1)</script></body></html>';
|
||||||
|
|
||||||
|
var fr = mknod('iframe');
|
||||||
|
fr.setAttribute('sandbox', rules ? 'allow-' + rules.replace(/ /g, ' allow-') : '');
|
||||||
|
fr.setAttribute('srcdoc', html);
|
||||||
|
tgt.innerHTML = '';
|
||||||
|
tgt.appendChild(fr);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
window.addEventListener("message", function (e) {
|
||||||
|
try {
|
||||||
|
console.log('msg:' + e.data);
|
||||||
|
var t = e.data.split(/ /g);
|
||||||
|
if (t[0] == 'iheight') {
|
||||||
|
var el = QS(t[1]);
|
||||||
|
el.style.height = t[2] + 'px';
|
||||||
|
el.style.visibility = 'unset';
|
||||||
|
}
|
||||||
|
else if (t[0] == 'iscroll') {
|
||||||
|
var y1 = QS(t[1]).offsetTop,
|
||||||
|
y2 = parseInt(t[2]);
|
||||||
|
console.log(y1, y2);
|
||||||
|
document.documentElement.scrollTop = y1 + y2;
|
||||||
|
}
|
||||||
|
} catch (ex) {
|
||||||
|
console.log('msg-err: ' + ex);
|
||||||
|
}
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
|
||||||
|
if (sb_lg && logues.length) {
|
||||||
|
sandbox(ebi('pro'), sb_lg, '', logues[0]);
|
||||||
|
sandbox(ebi('epi'), sb_lg, '', logues[1]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
(function () {
|
(function () {
|
||||||
try {
|
try {
|
||||||
var tr = ebi('files').tBodies[0].rows;
|
var tr = ebi('files').tBodies[0].rows;
|
||||||
|
|
Loading…
Reference in a new issue