Submit #976047: https://github.com/tobychui/ zoraxy <= 3.3.4 Improper Input Validationinfo

Titlehttps://github.com/tobychui/ zoraxy <= 3.3.4 Improper Input Validation
DescriptionPath-exception rules for Basic Auth, ZorxAuth, and CAPTCHA match on raw string prefix, not a path boundary ## Summary Three separate path-exception matchers, Basic Auth (`BasicAuthExceptionRules`), ZorxAuth SSO (`ZorxAuthExceptionRules`), and CAPTCHA (`ExceptionRules`), compare the request path against a configured prefix with a raw `strings.HasPrefix`, not a path-segment boundary. An operator who excludes `/public` from one of these controls to expose a single subtree also exposes every sibling path that shares the string as a prefix: `/publicAdminPanel`, `/public-internal`, `/publicapi/secret`, none of them the operator's intended subtree, all pass the exception check and skip the control entirely. The forward-auth ignored-paths matcher in the same codebase (`mod/auth/sso/forward/ignoredpaths.go`) was already hardened against this exact class for issue #895, with a doc comment explaining the boundary/traversal risk. The three matchers here never got the same fix. ## Details Basic Auth, `mod/dynamicproxy/authProviders.go`, `handleBasicAuth` (pre-fix): ```go case AuthExceptionType_Paths: if strings.HasPrefix(r.RequestURI, exceptionRule.PathPrefix) { return nil } ``` ZorxAuth SSO, same file, `handleZorxAuth` (pre-fix): ```go if strings.HasPrefix(r.URL.Path, rule.PathPattern) { return nil } ``` CAPTCHA, `mod/dynamicproxy/captcha/captcha.go`, `CheckException` (pre-fix): ```go case ExceptionTypePaths: if strings.HasPrefix(requestPath, rule.PathPrefix) { return true } ``` The same file already has a boundary-safe matcher, `pathPrefixMatch`, used by `ShouldEnforcePath` to decide which paths CAPTCHA protects in the first place. `CheckException`, which decides which paths bypass it, was never switched to use it. ## Proof of Concept Go test against the Basic Auth handler: ```go func TestHandleBasicAuth_ExceptionRuleBoundary(t *testing.T) { pe := &ProxyEndpoint{ AuthenticationProvider: &AuthenticationProvider{ AuthMethod: AuthMethodBasic, BasicAuthCredentials: []*BasicAuthCredentials{ {Username: "admin", PasswordHash: "irrelevant-for-this-test"}, }, BasicAuthExceptionRules: []*BasicAuthExceptionRule{ {RuleType: AuthExceptionType_Paths, PathPrefix: "/public"}, }, }, } req := httptest.NewRequest("GET", "http://example.com/publicAdminPanel/secret", nil) w := httptest.NewRecorder() err := handleBasicAuth(w, req, pe) // pre-fix: err == nil, request served with no credentials } ``` Result before the fix: ``` --- FAIL: TestHandleBasicAuth_ExceptionRuleBoundary (0.00s) BUG: /publicAdminPanel/secret bypassed Basic Auth via the /public exception rule, no credentials required ``` The CAPTCHA test reproduces the same bypass via `captcha.CheckException` with an identical path/prefix pair. ## Impact Any deployment using a path-based exception rule on Basic Auth, ZorxAuth SSO, or CAPTCHA gates loses the guarantee that only the intended subtree is exempted. The severity depends on what sits behind the shared prefix on the proxied application: a path-restricted exception meant to expose a health check or webhook receiver can also expose an admin panel or API that happens to share the string prefix, with no authentication or CAPTCHA challenge at all. Affected: `github.com/tobychui/zoraxy`, confirmed on commit `f9bc2074e425a3c6da9ac9064212d2b556f5f407` (current `main`), same as `v3.3.4`. ## Fix Basic Auth and ZorxAuth now share a new `matchesAuthExceptionPrefix` helper in `authProviders.go`, mirroring the already-hardened `forward.requestPathWithinPrefix`: ```go func matchesAuthExceptionPrefix(requestPath string, prefix string) bool { if prefix == "" { return false } cleanedPath := path.Clean("/" + requestPath) cleanedPrefix := path.Clean("/" + prefix) return cleanedPath == cleanedPrefix || strings.HasPrefix(cleanedPath, cleanedPrefix+"/") } ``` CAPTCHA's `CheckException` now calls its own existing `pathPrefixMatch` instead of a raw `HasPrefix`. New tests lock in both the fix and that the intended exception path still works: `TestMatchesAuthExceptionPrefix_Boundary`, `TestHandleBasicAuth_ExceptionRuleBoundary` (`mod/dynamicproxy`), `TestCheckException_PathBoundary` (`mod/dynamicproxy/captcha`). `go test ./mod/dynamicproxy/... ./mod/auth/...` passes after the change.
Source⚠️ https://github.com/carfeii/oss/blob/main/zoraxy.md#finding-1-path-exception-rules-for-basic-auth-zorxauth-and-captcha-match-on-raw-string-prefix-not-a-path-boundary
User
 carfeii (UID 97470)
Submission09/13/2026 08:58 (1 month ago)
Moderation10/11/2026 16:40 (28 days later)
StatusAccepted
VulDB entry416690 [tobychui zoraxy up to 3.3.4 Path Exception Matcher authProviders.go handleBasicAuth PathPrefix improper authorization]
Points20

Want to stay up to date on a daily basis?

Enable the mail alert feature now!