VYPR
High severityNVD Advisory· Published Aug 1, 2025· Updated Aug 4, 2025

Copyparty is vulnerable to Regex Denial of Service (ReDoS) attacks through "Recent Uploads" page

CVE-2025-54796

Description

Copyparty is a portable file server. Versions prior to 1.18.9, the filter parameter for the "Recent Uploads" page allows arbitrary RegExes. If this feature is enabled (which is the default), an attacker can craft a filter which deadlocks the server. This is fixed in version 1.18.9.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
copypartyPyPI
< 1.18.91.18.9

Affected products

1

Patches

1
09910ba80784

fix GHSA-5662-2rj7-f2v6 ;

2 files changed · +37 9
  • copyparty/httpcli.py+22 9 modified
    @@ -107,6 +107,7 @@
         sendfile_py,
         set_fperms,
         stat_resource,
    +    str_anchor,
         ub64dec,
         ub64enc,
         ujoin,
    @@ -5369,9 +5370,9 @@ def tx_ups(self) -> bool:
                     raise Pebkac(500, "sqlite3 not found on server; unpost is disabled")
                 raise Pebkac(500, "server busy, cannot unpost; please retry in a bit")
     
    -        zs = self.uparam.get("filter") or ""
    -        filt = re.compile(zs, re.I) if zs else None
    -        lm = "ups %r" % (zs,)
    +        sfilt = self.uparam.get("filter") or ""
    +        nfi, vfi = str_anchor(sfilt)
    +        lm = "ups %d%r" % (nfi, sfilt)
     
             if self.args.shr and self.vpath.startswith(self.args.shr1):
                 shr_dbv, shr_vrem = self.vn.get_dbv(self.rem)
    @@ -5431,8 +5432,14 @@ def tx_ups(self) -> bool:
                 q = "select sz, rd, fn, at from up where ip=? and at>? order by at desc"
                 for sz, rd, fn, at in cur.execute(q, (self.ip, lim)):
                     vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
    -                if filt and not filt.search(vp):
    -                    continue
    +                if nfi == 0 or (nfi == 1 and vfi in vp):
    +                    pass
    +                elif nfi == 2:
    +                    if not vp.startswith(vfi):
    +                        continue
    +                elif nfi == 3:
    +                    if not vp.endswith(vfi):
    +                        continue
     
                     n -= 1
                     if not n:
    @@ -5513,8 +5520,8 @@ def tx_rups(self) -> bool:
                 raise Pebkac(500, "server busy, cannot list recent uploads; please retry")
     
             sfilt = self.uparam.get("filter") or ""
    -        filt = re.compile(sfilt, re.I) if sfilt else None
    -        lm = "ru %r" % (sfilt,)
    +        nfi, vfi = str_anchor(sfilt)
    +        lm = "ru %d%r" % (nfi, sfilt)
             self.log(lm)
     
             ret: list[dict[str, Any]] = []
    @@ -5549,8 +5556,14 @@ def tx_rups(self) -> bool:
                 q = "select sz, rd, fn, ip, at from up where at>0 order by at desc"
                 for sz, rd, fn, ip, at in cur.execute(q):
                     vp = "/" + "/".join(x for x in [vol.vpath, rd, fn] if x)
    -                if filt and not filt.search(vp):
    -                    continue
    +                if nfi == 0 or (nfi == 1 and vfi in vp):
    +                    pass
    +                elif nfi == 2:
    +                    if not vp.startswith(vfi):
    +                        continue
    +                elif nfi == 3:
    +                    if not vp.endswith(vfi):
    +                        continue
     
                     if not dots and "/." in vp:
                         continue
    
  • copyparty/util.py+15 0 modified
    @@ -2396,6 +2396,21 @@ def ujoin(rd: str, fn: str) -> str:
             return rd or fn
     
     
    +def str_anchor(txt) -> tuple[int, str]:
    +    if not txt:
    +        return 0, ""
    +    txt = txt.lower()
    +    a = txt.startswith("^")
    +    b = txt.endswith("$")
    +    if not b:
    +        if not a:
    +            return 1, txt  # ~
    +        return 2, txt[1:]  # ^
    +    if not a:
    +        return 3, txt[:-1]  # $
    +    return 4, txt[1:-1]  # ^$
    +
    +
     def log_reloc(
         log: "NamedLogger",
         re: dict[str, str],
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

5

News mentions

0

No linked articles in our index yet.