Repository navigation
build(deps): update module github.com/labstack/echo/v5 to v5.4.1 [security] - #83
Merged
mergify[bot] merged 1 commit intoOct 29, 2026
Conversation
Contributor
✅ Merged into
|
mergify
Bot
deleted the
renovate/go-github.com-labstack-echo-v5-vulnerability
branch
October 29, 2026 00:37
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Note
This PR body was truncated due to platform limits.
This PR contains the following updates:
v5.0.0→v5.4.1Echo has a Windows path traversal via backslash in middleware.Static default filesystem
CVE-2026-25766 / GHSA-pgvm-wxw2-hrv9 / GO-2026-4502
More information
Details
Summary
On Windows, Echo’s
middleware.Staticusing the default filesystem allows path traversal via backslashes, enablingunauthenticated remote file read outside the static root.
Details
In
middleware/static.go, the requested path is unescaped and normalized withpath.Clean(URL semantics).path.Cleandoes not treat\as a path separator, so..\sequences remain in the cleaned path. The resultingpath is then passed to
currentFS.Open(...). When the filesystem is left at the default (nil), Echo usesdefaultFSwhich calls
os.Open(echo.go:792). On Windows,os.Opentreats\as a path separator and resolves..\,allowing traversal outside the static root.
Relevant code:
middleware/static.go(path unescape +path.Clean+currentFS.Open)echo.godefaultFS.Open→os.OpenThis is the same class as CVE-2020-36565 (fixed in v4 by switching to OS-aware cleaning), but in v5 the
path.CleanPoC
Windows only.
Sample code (main.go):
Static file:
public/index.html
(content can be any HTML)
Run:
go run .
Verify:
curl http://localhost:1323/index.html
curl --path-as-is "http://localhost:1323/..%5c..%5cWindows%5cSystem32%5cdrivers%5cetc%5chosts"
Expected: 404
Screenshot:


Impact
Path traversal leading to arbitrary file read outside the static root. Any unauthenticated remote user can
read local files that the Echo process has access to on Windows, if
middleware.Staticis used with the defaultfilesystem.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:L/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo has a Windows path traversal via backslash in middleware.Static default filesystem in github.com/labstack/echo/v5
CVE-2026-25766 / GHSA-pgvm-wxw2-hrv9 / GO-2026-4502
More information
Details
Echo has a Windows path traversal via backslash in middleware.Static default filesystem in github.com/labstack/echo/v5
Severity
Unknown
References
This data is provided by OSV and the Go Vulnerability Database (CC-BY 4.0).
Echo has Rate Limiter Bypass via Spoofed X-Forwarded-For Header in Default Configuration
CVE-2026-118653 / GHSA-246p-cpwv-v3jq
More information
Details
Summary
When
echo.New()is used without explicitly settingEcho#IPExtractor, theRealIP()method falls back to reading the client-suppliedX-Forwarded-For(orX-Real-IP) header without any proxy-origin validation. The defaultRateLimiter()middleware derives its per-client identifier fromctx.RealIP(), so an attacker can rotate arbitrary values in theX-Forwarded-Forheader on each request to obtain a fresh rate-limit bucket and bypass IP-based throttling entirely.Affected Component
context.go—(*Context).RealIP()(lines 186–208), legacy fallback branch (lines 190–200)middleware/rate_limiter.go—DefaultRateLimiterConfig.IdentifierExtractor(lines 46–58);RateLimiter()(lines 71–76)echo.go—New()(lines 326–343),IPExtractorleftnilby defaultDescription
RealIP()unconditionally trustsX-Forwarded-ForwhenIPExtractoris unsetContext.RealIP()(context.go:186-208) first checksc.echo.IPExtractor. When this field isnil(the default forecho.New()), it falls through to legacy behavior and returns the first IP from theX-Forwarded-Forheader — the position an attacker has full control over:Rate limiter default identifier extractor calls
RealIP()unconditionallyDefaultRateLimiterConfig(rate_limiter.go:46-58) sets the identifier extractor toctx.RealIP()with no guard for whether a safeIPExtractoris configured:RateLimiter(store)(rate_limiter.go:71-76) copiesDefaultRateLimiterConfigdirectly:The result is that the most common usage pattern —
e.GET("/path", h, middleware.RateLimiter(store))— produces a rate limiter whose per-client identifier is fully controlled by the requester.Framework acknowledges the issue for IP extraction but the rate limiter carries no warning
ip.go:27explicitly states the default is insecure:And
ip.go:117-119:The
IPExtractormechanism (ExtractIPDirect(),ExtractIPFromXFFHeader(),ExtractIPFromRealIPHeader()) inip.goexists precisely to close this gap. However, theRateLimiter()function andDefaultRateLimiterConfighave no corresponding warning and make no attempt to detect or flag thatIPExtractoris unset, leaving operators who follow the standard rate limiter example silently exposed.The test
TestRateLimiterWithConfig_defaultConfig(rate_limiter_test.go:181-226) itself drives the rate limiter by settingX-Real-IPdirectly on the test request — demonstrating that the identifier is freely injectable without comment or mitigation.Full execution chain
echo.New()(noIPExtractorset) ande.GET("/limited", h, middleware.RateLimiter(store))GET /limitedwith headerX-Forwarded-For: 1.2.3.4— rate bucket for1.2.3.4consumedGET /limitedagain withX-Forwarded-For: 1.2.3.4— second token consumed; if burst is 1, next request would be 429GET /limitedwithX-Forwarded-For: 5.6.7.8— new bucket created for5.6.7.8, 200 OK returnedProof of Concept
Impact
middleware.RateLimiter()using the default configuration by rotating theX-Forwarded-Forheaderctx.RealIP()with default configuration (access logging, geo-blocking, audit trails) is equally underminedRecommended Remediation
Option 1: Require explicit
IPExtractorconfiguration inRateLimiter()(preferred)The
RateLimiter()convenience function should detect whenIPExtractoris not configured and either panic with a clear message or fall back toExtractIPDirect()(direct-connection IP only):Option 2: Document and warn in
RateLimiter()godocAt minimum, add a prominent warning to the
RateLimiter()andDefaultRateLimiterConfigdocumentation directing users to configureEcho#IPExtractorbefore using IP-based rate limiting, mirroring the guidance already present inip.go.Credit
This vulnerability was discovered and reported by bugbunny.ai.
Patches
Fixed in
github.com/labstack/echo/v5v5.1.0: withoutEcho#IPExtractor,Context.RealIP()returns the address of the direct peer; the previous behavior is available asLegacyIPExtractor.github.com/labstack/echo/v4keeps its default for compatibility.Workarounds
Configure
Echo#IPExtractorfor your deployment (echo.ExtractIPDirect()without a proxy,echo.ExtractIPFromXFFHeader()behind proxies, withTrustIPRangefor proxies on public addresses), or setRateLimiterConfig.IdentifierExtractorto an identifier that clients cannot choose.Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:L/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo has Arbitrary Scheme Injection in WWWRedirect/NonWWWRedirect Middlewares via X-Forwarded-Proto
CVE-2026-118658 / GHSA-23wf-92cp-8pcj
More information
Details
Summary
The
c.Scheme()method in Echo (context.go:162-181) trusts theX-Forwarded-Proto,X-Forwarded-Protocol, andX-Url-Schemerequest headers without validating that the value ishttporhttps. TheWWWRedirect()andNonWWWRedirect()middlewares use this unsanitized scheme to construct redirect URLs, allowing an attacker to inject arbitrary URI schemes into theLocationresponse header.Affected Components
context.go:162-181-Scheme()method returns attacker-controlled value verbatimmiddleware/redirect.go:172-184-redirectWWW/redirectNonWWWuse unsanitized schemeProof of Concept
Confirmed injectable schemes:
javascript,data,vbscript,file,ftp,ssh,telnet,gopher.Only
WWWRedirect()andNonWWWRedirect()(andWithConfigvariants) are affected.HTTPSRedirect,HTTPSWWWRedirect,HTTPSNonWWWRedirectare NOT affected (they hardcodehttps://).Impact
X-Forwarded-Protofile://scheme injection could trigger file access in some browser configurationsSuggested Fix
Whitelist scheme in
c.Scheme()to only returnhttporhttps:Disclosure
Source code review, manually verified with actual HTTP server. Tested on Echo v5.0.4 (latest master).
Patches
Fixed in
github.com/labstack/echo/v5v5.1.1 andgithub.com/labstack/echo/v4v4.15.2.Context.Scheme()now accepts onlyhttp,https,wsorwssfrom theX-Forwarded-Proto,X-Forwarded-ProtocolandX-Url-Schemeheaders, and falls back tohttpfor any other value (#2953).Workarounds
Upgrade to a patched version. If you cannot upgrade, have your edge proxy overwrite or remove the
X-Forwarded-Proto,X-Forwarded-ProtocolandX-Url-Schemerequest headers.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:L/I:L/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo: Encoded slash (%2F) bypasses route-level protection and exposes static files
CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq / GO-2026-6293
More information
Details
Summary
Echo's router and static file handler disagree on URL path decoding. The router matches routes using the raw encoded path (preserving
%2Fas-is), whileStaticDirectoryHandlerunescapes%2Fto/before resolving filesystem paths. This allows an attacker to bypass route-level access controls and read static files without authorization.Details
Root cause 1 —
router.golines 798-802:The router uses
req.URL.RawPathfor route matching whenuseEscapedPathForRoutingis false (the default). This means/admin%2Fsecret.txtis treated as a single path segment and does NOT match the/admin/*route pattern.Root cause 2 —
echo.golines 559-568:StaticDirectoryHandlercallsurl.PathUnescape()on the path parameter before opening files. This converts%2Fback to/, resolvingadmin/secret.txton disk.PoC (Screenshot)
Sample:

403:

Bypass with encoded slash:

Impact
Unauthorized static file disclosure. Applications that protect route prefixes with authentication middleware while also serving static files from a broader root are vulnerable. An attacker only needs to encode the slash (
/→%2F) in the URL to bypass all route-level protection.Common affected pattern:
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo has a Static Middleware Double-Unescape Path Traversal
CVE-2026-118655 / GHSA-xwq5-6cqp-vx96
More information
Details
Summary
Commit a9ede66a (PR #3006, issue #2599) silently fixed a path traversal vulnerability in the Echo web framework's static file middleware.
Vulnerability
The static middleware double-unescaped URL paths. Go's net/http already decodes Request.URL.Path, but middleware/static.go unconditionally called url.PathUnescape() again:
This reassembled path traversal sequences from double-encoded input. An attacker could send GET /%2e%2e%2fsecret.txt or /..%252fsecret.txt and the double-unescape would produce ../secret.txt.
Fix
The commit's test TestStatic_percentEncodedTraversalIsBlocked explicitly verifies traversal vectors are blocked.
Impact
Arbitrary file read from server filesystem via percent-encoded path traversal.
References
Patches
Fixed in
github.com/labstack/echo/v5v5.2.0 andgithub.com/labstack/echo/v4v4.15.3, where the Static middleware stopped unescaping an already decoded path. Maintainer analysis: reading files outside the configured root could not be reproduced; the confirmed impact was that a double-encoded slash (%252F) could reach a file under a guarded route when the Static middleware is registered globally. Upgrade to v5.4.0 / v4.16.0 or later, which also includes related fixes for encoded and dot-segment paths.Workarounds
Keep files that need protection outside the Static middleware's root directory.
Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Encoded slash (%2F) bypasses route-level protection and exposes static files in github.com/labstack/echo/v4 and github.com/labstack/echo/v5
CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq / GO-2026-6293
More information
Details
Encoded slash (%2F) bypasses route-level protection and exposes static files in github.com/labstack/echo/v4 and github.com/labstack/echo/v5
Severity
Unknown
References
This data is provided by OSV and the Go Vulnerability Database (CC-BY 4.0).
Echo: HTTPS Redirect and HSTS Bypass via Unconditional Trust of Forwarded Scheme Headers
CVE-2026-118657 / GHSA-2ffq-g2xg-c22p
More information
Details
Description
Context.Scheme()unconditionally trusts four client-controlled HTTP headers to determine the request scheme, with no mechanism to configure trusted sources. This allows a remote unauthenticated attacker to bypass HTTPS redirect enforcement and suppress HSTS headers.The trusted headers (checked in this order of precedence):
X-Forwarded-ProtoX-Forwarded-ProtocolX-Forwarded-SslX-Url-SchemeThreat model
Scenario A — Directly exposed application (vulnerable by default):
The Echo application listens on HTTP and is reachable directly by clients (no L7 proxy in front), or an attacker can bypass the proxy and reach the backend directly. In this case, the attacker has full control over all request headers. Sending
X-Forwarded-Proto: httpsdefeats theHTTPSRedirectmiddleware entirely — traffic stays on plaintext HTTP, enabling man-in-the-middle attacks.This is the primary scenario. The application is vulnerable by default with no misconfiguration required — simply using
e.Pre(middleware.HTTPSRedirect())as documented is sufficient.Scenario B — Behind L7 proxy with header sanitization (reduced risk):
If the application is strictly behind a correctly configured L7 proxy that always overwrites
X-Forwarded-Protoand there is no direct access to the backend, the practical risk is lower. However, this relies on perimeter controls external to the framework, not on the framework itself. As Echo's own IP-extraction documentation states: "you must not trust HTTP headers unconditionally."Root cause
context.go, lines 161–181:Two additional issues with this implementation:
No value validation.
Scheme()returns any arbitrary string from the header, not just"http"or"https". This breaks the documented contract ("returns the HTTP protocol scheme,httporhttps") and causes unexpected behavior in middleware that uses the return value to construct URLs (e.g.,WWWRedirectbuildsscheme + "://www." + host + uri).Consumed by security-critical middleware. The return value is used directly by redirect middleware (
middleware/redirect.go:136) and secure middleware (middleware/secure.go:125) for security enforcement decisions.Affected components
Context.Scheme()context.goHTTPSRedirectmiddleware/redirect.goHTTPSWWWRedirectmiddleware/redirect.goHTTPSNonWWWRedirectmiddleware/redirect.goWWWRedirectmiddleware/redirect.goNonWWWRedirectmiddleware/redirect.gomiddleware/secure.goThis is an unsafe-by-default API surface, not just misconfiguration
This behavior is not accidental — it is codified in the project's own test suite. For example,
middleware/redirect_test.go(lines 29–34) explicitly asserts thatX-Forwarded-Proto: httpson an HTTP request results in 200 OK (no redirect):{ whenHost: "labstack.com", whenHeader: map[string][]string{echo.HeaderXForwardedProto: {"https"}}, expectLocation: "", expectStatusCode: http.StatusOK, },Similarly,
middleware/secure_test.go(lines 42–53) andcontext_test.go(lines 1104–1111) assert that scheme is derived from the header.The problem is not that the framework can use forwarded headers — that is legitimate behind a trusted proxy. The problem is that there is no mechanism to control whether this trust is appropriate, unlike the existing IP header trust model.
Scheme()always trusts the headers, making any middleware that depends on it unsafe-by-default.Inconsistency with existing trust model
Echo already recognizes this class of header-trust vulnerability for IP addresses. The
IPExtractormechanism withTrustOption(TrustLoopback,TrustPrivateNet,TrustLinkLocal,TrustIPRange) was introduced specifically because:No equivalent mechanism exists for scheme detection.
Scheme()unconditionally trusts all four forwarded headers with no configuration, opt-in, or documentation warning.Note on proxies and the "3 additional headers"
A correctly configured nginx
proxy_set_header X-Forwarded-Proto $scheme;overwritesX-Forwarded-Protowith the actual incoming scheme. When a proxy does this,X-Forwarded-Protois trustworthy and the three fallback headers (X-Forwarded-Protocol,X-Forwarded-Ssl,X-Url-Scheme) are never reached becauseX-Forwarded-Prototakes precedence inScheme().The three additional headers become real attack vectors specifically when:
X-Forwarded-Protoat all (common in default proxy configs), orIn such cases, an attacker can bypass via any of the four headers. The main vulnerability (HTTPS redirect bypass via
X-Forwarded-Protoon a directly exposed app) does not depend on the additional headers.Impact
HTTPS downgrade / MITM: An attacker who can reach the Echo HTTP endpoint directly can prevent HTTPS redirection, keeping client traffic on plaintext HTTP and enabling man-in-the-middle attacks.
HSTS suppression: Behind a TLS-terminating proxy, spoofing
X-Forwarded-Proto: httpprevents theStrict-Transport-Securityheader from being sent. When the response ultimately reaches the client over HTTPS (via the proxy), the missing HSTS header weakens browser-side HTTPS enforcement. Note: browsers ignore HSTS received over plain HTTP, so the practical risk is specifically in the suppression scenario behind a TLS-terminating proxy, not in injecting HSTS on HTTP.URL construction with arbitrary scheme:
WWWRedirectandNonWWWRedirectusec.Scheme()to build redirect URLs (scheme + "://www." + host + uri). An attacker can inject arbitrary strings (e.g.,javascript:,data:) as the scheme component of redirect URLs.Proof of Concept
Tested on Echo v5.0.4 (commit
fa5311b, 2026-02-15).PoC 1: HTTPS Redirect Bypass (Go test)
PoC 2: curl examples (easiest to reproduce)
Start a minimal Echo server:
Then:
PoC 3: Arbitrary scheme in redirect URL (WWWRedirect)
PoC 4: HSTS Suppression
Test output (all attacks succeed)
Precedent CVEs
X-Forwarded-Protoheader trusted for scheme, enabling HTTPS enforcement bypass. Fixed by addingsanitize_forwarded_protooption.Suggested fix
Option A:
SchemeExtractor(recommended, backward-compatible with opt-in)Add a
SchemeExtractormechanism analogous to the existingIPExtractor, where the extractor decides whether to trust forwarded headers based onRemoteAddror trusted proxy ranges:Usage:
The default should be secure. For backward compatibility, a migration period with deprecation warnings for the legacy behavior may be appropriate.
Option B: Minimal hardening (does NOT fix the bypass, but fixes the contract violation)
Scheme()should validate that the returned value is"http"or"https", rejecting arbitrary strings. This prevents the "arbitrary scheme in redirect URL" issue but does not address the core HTTPS redirect bypass (because"https"is a valid value that the attacker injects).Both options should be applied together.
Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.16.0.Context.Scheme()now uses theX-Forwarded-Proto,X-Forwarded-Protocol,X-Forwarded-SslandX-Url-Schemeheaders only when the request comes directly from a loopback, link-local or private network address or a unix socket. WhenX-Forwarded-Protois present, only its last value is used. The newEcho#SchemeExtractorselects the strategy (ExtractSchemeFromHeaders,ExtractSchemeDirect,LegacySchemeExtractor). The Secure middleware sets HSTS fromContext.Scheme(), and the Proxy middleware always setsX-Forwarded-Protoand removes the other scheme headers.Workarounds
If a proxy or load balancer connects to your application from a public address, trust its address ranges after upgrading, for example
e.SchemeExtractor = echo.ExtractSchemeFromHeaders(echo.TrustIPRange(proxyRange)); otherwiseHTTPSRedirectredirects in a loop. If you cannot upgrade, make sure every request passes through a proxy that overwritesX-Forwarded-Protoand removesX-Forwarded-Protocol,X-Forwarded-SslandX-Url-Scheme, and that the application cannot be reached directly.Severity
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:U/C:H/I:L/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo's middleware.Static:
%2F-encoded path bypasses route/group middleware guarding a statically-served pathCVE-2026-118650 / GHSA-375p-5qhx-8wq4
More information
Details
Summary
When
middleware.Staticis 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 forStaticFS/StaticDirectoryHandlerin CVE-2026-55677 / GHSA-vfp3-v2gw-7wfq, but the fix did not cover themiddleware.Staticpath. In addition, theStaticConfig.EnablePathUnescapingdocstring 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: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:
%2Frouter-vs-handler mismatch, but fixed forStaticFS/StaticDirectoryHandler. Themiddleware.Staticpath was not covered — this report is that gap.PoC
Environment:
golang:1.25, module using the official release (go get github.com/labstack/echo/v5@v5.3.1, no localreplace). Static rootpublic/contains a marker filepublic/admin/secret.txt=SECRET.Minimal app (the pattern under test: static served globally + a group guard intended to protect
/admin):Requests (unauthenticated):
Observed on
v5.3.1: literal →401;%2F→200with the file contents. For contrast, serving via the (patched)e.StaticFS("/")returns404for the same%2Frequest, showing themiddleware.Staticpath was not covered by the CVE-2026-55677 fix.Impact
An unauthenticated remote user can retrieve a file served by
middleware.Staticeven 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
EnablePathUnescapingclaim) or won't-fix rather than a code vulnerability. It is filed primarily because (1) the router-vs-handler%2Finconsistency is the exact class fixed in CVE-2026-55677 but left uncovered formiddleware.Static, and (2) the docstring currently promises protection it does not provide.Patches
Fixed in
github.com/labstack/echo/v5v5.4.0 andgithub.com/labstack/echo/v4v4.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.txtor/%61dmin/secret.txtno longer reach a file under a guarded route. Paths with.,..or empty segments are no longer served.Note: middleware registered with
Echo#Useruns 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 withEcho#Staticbehind the guard.Workarounds
Keep files that need protection outside the Static middleware's root directory.
Severity
CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:NReferences
This data is provided by OSV and the GitHub Advisory Database (CC-BY 4.0).
Echo static file middleware route protection bypass via encoded dot-dot traversal
CVE-2026-118654 / GHSA-3pmx-cf9f-34xr
More information
Details
Summary
Echo v5.2.0 contains a bypass of the fix for GHSA-vfp3-v2gw-7wfq. The fix rejects encoded path separators such as
%2Fand%5Cbefore static file path unescaping, but two traversal forms still bypass route-level middleware protections for static files.The primary bypass uses encoded dot-dot segments. A request such as
/public/%2E%2E/admin/secret.txtpasses the encoded-separator guard, is unescaped topublic/../admin/secret.txt, thenpath.Cleanresolves it toadmin/secret.txt. If/admin/*is protected by middleware but the static filesystem is mounted at/, the protected file is returned through the public static route.A second bypass exists when
UseEscapedPathForMatching=true. In that mode, the router normalizes encoded separators before the static handler checks the wildcard. The handler receives a decoded wildcard with no%characters, so the encoded-separator guard does not run on the original encoded path.Impact
An unauthenticated attacker can read files that an application intended to protect with route-level middleware, when those files are also reachable through a static filesystem mount after path normalization.
The high-severity variant works with the default router configuration and requires only encoded dot-dot path segments. The direct protected route returns 403, while the public static route returns the protected file content.
The
UseEscapedPathForMatching=truevariant is lower severity because it requires a non-default router configuration.Affected versions
Tested vulnerable:
github.com/labstack/echo/v5v5.2.0masterat5786024ea4d85c01784796e8aa7aac53e007b39bNo patched version is currently known.
Policy and version checks
GitHub Private Vulnerability Reporting is enabled for
labstack/echo. The repository has a rootSECURITY.mdand accepts security reports through GitHub Security Advisories.The vulnerable code was revalidated on both current
masterand the latest release tag:master:5786024ea4d85c01784796e8aa7aac53e007b39bv5.2.0, published June 14 2026github.com/labstack/echo/v5The same focused Go tests pass on the checked out
v5.2.0/mastercommit, proving the bypass remains present in the latest released version and latest main branch at the time of reporting.Reproduction
The following application shape reproduces the high-severity bypass:
Control request:
Observed result:
Bypass request:
Observed result:
For the non-default router variant:
Bypass request:
Observed result:
Proof of concept evidence
I added focused tests against the current Echo codebase. The tests exercise both
StaticDirectoryHandlerand the static middleware path.Command:
go test -run TestHostile -v ./...Observed result: