Keep reserved prefixes out of the SPA fallback under dot segments
Independent review Not signed off — 1 high (#4)
Independent review Not signed off — 1 high (#4)
Address review findings F1 and F2: - F1: check reserved prefixes against the request path before and after cleaning, so /auth/../quests/42 and /api%2F..%2Fquests/42 get the JSON 404 instead of index.html. Add frontend and router tests for both. - F2: add an unhashed asset (robots.txt) to the frontend fixture and assert its status, body and immutable Cache-Control. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -48,7 +48,8 @@ func Assets() fs.FS {
|
||||
// NewHandler returns a handler that serves files from fsys. A GET or HEAD for a
|
||||
// path that is not a file in fsys is answered with index.html so client-side
|
||||
// routes load the app. Paths equal to or under a reserved prefix (for example
|
||||
// "/api") are never answered with index.html; they are passed to notFound.
|
||||
// "/api") are never answered with index.html, whether before or after the path
|
||||
// is cleaned; they are passed to notFound.
|
||||
// fsys must contain index.html.
|
||||
func NewHandler(fsys fs.FS, logger *slog.Logger, notFound http.Handler, reserved ...string) (http.Handler, error) {
|
||||
if _, err := fs.Stat(fsys, indexFile); err != nil {
|
||||
@@ -66,7 +67,9 @@ type handler struct {
|
||||
|
||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
urlPath := path.Clean("/" + r.URL.Path)
|
||||
if h.isReserved(urlPath) {
|
||||
// Check the uncleaned path too, so dot segments cannot escape a reserved
|
||||
// prefix into the fallback ("/auth/../quests/42" cleans to "/quests/42").
|
||||
if h.isReserved(r.URL.Path) || h.isReserved(urlPath) {
|
||||
h.notFound.ServeHTTP(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user