diff --git a/copyparty/__main__.py b/copyparty/__main__.py index e69c8d14..be5c3b98 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -1288,6 +1288,7 @@ def add_stats(ap): def add_yolo(ap): ap2 = ap.add_argument_group('yolo options') ap2.add_argument("--allow-csrf", action="store_true", help="disable csrf protections; let other domains/sites impersonate you through cross-site requests") + ap2.add_argument("--cookie-lax", action="store_true", help="allow cookies from other domains (if you follow a link from another website into your server, you will arrive logged-in); this reduces protection against CSRF") ap2.add_argument("--getmod", action="store_true", help="permit ?move=[...] and ?delete as GET") ap2.add_argument("--wo-up-readme", action="store_true", help="allow users with write-only access to upload logues and readmes without adding the _wo_ filename prefix (volflag=wo_up_readme)") diff --git a/copyparty/httpcli.py b/copyparty/httpcli.py index e2a74bcd..2aa6a059 100644 --- a/copyparty/httpcli.py +++ b/copyparty/httpcli.py @@ -2995,12 +2995,20 @@ class HttpCli(object): # reset both plaintext and tls # (only affects active tls cookies when tls) for k in ("cppwd", "cppws") if self.is_https else ("cppwd",): - ck = gencookie(k, pwd, self.args.R, False) + ck = gencookie(k, pwd, self.args.R, self.args.cookie_lax, False) self.out_headerlist.append(("Set-Cookie", ck)) self.out_headers.pop("Set-Cookie", None) # drop keepalive else: k = "cppws" if self.is_https else "cppwd" - ck = gencookie(k, pwd, self.args.R, self.is_https, dur, "; HttpOnly") + ck = gencookie( + k, + pwd, + self.args.R, + self.args.cookie_lax, + self.is_https, + dur, + "; HttpOnly", + ) self.out_headers["Set-Cookie"] = ck return dur > 0, msg @@ -5041,7 +5049,7 @@ class HttpCli(object): def setck(self) -> bool: k, v = self.uparam["setck"].split("=", 1) t = 0 if v in ("", "x") else 86400 * 299 - ck = gencookie(k, v, self.args.R, False, t) + ck = gencookie(k, v, self.args.R, self.args.cookie_lax, False, t) self.out_headerlist.append(("Set-Cookie", ck)) if "cc" in self.ouparam: self.redirect("", "?h#cc") @@ -5053,7 +5061,7 @@ class HttpCli(object): for k in ALL_COOKIES: if k not in self.cookies: continue - cookie = gencookie(k, "x", self.args.R, False) + cookie = gencookie(k, "x", self.args.R, self.args.cookie_lax, False) self.out_headerlist.append(("Set-Cookie", cookie)) self.redirect("", "?h#cc") diff --git a/copyparty/util.py b/copyparty/util.py index 4bc74a0a..0b2dbcc4 100644 --- a/copyparty/util.py +++ b/copyparty/util.py @@ -2037,15 +2037,25 @@ def formatdate(ts: Optional[float] = None) -> str: return RFC2822 % (WKDAYS[wd], d, MONTHS[mo - 1], y, h, mi, s) -def gencookie(k: str, v: str, r: str, tls: bool, dur: int = 0, txt: str = "") -> str: +def gencookie( + k: str, v: str, r: str, lax: bool, tls: bool, dur: int = 0, txt: str = "" +) -> str: v = v.replace("%", "%25").replace(";", "%3B") if dur: exp = formatdate(time.time() + dur) else: exp = "Fri, 15 Aug 1997 01:00:00 GMT" - t = "%s=%s; Path=/%s; Expires=%s%s%s; SameSite=Lax" - return t % (k, v, r, exp, "; Secure" if tls else "", txt) + t = "%s=%s; Path=/%s; Expires=%s%s%s; SameSite=%s" + return t % ( + k, + v, + r, + exp, + "; Secure" if tls else "", + txt, + "Lax" if lax else "Strict", + ) def humansize(sz: float, terse: bool = False) -> str: diff --git a/tests/util.py b/tests/util.py index 2b3d4b52..147c3b64 100644 --- a/tests/util.py +++ b/tests/util.py @@ -143,7 +143,7 @@ class Cfg(Namespace): def __init__(self, a=None, v=None, c=None, **ka0): ka = {} - ex = "chpw daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" + ex = "chpw cookie_lax daw dav_auth dav_mac dav_rt e2d e2ds e2dsa e2t e2ts e2tsr e2v e2vu e2vp early_ban ed emp exp force_js getmod grid gsel hardlink hardlink_only ih ihead magic nid nih no_acode no_athumb no_bauth no_clone no_cp no_dav no_db_ip no_del no_dirsz no_dupe no_lifetime no_logues no_mv no_pipe no_poll no_readme no_robots no_sb_md no_sb_lg no_scandir no_tail no_tarcmp no_thumb no_vthumb no_zip nrand nsort nw og og_no_head og_s_title ohead q rand re_dirsz rmagic rss smb srch_dbg srch_excl stats uqe vague_403 vc ver wo_up_readme write_uplog xdev xlink xvol zipmaxu zs" ka.update(**{k: False for k in ex.split()}) ex = "dav_inf dedup dotpart dotsrch hook_v no_dhash no_fastboot no_fpool no_htp no_rescan no_sendfile no_ses no_snap no_up_list no_voldump re_dhash see_dots plain_ip"