Skip to content

fix: critical security — API auth middleware and path traversal prevention#605

Open
jennniec wants to merge 1 commit into
666ghj:mainfrom
jennniec:fix/security-critical-auth-path-traversal
Open

fix: critical security — API auth middleware and path traversal prevention#605
jennniec wants to merge 1 commit into
666ghj:mainfrom
jennniec:fix/security-critical-auth-path-traversal

Conversation

@jennniec
Copy link
Copy Markdown

@jennniec jennniec commented May 5, 2026

Summary

  • Path traversal (CRITICAL / CWE-22): simulation_id, report_id, and project_id were passed directly to os.path.join() with no validation, allowing ../ sequences to escape intended directories.
  • Missing authentication (CRITICAL): all API endpoints were fully open with no auth mechanism.
  • Hardcoded secret / debug-on defaults (HIGH): SECRET_KEY fell back to a hardcoded string; FLASK_DEBUG defaulted to True.
  • Traceback disclosure (MEDIUM): full Python tracebacks were returned in error responses.

Changes

File What changed
backend/app/utils/id_validator.py (new) validate_safe_id() rejects IDs with dots/slashes; safe_join() verifies realpath stays inside base dir
backend/app/utils/auth.py (new) X-Api-Key middleware as before_request hook
backend/app/config.py SECRET_KEY uses os.urandom fallback; FLASK_DEBUG defaults False; API_KEY config added
backend/app/__init__.py Registers auth hook; logs startup warning when API_KEY is unset
backend/app/api/simulation.py validate_safe_id + safe_join at 3 path sites; 30 traceback fields removed
backend/app/api/report.py validate_safe_id in 12 route handlers; 17 traceback fields removed
backend/app/api/graph.py validate_safe_id at 6 sites; filename sanitized with os.path.basename; 4 traceback fields removed
.env.example Documents API_KEY, SECRET_KEY, FLASK_DEBUG with production guidance

Enabling authentication

Set API_KEY in .env, then include X-Api-Key: <your-key> on every request. If unset, the app starts normally with a warning (backwards-compatible for local dev).

Test plan

  • Start backend without API_KEY — all endpoints accessible, startup warning logged
  • Set API_KEY=test123 — requests without X-Api-Key: test123 return 401
  • GET /health returns 200 even without the header (exempted)
  • GET /api/simulation/../../etc/passwd returns 400 Invalid simulation_id
  • Normal simulation/report/graph workflows continue to function

Generated with Claude Code

Two critical issues and several high/medium issues were identified during
a security review of the backend API.

**Critical fixes:**

1. Path traversal (CWE-22): user-supplied `simulation_id`, `report_id`,
   and `project_id` values were passed directly to `os.path.join()`
   without validation, allowing `../` sequences to escape intended
   directories.
   - Added `backend/app/utils/id_validator.py` with `validate_safe_id()`
     (rejects anything that isn't alphanumeric/underscore/hyphen) and
     `safe_join()` (resolves realpath and verifies containment).
   - Applied to all 3 path-construction sites in simulation.py, all 12
     relevant handlers in report.py, and 6 sites in graph.py.
   - Sanitized uploaded filenames with `os.path.basename()` in graph.py.

2. Missing authentication: all API endpoints were publicly accessible
   with no auth mechanism.
   - Added `backend/app/utils/auth.py` with an `X-Api-Key` middleware
     registered as a `before_request` hook.
   - Auth is opt-in: set `API_KEY` in `.env` to enforce it; if unset a
     startup warning is logged. This preserves local dev workflows.

**High fixes:**

3. Hardcoded `SECRET_KEY` fallback replaced with `os.urandom(32).hex()`
   so an unset key is never predictable.
4. `FLASK_DEBUG` now defaults to `False` instead of `True`.
5. Full Python tracebacks removed from all API error responses (51 total
   across graph.py, report.py, simulation.py) — tracebacks still go to
   the logger.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@dosubot dosubot Bot added the size:L This PR changes 100-499 lines, ignoring generated files. label May 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:L This PR changes 100-499 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant