Skip to content

Commit ec79b58

Browse files
authored
Merge pull request #3020 from aldas/v4_v4-15-4_changelog
Changelog for v4.15.4 - security fix
2 parents 13f0ed1 + 2714c07 commit ec79b58

2 files changed

Lines changed: 50 additions & 1 deletion

File tree

CHANGELOG.md

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,54 @@
11
# Changelog
22

3+
## v4.15.4 - 2026-06-15
4+
5+
**Security**
6+
7+
Fixes [GHSA-vfp3-v2gw-7wfq](https://github.com/labstack/echo/security/advisories/GHSA-vfp3-v2gw-7wfq)
8+
9+
Make serving static file releated methods and middleware not unescape path by default - so how the way Router interprets paths and Static methods/middleware is consistent.
10+
11+
Given following situation:
12+
```go
13+
// 0.
14+
// given folder structure:
15+
// private.txt
16+
// public/
17+
// public/index.html
18+
// public/text.txt
19+
// public/admin/private.txt
20+
21+
// 1. share `public/` folder contents from the server root. This folder actually contains subfolder `admin` which
22+
// contents we want to forbid from downloading
23+
e.Static("/", "public")
24+
25+
// 2. naively assume that everything under /admin folder is now forbidden
26+
e.GET("/admin/*", func(c *Context) error {
27+
return ErrForbidden
28+
})
29+
```
30+
31+
Then requests to `/admin%2fprivate.txt` would not be matched to `GET /admin/*` route (routing does not look unescaped path) and static file serving will use unescaped path to serve the file.
32+
33+
Note: this way of "guarding" subfolders will never work for for paths like `/assets/../admin%2fprivate.txt` which will `path.Clean("/assets/../admin%2fprivate.txt")` to `/admin/private.txt` and are servable if static file serving is configured to unescape paths.
34+
35+
If you want to guard routes - use middlewares on `Static*` methods and before `Static` middleware.
36+
37+
**Breaking change / migration:** If you serve files whose names contain URL-encoded characters (e.g., `/hello%20world.txt``hello world.txt`), you must now opt in:
38+
39+
```go
40+
e := echo.New()
41+
e.EnablePathUnescapingStaticFiles = true // <-- enable old behavior
42+
e.Static("/", "public")
43+
```
44+
for static middleware
45+
```go
46+
e.Use(middleware.StaticWithConfig(middleware.StaticConfig{
47+
EnablePathUnescaping: true, // <-- enable old behavior
48+
}))
49+
```
50+
51+
352
## v4.15.3 - 2026-06-14
453

554
**Security**

echo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -279,7 +279,7 @@ const (
279279

280280
const (
281281
// Version of Echo
282-
Version = "4.15.3"
282+
Version = "4.15.4"
283283
website = "https://echo.labstack.com"
284284
// http://patorjk.com/software/taag/#p=display&f=Small%20Slant&t=Echo
285285
banner = `

0 commit comments

Comments
 (0)