dufs: backport upstream fixes
authorTianling Shen <[email protected]>
Sun, 24 Aug 2025 15:14:02 +0000 (23:14 +0800)
committerTianling Shen <[email protected]>
Sun, 24 Aug 2025 17:32:15 +0000 (01:32 +0800)
Including one security fix.

Signed-off-by: Tianling Shen <[email protected]>
net/dufs/Makefile
net/dufs/patches/001-feat-make-dir-urls-inherit-noscript-params-614-.patch [new file with mode: 0644]
net/dufs/patches/002-fix-perms-on-dufs-A-a-ro-619-.patch [new file with mode: 0644]
net/dufs/patches/003-fix-login-btn-does-not-work-for-readonly-annoymous-620-.patch [new file with mode: 0644]

index 1d3066de953e7bb95583cf43b6a4123adfe067ef..2abf392b55c50187bc5187b26e3b4a7ffe5b3f4a 100644 (file)
@@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
 
 PKG_NAME:=dufs
 PKG_VERSION:=0.44.0
-PKG_RELEASE:=1
+PKG_RELEASE:=2
 
 PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
 PKG_SOURCE_URL:=https://codeload.github.com/sigoden/dufs/tar.gz/v$(PKG_VERSION)?
diff --git a/net/dufs/patches/001-feat-make-dir-urls-inherit-noscript-params-614-.patch b/net/dufs/patches/001-feat-make-dir-urls-inherit-noscript-params-614-.patch
new file mode 100644 (file)
index 0000000..acbf91d
--- /dev/null
@@ -0,0 +1,37 @@
+From b2f244a4cfeb492b38ad9b92692e230e04540ea0 Mon Sep 17 00:00:00 2001
+From: sigoden <[email protected]>
+Date: Sat, 16 Aug 2025 07:36:19 +0800
+Subject: [PATCH] feat: make dir urls inherit `?noscript` params (#614)
+
+---
+ src/noscript.rs | 13 ++++++++-----
+ 1 file changed, 8 insertions(+), 5 deletions(-)
+
+--- a/src/noscript.rs
++++ b/src/noscript.rs
+@@ -55,17 +55,20 @@ pub fn generate_noscript_html(data: &Ind
+ fn render_parent() -> String {
+     let value = "../";
+-    format!("<tr><td><a href=\"{value}\">{value}</a></td><td></td><td></td></tr>")
++    format!("<tr><td><a href=\"{value}?noscript\">{value}</a></td><td></td><td></td></tr>")
+ }
+ fn render_path_item(path: &PathItem) -> String {
+-    let href = encode_uri(&path.name);
+-    let suffix = if path.path_type.is_dir() { "/" } else { "" };
+-    let name = escape_str_pcdata(&path.name);
++    let mut href = encode_uri(&path.name);
++    let mut name = escape_str_pcdata(&path.name).to_string();
++    if path.path_type.is_dir() {
++        href.push_str("/?noscript");
++        name.push('/');
++    };
+     let mtime = format_mtime(path.mtime).unwrap_or_default();
+     let size = format_size(path.size, path.path_type);
+-    format!("<tr><td><a href=\"{href}{suffix}\">{name}{suffix}</a></td><td>{mtime}</td><td>{size}</td></tr>")
++    format!("<tr><td><a href=\"{href}\">{name}</a></td><td>{mtime}</td><td>{size}</td></tr>")
+ }
+ fn format_mtime(mtime: u64) -> Option<String> {
diff --git a/net/dufs/patches/002-fix-perms-on-dufs-A-a-ro-619-.patch b/net/dufs/patches/002-fix-perms-on-dufs-A-a-ro-619-.patch
new file mode 100644 (file)
index 0000000..46d6d48
--- /dev/null
@@ -0,0 +1,110 @@
+From f8a7873582567a85095ca9d2124b185cd3eb2ffd Mon Sep 17 00:00:00 2001
+From: sigoden <[email protected]>
+Date: Tue, 19 Aug 2025 07:51:52 +0800
+Subject: [PATCH] fix: perms on `dufs -A -a @/:ro` (#619)
+
+---
+ src/auth.rs   |  9 ++++++---
+ src/server.rs |  4 ++--
+ tests/auth.rs | 18 ++++++++++++++++++
+ 3 files changed, 26 insertions(+), 5 deletions(-)
+
+--- a/src/auth.rs
++++ b/src/auth.rs
+@@ -30,6 +30,7 @@ lazy_static! {
+ #[derive(Debug, Clone, PartialEq)]
+ pub struct AccessControl {
++    empty: bool,
+     use_hashed_password: bool,
+     users: IndexMap<String, (String, AccessPaths)>,
+     anonymous: Option<AccessPaths>,
+@@ -38,6 +39,7 @@ pub struct AccessControl {
+ impl Default for AccessControl {
+     fn default() -> Self {
+         AccessControl {
++            empty: true,
+             use_hashed_password: false,
+             users: IndexMap::new(),
+             anonymous: Some(AccessPaths::new(AccessPerm::ReadWrite)),
+@@ -48,7 +50,7 @@ impl Default for AccessControl {
+ impl AccessControl {
+     pub fn new(raw_rules: &[&str]) -> Result<Self> {
+         if raw_rules.is_empty() {
+-            return Ok(Default::default());
++            return Ok(Self::default());
+         }
+         let new_raw_rules = split_rules(raw_rules);
+         let mut use_hashed_password = false;
+@@ -93,13 +95,14 @@ impl AccessControl {
+         }
+         Ok(Self {
++            empty: false,
+             use_hashed_password,
+             users,
+             anonymous,
+         })
+     }
+-    pub fn exist(&self) -> bool {
++    pub fn has_users(&self) -> bool {
+         !self.users.is_empty()
+     }
+@@ -111,7 +114,7 @@ impl AccessControl {
+         token: Option<&String>,
+         guard_options: bool,
+     ) -> (Option<String>, Option<AccessPaths>) {
+-        if self.users.is_empty() {
++        if self.empty {
+             return (None, Some(AccessPaths::new(AccessPerm::ReadWrite)));
+         }
+--- a/src/server.rs
++++ b/src/server.rs
+@@ -962,7 +962,7 @@ impl Server {
+             uri_prefix: self.args.uri_prefix.clone(),
+             allow_upload: self.args.allow_upload,
+             allow_delete: self.args.allow_delete,
+-            auth: self.args.auth.exist(),
++            auth: self.args.auth.has_users(),
+             user,
+             editable,
+         };
+@@ -1226,7 +1226,7 @@ impl Server {
+             allow_search: self.args.allow_search,
+             allow_archive: self.args.allow_archive,
+             dir_exists: exist,
+-            auth: self.args.auth.exist(),
++            auth: self.args.auth.has_users(),
+             user,
+             paths,
+         };
+--- a/tests/auth.rs
++++ b/tests/auth.rs
+@@ -126,6 +126,24 @@ fn auth_skip_if_no_auth_user(server: Tes
+ }
+ #[rstest]
++fn auth_no_skip_if_anonymous(
++    #[with(&["--auth", "@/:ro"])] server: TestServer,
++) -> Result<(), Error> {
++    let url = format!("{}index.html", server.url());
++    let resp = fetch!(b"GET", &url)
++        .basic_auth("user", Some("pass"))
++        .send()?;
++    assert_eq!(resp.status(), 401);
++    let resp = fetch!(b"GET", &url).send()?;
++    assert_eq!(resp.status(), 200);
++    let resp = fetch!(b"DELETE", &url)
++        .basic_auth("user", Some("pass"))
++        .send()?;
++    assert_eq!(resp.status(), 401);
++    Ok(())
++}
++
++#[rstest]
+ fn auth_check(
+     #[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
+ ) -> Result<(), Error> {
diff --git a/net/dufs/patches/003-fix-login-btn-does-not-work-for-readonly-annoymous-620-.patch b/net/dufs/patches/003-fix-login-btn-does-not-work-for-readonly-annoymous-620-.patch
new file mode 100644 (file)
index 0000000..02d4b9a
--- /dev/null
@@ -0,0 +1,96 @@
+From 4016715187db5bd84c7d15ea6abcd99fd4a0a667 Mon Sep 17 00:00:00 2001
+From: sigoden <[email protected]>
+Date: Tue, 19 Aug 2025 08:58:59 +0800
+Subject: [PATCH] fix: login btn does not work for readonly annoymous (#620)
+
+---
+ assets/index.js |  7 ++++---
+ src/server.rs   | 13 ++++++++++++-
+ tests/auth.rs   | 16 ++++++++++++++--
+ 3 files changed, 30 insertions(+), 6 deletions(-)
+
+--- a/assets/index.js
++++ b/assets/index.js
+@@ -534,7 +534,7 @@ async function setupAuth() {
+     $loginBtn.classList.remove("hidden");
+     $loginBtn.addEventListener("click", async () => {
+       try {
+-        await checkAuth();
++        await checkAuth("login");
+       } catch { }
+       location.reload();
+     });
+@@ -782,9 +782,10 @@ async function saveChange() {
+   }
+ }
+-async function checkAuth() {
++async function checkAuth(variant) {
+   if (!DATA.auth) return;
+-  const res = await fetch(baseUrl(), {
++  const qs = variant ? `?${variant}` : "";
++  const res = await fetch(baseUrl() + qs, {
+     method: "CHECKAUTH",
+   });
+   await assertResOK(res);
+--- a/src/server.rs
++++ b/src/server.rs
+@@ -211,7 +211,18 @@ impl Server {
+         }
+         if method.as_str() == "CHECKAUTH" {
+-            *res.body_mut() = body_full(user.clone().unwrap_or_default());
++            match user.clone() {
++                Some(user) => {
++                    *res.body_mut() = body_full(user);
++                }
++                None => {
++                    if has_query_flag(&query_params, "login") || !access_paths.perm().readwrite() {
++                        self.auth_reject(&mut res)?
++                    } else {
++                        *res.body_mut() = body_full("");
++                    }
++                }
++            }
+             return Ok(res);
+         } else if method.as_str() == "LOGOUT" {
+             self.auth_reject(&mut res)?;
+--- a/tests/auth.rs
++++ b/tests/auth.rs
+@@ -147,7 +147,7 @@ fn auth_no_skip_if_anonymous(
+ fn auth_check(
+     #[with(&["--auth", "user:pass@/:rw", "--auth", "user2:pass2@/", "-A"])] server: TestServer,
+ ) -> Result<(), Error> {
+-    let url = format!("{}index.html", server.url());
++    let url = format!("{}", server.url());
+     let resp = fetch!(b"CHECKAUTH", &url).send()?;
+     assert_eq!(resp.status(), 401);
+     let resp = send_with_digest_auth(fetch!(b"CHECKAUTH", &url), "user", "pass")?;
+@@ -161,7 +161,7 @@ fn auth_check(
+ fn auth_check2(
+     #[with(&["--auth", "user:pass@/:rw|user2:pass2@/", "-A"])] server: TestServer,
+ ) -> Result<(), Error> {
+-    let url = format!("{}index.html", server.url());
++    let url = format!("{}", server.url());
+     let resp = fetch!(b"CHECKAUTH", &url).send()?;
+     assert_eq!(resp.status(), 401);
+     let resp = send_with_digest_auth(fetch!(b"CHECKAUTH", &url), "user", "pass")?;
+@@ -171,6 +171,18 @@ fn auth_check2(
+     Ok(())
+ }
++#[rstest]
++fn auth_check3(
++    #[with(&["--auth", "user:pass@/:rw", "--auth", "@/dir1:rw", "-A"])] server: TestServer,
++) -> Result<(), Error> {
++    let url = format!("{}dir1/", server.url());
++    let resp = fetch!(b"CHECKAUTH", &url).send()?;
++    assert_eq!(resp.status(), 200);
++    let resp = fetch!(b"CHECKAUTH", format!("{url}?login")).send()?;
++    assert_eq!(resp.status(), 401);
++    Ok(())
++}
++
+ #[rstest]
+ fn auth_logout(
+     #[with(&["--auth", "user:pass@/:rw", "-A"])] server: TestServer,