fix: patch path traversal (CWE-22) and harden /config endpoint [MSRC 112301]#174
Conversation
…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>
0ae043c to
c38520b
Compare
There was a problem hiding this comment.
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 withinBUILD_DIR; only serve regular files viaisfile(). - Replace wildcard CORS with an
ALLOWED_ORIGINSenvironment variable (defaulting to none) and restrict allowed methods toGET. - Add same-origin gating for
/configand remove verbose “not set” fallback strings from returned config values. - Set
ALLOWED_ORIGINSin 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 |
There was a problem hiding this comment.
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.
| from fastapi.responses import FileResponse, HTMLResponse, JSONResponse | |
| from fastapi.responses import FileResponse, JSONResponse |
| # 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"}) |
There was a problem hiding this comment.
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.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
🎉 This PR is included in version 1.0.4 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Purpose
Fixes a Path Traversal (CWE-22) security vulnerability in
src/frontend/frontend_server.pyreported 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..%2Ftraversal sequences. Additionally, the/configendpoint exposed MSAL client IDs, Azure AD authority, and API scopes to any unauthenticated cross-origin caller due toallow_origins=["*"]CORS policy.Changes
os.path.realpath()to canonicalize the resolved path and verify it stays withinBUILD_DIRos.path.exists()withos.path.isfile()to only serve regular files*origin with configurableALLOWED_ORIGINSenvironment variable (defaults to empty = same-origin only); restrict methods to GETRelated
Does this introduce a breaking change?
Golden Path Validation
Deployment Validation
What to Check
Verify that the following are valid
curl "http://localhost:3000/..%2F..%2Fetc%2Fpasswd") now returnindex.htmlinstead of the target file/configendpoint returns 403 for cross-origin requestsindex.html)/assets/still load normallyOther 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.