-
Notifications
You must be signed in to change notification settings - Fork 1
fix: patch path traversal (CWE-22) and harden /config endpoint [MSRC 112301] #174
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 |
|---|---|---|
|
|
@@ -2,20 +2,24 @@ | |
|
|
||
| import uvicorn | ||
| from dotenv import load_dotenv | ||
| from fastapi import FastAPI | ||
| from fastapi import FastAPI, Request | ||
| from fastapi.middleware.cors import CORSMiddleware | ||
| from fastapi.responses import FileResponse, HTMLResponse | ||
| from fastapi.responses import FileResponse, HTMLResponse, JSONResponse | ||
| from fastapi.staticfiles import StaticFiles | ||
|
|
||
| # Load environment variables from .env file | ||
| load_dotenv() | ||
|
|
||
| app = FastAPI() | ||
|
|
||
| # Read allowed origins from environment; fall back to same-origin only | ||
| _allowed_origins = os.getenv("ALLOWED_ORIGINS", "").split(",") | ||
| _allowed_origins = [o.strip() for o in _allowed_origins if o.strip()] | ||
|
|
||
| app.add_middleware( | ||
| CORSMiddleware, | ||
| allow_origins=["*"], | ||
| allow_methods=["*"], | ||
| allow_origins=_allowed_origins, | ||
| allow_methods=["GET"], | ||
| allow_headers=["*"], | ||
| ) | ||
|
|
||
|
|
@@ -35,20 +39,27 @@ async def serve_index(): | |
|
|
||
|
|
||
| @app.get("/config") | ||
| async def get_config(): | ||
| async def get_config(request: Request): | ||
| # 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"}) | ||
|
Comment on lines
+43
to
+48
|
||
|
|
||
| config = { | ||
| "API_URL": os.getenv("API_URL", "API_URL not set"), | ||
| "API_URL": os.getenv("API_URL", ""), | ||
| "REACT_APP_MSAL_AUTH_CLIENTID": os.getenv( | ||
| "REACT_APP_MSAL_AUTH_CLIENTID", "Client ID not set" | ||
| "REACT_APP_MSAL_AUTH_CLIENTID", "" | ||
| ), | ||
| "REACT_APP_MSAL_AUTH_AUTHORITY": os.getenv( | ||
| "REACT_APP_MSAL_AUTH_AUTHORITY", "Authority not set" | ||
| "REACT_APP_MSAL_AUTH_AUTHORITY", "" | ||
| ), | ||
| "REACT_APP_MSAL_REDIRECT_URL": os.getenv( | ||
| "REACT_APP_MSAL_REDIRECT_URL", "Redirect URL not set" | ||
| "REACT_APP_MSAL_REDIRECT_URL", "" | ||
| ), | ||
| "REACT_APP_MSAL_POST_REDIRECT_URL": os.getenv( | ||
| "REACT_APP_MSAL_POST_REDIRECT_URL", "Post Redirect URL not set" | ||
| "REACT_APP_MSAL_POST_REDIRECT_URL", "" | ||
| ), | ||
| "REACT_APP_WEB_SCOPE": os.getenv( | ||
| "REACT_APP_WEB_SCOPE", "" | ||
|
|
@@ -63,9 +74,12 @@ async def get_config(): | |
|
|
||
| @app.get("/{full_path:path}") | ||
| async def serve_app(full_path: str): | ||
| # First check if file exists in build directory | ||
| file_path = os.path.join(BUILD_DIR, full_path) | ||
| if os.path.exists(file_path): | ||
| # Resolve the requested path and ensure it stays within BUILD_DIR | ||
| file_path = os.path.realpath(os.path.join(BUILD_DIR, full_path)) | ||
| build_dir_real = os.path.realpath(BUILD_DIR) | ||
| if not file_path.startswith(build_dir_real + os.sep) and file_path != build_dir_real: | ||
| return FileResponse(INDEX_HTML) | ||
| if os.path.isfile(file_path): | ||
| return FileResponse(file_path) | ||
| # Otherwise serve index.html for client-side routing | ||
| return FileResponse(INDEX_HTML) | ||
|
|
||
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.
HTMLResponseis imported but no longer used in this module. Removing unused imports helps keep the file lint-clean and avoids confusion about supported response types.