Hi,
The Matches() function in rules/rules.go uses strings.HasPrefix() without a trailing directory separator when matching paths against access rules. A rule for /uploads also matches /uploads_backup/, granting or denying access to unintended directories. Verified against v2.62.2 (commit 860c19d).
At rules/rules.go:29-35:
func (r *Rule) Matches(path string) bool {
if r.Regex {
return r.Regexp.MatchString(path)
}
return strings.HasPrefix(path, r.Path)
}
When a rule has Path: "/uploads", any path starting with /uploads matches, including /uploads_backup/secret.txt. The regex variant at line 31 uses proper matching, but the non-regex path uses a prefix check without ensuring the match ends at a directory boundary.
The Check() function at http/data.go:29-48 iterates all rules with last-match-wins semantics. No secondary validation exists beyond this prefix check.
Admin configures: allow rule Path: "/shared" for a restricted user.
Filesystem contains:
- /shared/ (intended to be accessible)
- /shared_private/ (intended to be restricted)
User requests /shared_private/secret.txt:
- strings.HasPrefix("/shared_private/secret.txt", "/shared") returns true
- Allow rule applies
- Access granted to the unintended directory
Authenticated users can access files in sibling directories that share a common prefix with an allowed directory, bypassing the admin's intended access configuration.
Prior advisories GHSA-4mh3-h929-w968 (path-based access control bypass) and GHSA-9f3r-2vgw-m8xp (path traversal in copy/rename) addressed related access control issues. This HasPrefix prefix-collision is a distinct, unreported variant.
func (r *Rule) Matches(path string) bool {
if r.Regex {
return r.Regexp.MatchString(path)
}
prefix := r.Path
if prefix != "/" && !strings.HasSuffix(prefix, "/") {
prefix += "/"
}
return path == r.Path || strings.HasPrefix(path, prefix)
}
Koda Reef
Update: Fix submitted as PR #5889.
{
"nvd_published_at": "2026-04-07T17:16:34Z",
"severity": "MODERATE",
"github_reviewed": true,
"cwe_ids": [
"CWE-22"
],
"github_reviewed_at": "2026-04-08T00:04:49Z"
}