Skip to content

fix: path traversal guard + SPA method check#85

Merged
chitcommit merged 1 commit intomainfrom
fix/review-feedback-83
Apr 8, 2026
Merged

fix: path traversal guard + SPA method check#85
chitcommit merged 1 commit intomainfrom
fix/review-feedback-83

Conversation

@chitcommit
Copy link
Copy Markdown
Contributor

@chitcommit chitcommit commented Apr 8, 2026

Summary

  • Addresses review feedback from PR fix: serve built frontend in production node server #83 (CodeRabbit + Copilot)
  • Adds path traversal guard: resolved paths must stay within publicDir
  • SPA fallback only returns index.html for GET requests; POST/PUT/DELETE to unknown routes return 404

Test plan

  • GET / → 200 HTML
  • GET /assets/*.css → 200 correct MIME
  • GET /dashboard → 200 SPA fallback
  • POST /nonexistent → 404 (not HTML)
  • Path traversal (/../../server/dev.ts) → blocked by guard
  • GET /health → 200 JSON

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Improved security by validating asset request paths to prevent directory traversal and unauthorized access
    • Refined single-page application fallback behavior to return proper 404 responses for non-GET requests instead of serving the index file

- Validate resolved file paths stay within publicDir (prevents traversal)
- Only return SPA fallback HTML for GET requests (POST/PUT → 404)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings April 8, 2026 06:45
@chitcommit chitcommit enabled auto-merge (squash) April 8, 2026 06:45
@github-actions
Copy link
Copy Markdown
Contributor

github-actions bot commented Apr 8, 2026

@coderabbitai review

Please evaluate:

  • Security implications
  • Credential exposure risk
  • Dependency supply chain concerns
  • Breaking API changes

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 8, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 5aa0a47c-da5e-4c4d-bcb7-709c1944cd0d

📥 Commits

Reviewing files that changed from the base of the PR and between b9e4b11 and 5393e1f.

📒 Files selected for processing (1)
  • server/dev.ts

📝 Walkthrough

Walkthrough

The change adds path traversal protection to the asset-serving handler by validating that resolved filesystem paths remain within publicDir, returning 403 Forbidden for escaped paths. Additionally, SPA fallback to index.html is now restricted to GET requests; other HTTP methods return 404 instead.

Changes

Cohort / File(s) Summary
Path Validation & SPA Fallback Restrictions
server/dev.ts
Added filesystem path validation to prevent directory traversal attacks by ensuring resolved paths stay within publicDir (403 response if escaped). Restricted SPA fallback behavior to GET requests only; non-GET requests now return 404 instead of serving index.html.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hops through paths with vigilant care,
No traversals escape this fair share,
GET requests dance where index may dwell,
Others return 404 quite well!
Security strengthened, one commit at a time.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/review-feedback-83

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@cloudflare-workers-and-pages
Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Updated (UTC)
✅ Deployment successful!
View logs
chittyfinance 5393e1f Apr 08 2026, 06:46 AM

@chitcommit chitcommit merged commit 62cfd87 into main Apr 8, 2026
11 of 13 checks passed
@chitcommit chitcommit deleted the fix/review-feedback-83 branch April 8, 2026 06:46
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5393e1fe53

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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) {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Use platform-safe path prefix check

The traversal guard compares filePath against publicDir + '/', which assumes POSIX separators. In server/dev.ts, both resolve() 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 whenever NODE_ENV=production is 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 👍 / 👎.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR hardens static asset serving in the Node production entrypoint by adding a path traversal guard and tightening SPA fallback behavior so that only GET requests can receive index.html.

Changes:

  • Add a path traversal check to ensure resolved asset paths remain within publicDir.
  • Restrict SPA fallback to GET requests so non-GET unknown routes return 404.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 57 to +61
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 });
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The path-traversal guard is implemented via a string prefix check (filePath.startsWith(publicDir + '/')). This is brittle (e.g., depends on a hard-coded '/' separator and can break on non-POSIX paths) and is easier to get wrong than a path-based containment check. Consider using a path.relative(publicDir, filePath)-based check (and/or canonicalizing with realpath) to reliably ensure the target is inside publicDir before calling statSync/readFileSync.

Copilot uses AI. Check for mistakes.
Comment on lines +69 to +72
// 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' } });
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The SPA fallback will return index.html for any GET that isn't a file on disk. That means missing static assets like /assets/missing.css will incorrectly return HTML with a 200 instead of a 404, which can break caching and make asset errors harder to detect. Consider restricting the SPA fallback to "route" paths only (e.g., exclude /assets/ and/or only fallback when the path has no file extension or when the request Accept prefers text/html).

Copilot uses AI. Check for mistakes.
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.

2 participants