Skip to content

fix: patch path traversal (CWE-22) and harden /config endpoint [MSRC 112301]#174

Merged
Prajwal-Microsoft merged 3 commits into
devfrom
fix/cwe-22-path-traversal-msrc-112301
Apr 6, 2026
Merged

fix: patch path traversal (CWE-22) and harden /config endpoint [MSRC 112301]#174
Prajwal-Microsoft merged 3 commits into
devfrom
fix/cwe-22-path-traversal-msrc-112301

Conversation

@Prajwal-Microsoft

Copy link
Copy Markdown

Purpose

Fixes a Path Traversal (CWE-22) security vulnerability in src/frontend/frontend_server.py reported via MSRC Case 112301 (Deadline: 8/2/2026).

The catch-all route passed user-supplied URL paths directly to os.path.join() without sanitization, allowing unauthenticated attackers to read arbitrary files on the container filesystem (/etc/passwd, application source, Azure credentials in /proc/self/environ) via URL-encoded ..%2F traversal sequences. Additionally, the /config endpoint exposed MSAL client IDs, Azure AD authority, and API scopes to any unauthenticated cross-origin caller due to allow_origins=["*"] CORS policy.

Changes

  • Path traversal fix: Use os.path.realpath() to canonicalize the resolved path and verify it stays within BUILD_DIR
  • Directory listing prevention: Replace os.path.exists() with os.path.isfile() to only serve regular files
  • CORS hardening: Replace wildcard * origin with configurable ALLOWED_ORIGINS environment variable (defaults to empty = same-origin only); restrict methods to GET
  • /config endpoint protection: Add origin-vs-host validation to block cross-origin credential reads; remove verbose fallback strings

Related

  • MSRC Case: 112301
  • ADO Bug: 39317
  • Deadline: 8/2/2026

Does this introduce a breaking change?

  • Yes
  • No

The ALLOWED_ORIGINS env var defaults to empty (same-origin), which matches the expected production behavior. Set ALLOWED_ORIGINS if explicit cross-origin access is needed.

Golden Path Validation

  • I have tested the primary workflows (the "golden path") to ensure they function correctly without errors.

Deployment Validation

  • I have validated the deployment process successfully and all services are running as expected with this change.

What to Check

Verify that the following are valid

  • Path traversal payloads (e.g. curl "http://localhost:3000/..%2F..%2Fetc%2Fpasswd") now return index.html instead of the target file
  • /config endpoint returns 403 for cross-origin requests
  • Frontend SPA routing still works correctly (all client-side routes serve index.html)
  • Static assets under /assets/ still load normally

Other Information

This is a security fix for a vulnerability reported by an external researcher. Do not share vulnerability details outside the MSRC case. Resolution deadline is 8/2/2026.

Prajwal D C and others added 2 commits April 6, 2026 15:40
…112301]

- Use os.path.realpath() to resolve symlinks and '..' sequences, then
  verify the resolved path stays within BUILD_DIR before serving files.
- Replace os.path.exists() with os.path.isfile() to prevent directory
  listing.
- Replace allow_origins=['*'] CORS wildcard with configurable
  ALLOWED_ORIGINS env var (empty = same-origin only).
- Restrict CORS allow_methods to GET.
- Add origin-vs-host check on /config to block cross-origin reads.
- Remove verbose fallback strings from /config defaults that disclosed
  deployment state.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add ALLOWED_ORIGINS to both main.bicep and main_custom.bicep so the
frontend CORS policy is automatically configured with the container
app's own FQDN during Azure deployment.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Security hardening for the frontend FastAPI server to address a reported path traversal (CWE-22) risk in the SPA catch-all route, and to reduce cross-origin exposure of the /config endpoint by tightening CORS behavior and adding request-origin validation.

Changes:

  • Canonicalize requested static paths with realpath() and ensure resolved paths remain within BUILD_DIR; only serve regular files via isfile().
  • Replace wildcard CORS with an ALLOWED_ORIGINS environment variable (defaulting to none) and restrict allowed methods to GET.
  • Add same-origin gating for /config and remove verbose “not set” fallback strings from returned config values.
  • Set ALLOWED_ORIGINS in the Bicep templates for the frontend container app.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/frontend/frontend_server.py Implements path canonicalization / file-only serving, CORS env configuration, and /config origin validation.
infra/main.bicep Adds ALLOWED_ORIGINS env var for the frontend container app deployment.
infra/main_custom.bicep Adds ALLOWED_ORIGINS env var for the frontend container app deployment (custom template).

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

from fastapi import FastAPI, Request
from fastapi.middleware.cors import CORSMiddleware
from fastapi.responses import FileResponse, HTMLResponse
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

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

HTMLResponse is imported but no longer used in this module. Removing unused imports helps keep the file lint-clean and avoids confusion about supported response types.

Suggested change
from fastapi.responses import FileResponse, HTMLResponse, JSONResponse
from fastapi.responses import FileResponse, JSONResponse

Copilot uses AI. Check for mistakes.
Comment on lines +43 to +48
# Only serve config to same-origin requests by checking the Referer/Origin
origin = request.headers.get("origin") or ""
referer = request.headers.get("referer") or ""
host = request.headers.get("host") or ""
if origin and not origin.endswith(host):
return JSONResponse(status_code=403, content={"detail": "Forbidden"})

Copilot AI Apr 6, 2026

Copy link

Choose a reason for hiding this comment

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

The same-origin check is currently origin.endswith(host), which is vulnerable to suffix-matching bypasses (e.g., https://notexample.com endswith example.com) and can also misbehave with ports/IDNs. Consider parsing the Origin/Referer URL and comparing the hostname (and port if present) for exact equality against the expected host (or against the configured ALLOWED_ORIGINS list), and remove referer if it’s not used.

Copilot uses AI. Check for mistakes.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@Prajwal-Microsoft Prajwal-Microsoft merged commit a88b880 into dev Apr 6, 2026
10 checks passed
@github-actions

github-actions Bot commented Apr 8, 2026

Copy link
Copy Markdown

🎉 This PR is included in version 1.0.4 🎉

The release is available on GitHub release

Your semantic-release bot 📦🚀

@Prajwal-Microsoft Prajwal-Microsoft deleted the fix/cwe-22-path-traversal-msrc-112301 branch April 20, 2026 03:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants