Skip to content

Commit 0e0d1c6

Browse files
vishrclaude
andcommitted
test(static): add percent-encoded traversal-protection case (#2599)
Companion test confirming that defaulting pathUnescape to false does not weaken traversal protection — single-, double-, and dot-encoded "../" all stay within the served root. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 05021d3 commit 0e0d1c6

1 file changed

Lines changed: 23 additions & 0 deletions

File tree

middleware/static_percent_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,26 @@ func TestStatic_servesFileWithPercentInName(t *testing.T) {
3939
assert.Equal(t, want, rec.Body.String(), "GET %s should return the file contents", url)
4040
}
4141
}
42+
43+
// Companion to #2599: not unescaping the already-decoded path must not weaken
44+
// traversal protection. A percent-encoded "../" must not escape the served root
45+
// (and notably must not be re-assembled from double-encoded input, as the old
46+
// double-unescape could do).
47+
func TestStatic_percentEncodedTraversalIsBlocked(t *testing.T) {
48+
e := echo.New()
49+
e.Use(StaticWithConfig(StaticConfig{
50+
Root: "public",
51+
Filesystem: fstest.MapFS{
52+
"public/page.txt": &fstest.MapFile{Data: []byte("public page")},
53+
"secret.txt": &fstest.MapFile{Data: []byte("SECRET")},
54+
},
55+
}))
56+
57+
for _, url := range []string{"/..%2fsecret.txt", "/..%252fsecret.txt", "/%2e%2e%2fsecret.txt"} {
58+
req := httptest.NewRequest(http.MethodGet, url, nil)
59+
rec := httptest.NewRecorder()
60+
e.ServeHTTP(rec, req)
61+
assert.NotEqual(t, http.StatusOK, rec.Code, "GET %s must not escape the served root", url)
62+
assert.NotContains(t, rec.Body.String(), "SECRET", "GET %s must not leak files above the root", url)
63+
}
64+
}

0 commit comments

Comments
 (0)