Preserve the query string on the static directory redirect - #3123
januththedev wants to merge 1 commit into
Conversation
|
Thanks — this looks right to me. I independently checked the claims rather than just reading the diff, and I have a few concrete notes. Verified
Not verified by me (so I'm not claiming it)
One CI noteThe PR currently reports LGTM otherwise: small, well-scoped, matches the behaviour of the standard library it's mirroring, and the failure mode it fixes (a permanent, client-cached redirect quietly losing parameters) is exactly the kind of thing that stays broken for years. |
|
Thanks — that is a much better review than I expected, and all three points were worth making. I've updated the description:
One thing I did not verify and want to be honest about: I also cited Thank you also for confirming |
Preserve the query string on the static directory redirect
Description
StaticDirectoryHandlerbuilds the "directory without trailing slash → add trailing slash" 301 redirect fromc.Request().URL.Pathonly:URL.Pathexcludes the query, so the redirect target silently loses it.Why it's wrong
net/http.localRedirect— used byhttp.FileServer,http.ServeMuxandhttp.ServeFile— explicitly appendsr.URL.RawQuery:if q := r.URL.RawQuery; q != "" { newPath += "?" + q }. (Citingsrc/net/http/fs.go,func localRedirect, rather than a line range, since those lines drift between Go releases.)AddTrailingSlash/RemoveTrailingSlashmiddleware already does this correctly (middleware/slash.go:64-70buildsuri += "?" + qs), so the static handler is inconsistent with the framework's own convention.GET /folder?v=2becomes a permanent redirect to/folder/, and every subsequent request is served the wrong or an unparameterized variant. The damage is sticky, not transient.Echo.Static,Echo.StaticFS,Group.StaticandGroup.StaticFS— all four funnel throughStaticDirectoryHandler.This is the directory-redirect branch only; the
middleware/static.govariant does not redirect at all, so it is unaffected.The fix
sanitizeURIstill runs over the whole assembled URI, so the open-redirect guard is preserved. Worth answering the obvious objection directly:sanitizeURIonly percent-encodes C0 control characters and DEL viaescapeControlChars, and collapses a leading//,\\or\/— it never parses or unescapes the URI, so it cannot rewrite, reorder or drop the query. It is safe to run over the assembledpath + "?" + query.Tests
One case appended to the existing
TestEcho_StaticFStable, asserting a 301 whoseLocationis/folder/?foo=bar&baz=1for a request to/folder?foo=bar&baz=1.Not equal: expected "/folder/?foo=bar&baz=1" actual "/folder/". After: passes.go test ./... -count=1: baseline on unmodified HEAD was 660 top-level PASS / 1709 subtests PASS, 0 FAIL; after the change 660 / 1710 / 0 FAIL — exactly one new passing subtest, zero regressions.go vet ./...clean.TestEcho_StaticDirectoryRedirect_controlCharactersand the GHSA-3pmx-cf9f-34xr path-traversal cases still pass, confirming the security guard is intact.I checked the open issues and PRs on labstack/echo and searched the static-handler area: no existing report or PR. The nearest open static PR is #2991 (HTML5 route fallback), unrelated.
Several real neighbouring defects are already being worked on upstream and I left them alone: the router panic on
\:+:paramat the same position (#3111/#3113),Context.FileFSnil-deref whenStaterrors (#3115), theBodyLimitbypass on a single oversizedRead(#3071, PRs #3072/#3087/#3092), gzip compressing whenq=0is requested (#3021), andGzip.Writereturning buffer length rather thanlen(b)(#2981).randomString's apparentuint8overflow is unreachable —randomStringMaxByteis 207, so the counter can never wrap.