-
Notifications
You must be signed in to change notification settings - Fork 0
fix: path traversal guard + SPA method check #85
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -55,15 +55,21 @@ const stubAssets = { | |
| ? async (req: Request) => { | ||
| const url = new URL(req.url); | ||
| const filePath = resolve(publicDir, url.pathname.replace(/^\//, '')); | ||
| // Prevent path traversal — resolved path must stay inside publicDir | ||
| if (!filePath.startsWith(publicDir + '/') && filePath !== publicDir) { | ||
| return new Response('Forbidden', { status: 403 }); | ||
| } | ||
|
Comment on lines
57
to
+61
|
||
| try { | ||
| if (statSync(filePath).isFile()) { | ||
| const body = readFileSync(filePath); | ||
| const mime = getMimeType(filePath) || 'application/octet-stream'; | ||
| return new Response(body, { headers: { 'content-type': mime } }); | ||
| } | ||
| } catch {} | ||
| // SPA fallback: return index.html for non-file routes | ||
| if (indexHtml) return new Response(indexHtml, { headers: { 'content-type': 'text/html; charset=utf-8' } }); | ||
| // SPA fallback: return index.html only for GET requests | ||
| if (req.method === 'GET' && indexHtml) { | ||
| return new Response(indexHtml, { headers: { 'content-type': 'text/html; charset=utf-8' } }); | ||
| } | ||
|
Comment on lines
+69
to
+72
|
||
| return new Response('Not found', { status: 404 }); | ||
| } | ||
| : async () => new Response('Not found', { status: 404 }), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The traversal guard compares
filePathagainstpublicDir + '/', which assumes POSIX separators. Inserver/dev.ts, bothresolve()outputs use backslashes on Windows (for example,C:\app\public\...), so this condition rejects valid in-tree files and returns 403 for most asset and SPA routes wheneverNODE_ENV=productionis used on Windows. This is a functional regression introduced by the new guard; use a separator-agnostic check (e.g.,path.relative-based) instead of hardcoding'/'.Useful? React with 👍 / 👎.