Summary
Echo's middleware.Static: %2F-encoded path bypasses route/group middleware guarding a statically-served path
When middleware.Static is mounted globally (e.Use(middleware.Static(...))) and an application relies on a route- or group-level middleware guard to restrict a path that also falls under the served static root, a %2F-encoded request bypasses that guard while the file is still served. This is the same encoding/decoding mismatch fixed for StaticFS/StaticDirectoryHandler in CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq, but the fix did not cover the middleware.Static path. In addition, the StaticConfig.EnablePathUnescaping docstring states the default configuration prevents exactly this bypass, which is inaccurate for the global-mount case.
This is not path traversal: no file outside the static root is accessed. The served file lives inside the static root; the issue is that access-control expectations set by a route guard are inconsistent between the literal and %2F-encoded forms of the same path.
Details
Root cause, the router and the static handler disagree on how to interpret %2F:
- The router matches routes on the raw, still-encoded path, so
GET /admin%2Fsecret.txtdoes not match a/admin/...route or group; any middleware attached to that route/group is therefore skipped. middleware.Staticselects its path source inmiddleware/static.go(~L219): with a globale.Usemount there is no wildcard route, so it usesc.Request().URL.Path, whichnet/httphas already decoded (%2F→/). It thenpath.Cleans (~L235) andOpens (~L259)admin/secret.txt, serving the file.
Net effect: for the literal path the guard runs; for the %2F-encoded path the guard is skipped but the same file is served.
Relevant code:
middleware/static.go~L219 (p := c.Request().URL.Path), ~L235 (path.Clean), ~L259 (currentFS.Open)StaticConfig.EnablePathUnescapingdocstring (static.go~L60-67), claims the default prevents the/admin%2fprivate.txt→/admin/*guard bypass; this is not true for the global mount.
Relationship to prior advisories:
- CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq (patched 5.2.0): same
%2Frouter-vs-handler mismatch, but fixed forStaticFS/StaticDirectoryHandler. Themiddleware.Staticpath was not covered, this report is that gap. - CVE-2026-25766 / GHSA-pgvm-wxw2-hrv9 (patched 5.0.3): a different issue (Windows backslash traversal outside the root). Referenced only to disambiguate; not the same as this report.
PoC
Environment: golang:1.25, module using the official release (go get github.com/labstack/echo/[email protected], no local replace). Static root public/ contains a marker file public/admin/secret.txt = SECRET.
Minimal app (the pattern under test: static served globally + a group guard intended to protect /admin):
package main
import (
"net/http"
"github.com/labstack/echo/v5"
"github.com/labstack/echo/v5/middleware"
)
func main() {
e := echo.New()
e.Use(middleware.Static("public")) // serves ./public globally (default filesystem)
admin := e.Group("/admin", func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c *echo.Context) error { return c.String(http.StatusUnauthorized, "auth required") }
})
admin.GET("/secret.txt", func(c *echo.Context) error { return c.String(http.StatusOK, "handler") })
e.Start(":1323")
}
Requests (unauthenticated):
# Literal: route matches -> group guard runs
curl -i http://localhost:1323/admin/secret.txt
# => 401
# %2F-encoded: route does NOT match -> guard skipped, but middleware.Static serves the file
curl -i --path-as-is http://localhost:1323/admin%2Fsecret.txt
# => 200, body: SECRET
Observed on v5.3.1: literal → 401; %2F → 200 with the file contents. For contrast, serving via the (patched) e.StaticFS("/") returns 404 for the same %2F request, showing the middleware.Static path was not covered by the CVE-2026-55677 fix.
Workarounds
Keep files that need protection outside the Static middleware's root directory.
Impact
An unauthenticated remote user can retrieve a file served by middleware.Static even when the application attached a route/group middleware guard to that path, by substituting / with %2F. The consequence is limited to files already inside the served static root (CWE-863 authorization inconsistency; CWE-436 interpretation conflict), there is no access to files outside the static root.
Honest caveat on validity / severity: this only has security impact for applications that (a) serve a directory statically to the public and (b) place a sensitive file inside that same directory while relying on a route-level guard to protect it. That combination is arguably a misconfiguration, a statically-served directory is public by design, so maintainers may reasonably classify this as a documentation fix (correct the EnablePathUnescaping claim) or won't-fix rather than a code vulnerability. It is filed primarily because (1) the router-vs-handler %2F inconsistency is the exact class fixed in CVE-2026-55677 but left uncovered for middleware.Static, and (2) the docstring currently promises protection it does not provide.
The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions. Typical impact: unauthorized data access or execution of privileged operations.
CVE-2026-118650 has a CVSS score of 7.5 (High). The vector is network-reachable, no privileges required, and no user interaction. A CVSS score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether this affects your application depends on whether the vulnerable code is present and reachable in your environment. A fixed version is available (5.4.0, 4.16.0); upgrading removes the vulnerable code path.
Affected versions
Security releases
Kodem intelligence
Severity tells you how bad this could be in the worst case. It does not tell you whether you are exposed. Exploitability and impact are functions of runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A vulnerable package can sit in your dependency tree and never run.
Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter. Kodem's runtime-powered SCA identifies whether this CVE is reachable in your applications.
Already deployed Kodem?
See it in your environmentNew to Kodem? Get a demo →Remediation advice
Fixed in github.com/labstack/echo/v5 v5.4.0 and github.com/labstack/echo/v4 v4.16.0. With the default settings, the Static middleware resolves files from the same form of the path that the router matched, so percent-encoded paths such as /admin%2Fsecret.txt or /%61dmin/secret.txt no longer reach a file under a guarded route. Paths with ., .. or empty segments are no longer served.
Note: middleware registered with Echo#Use runs before route and group middleware, so route guards never protect files served by a global Static middleware. Keep protected files outside its root directory, or serve them with Echo#Static behind the guard.
Frequently Asked Questions
- What is CVE-2026-118650? CVE-2026-118650 is a high-severity incorrect authorization vulnerability in github.com/labstack/echo/v5 (go), affecting versions < 5.4.0. It is fixed in 5.4.0, 4.16.0. The application does not correctly enforce access controls, allowing a principal to access resources or operations beyond their granted permissions.
- How severe is CVE-2026-118650? CVE-2026-118650 has a CVSS score of 7.5 (High). This score reflects the worst-case severity of the vulnerability, not your specific exposure. Whether it represents real risk in your environment depends on whether the vulnerable code is present and reachable.
- Which packages are affected by CVE-2026-118650?
github.com/labstack/echo/v5(go) (versions < 5.4.0)github.com/labstack/echo/v4(go) (versions < 4.16.0)
- Is there a fix for CVE-2026-118650? Yes. CVE-2026-118650 is fixed in 5.4.0, 4.16.0. Upgrade to this version or later.
- Is CVE-2026-118650 exploitable, and should I be worried? Whether CVE-2026-118650 is exploitable in your environment depends on whether the vulnerable code is present and reachable. A CVSS score is a worst-case rating; it does not account for your specific deployment, configuration, or usage patterns. Kodem, an Intelligent Application Security platform, uses runtime intelligence to show which vulnerabilities actually execute in production, so you can focus on the ones that represent real risk. Get a demo
- What actually determines whether CVE-2026-118650 is exploitable, and how bad it is? Exploitability and impact are not fixed properties of a CVE. They depend on runtime truth: whether the vulnerable code is present, reachable, and actually executes in your application. A high CVSS score on a dependency that never runs is not the same as real risk. Kodem, an Intelligent Application Security platform, uses runtime intelligence to reveal which vulnerabilities actually execute in production, so teams prioritize the ones that genuinely matter.
- How do I fix CVE-2026-118650?
- Upgrade
github.com/labstack/echo/v5to 5.4.0 or later - Upgrade
github.com/labstack/echo/v4to 4.16.0 or later
- Upgrade