Skip to content

solid-v2/fullstack, fullstack-tanstack: server.js rejects the [...404] route chunk; check path containment instead of ".." - #305

Open
brenelz wants to merge 1 commit into
solidjs:mainfrom
brenelz:fullstack-server-traversal-guard
Open

solid-v2/fullstack, fullstack-tanstack: server.js rejects the [...404] route chunk; check path containment instead of ".."#305
brenelz wants to merge 1 commit into
solidjs:mainfrom
brenelz:fullstack-server-traversal-guard

Conversation

@brenelz

@brenelz brenelz commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

server.js in both fullstack templates guards static asset serving with !url.includes('..'). A catch-all route src/routes/[...404].tsx builds to dist/client/assets/_...404_-<hash>.js (Vite's chunk-name sanitizing turns the brackets into _ and keeps the dots), so the chunk's URL contains .., the request falls through to handleRequest, and the SSR handler answers a request for JavaScript with the 404 page's HTML. The module preload fails and the 404 page hydrates dead under node server.js. vite preview is unaffected.

Reproduced 2026-09-10 with solid-js 2.0.0-rc.6, @solidjs/router 2.0.0-next.21, @solidjs/vite-plugin 3.0.0-next.38 (vite build, then pnpm start):

request before after
GET /assets/_...404_-DdAS84x5.js 404 text/html 200 application/javascript
GET /assets/index-CbEfGLRp.js?v=1 200 application/octet-stream 200 application/javascript
GET /../server/server.js, /%2e%2e/%2e%2e/package.json, /assets/..%2f..%2f..%2fpackage.json, /%, /assets/%00.js SSR 404 page SSR 404 page

The fix maps the decoded pathname onto dist/client with path.join and serves only when the resolved path starts with <clientDir>/; misses still fall through to handleRequest. The MIME type now comes from the resolved file instead of the raw URL, which is what fixed the query-string row. Both server.js copies stay byte-identical (fullstack-tanstack has no catch-all route file, but it shares the guard). No README text describes the guard, so no docs change.

Verified in both templates: vitest run green (10 each), vite build clean, prod servers boot. In a browser against the fullstack prod server, /some-missing-page preloads the _...404_ chunk (200) and the 404 page hydrates: clicking Home is a client-side navigation (a window marker set on the 404 page survives it) and the Counter increments.

Fragility note: the .. in the chunk name comes from Vite's default chunk-name sanitizing of the route file name [...404].tsx (brackets → _, dots kept) in the build @solidjs/vite-plugin drives (Vite 8 / rolldown). Any static host, CDN rule, or hand-written server with a "reject .. in the path" guard refuses that chunk the same way. A sanitizer that also collapsed runs of dots would remove the whole class of problem upstream; that is probably worth an issue on the plugin or Vite, not attempted here.

🤖 Generated with Claude Code

…tring guard with resolved-path containment

server.js guarded static asset serving with `!url.includes('..')`. A
catch-all route `src/routes/[...404].tsx` builds to the client chunk
`dist/client/assets/_...404_-<hash>.js`, whose URL contains `..`, so the
request fell through to the SSR handler, which answered with the 404
page's HTML instead of JavaScript. The module preload failed and the 404
page never hydrated under `node server.js` (`vite preview` was
unaffected). Reproduced with solid-js 2.0.0-rc.6, @solidjs/router
2.0.0-next.21, @solidjs/vite-plugin 3.0.0-next.38:
`curl -i /assets/_...404_-<hash>.js` -> HTTP 404 text/html.

The guard now maps the decoded pathname onto dist/client with path.join
and serves only when the resolved path stays under that directory.
Everything else — `..`, `%2e%2e`, `..%2f`, malformed percent-encoding —
falls through to handleRequest as before. Side effect: the MIME type is
taken from the resolved file rather than the raw URL, so
`/assets/x.js?v=1` is application/javascript instead of octet-stream.

Both server.js copies stay identical. fullstack-tanstack has no
catch-all route file but shares the guard.

Verified in both templates: vitest suites green (10 each), `vite build`
clean, prod servers boot. fullstack: the `_...404_` chunk is served as
application/javascript, /some-missing-page preloads it, and the 404 page
hydrates (client-side navigation to / keeps window state; Counter
increments). Traversal probes get the SSR 404 page, never file contents.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant