# Security Analysis Report Date: 2026-03-02 Repository: /Users/bedas/Developer/GitHub/dcm Scope: backend FastAPI API and worker, frontend React app, Docker runtime configuration, and local `.env` posture. ## Executive Verdict Current state is **not production ready**. - Blocking code-level issues exist and should be fixed before production exposure. - Additional user-dependent deployment risks are present in `.env` and runtime defaults. Per request, these are listed as MUST KNOW and not marked as blocking. ## Method and Coverage Performed a read-only static review of: - API auth, authorization, upload and file handling, routing, settings, and worker pipelines. - Frontend auth token handling and preview rendering behavior. - Docker and environment defaults affecting network and secret posture. - Existing security-focused tests and basic frontend API tests. ## Blocking Security Issues (Code-Level) ### 1) High - No abuse controls on expensive authenticated endpoints Impact: - Any authenticated user can repeatedly trigger high-cost operations (upload processing, OCR, summarization, routing, indexing), causing queue saturation, infrastructure exhaustion, and external provider cost abuse. Exploit path: - Repeated `POST /api/v1/documents/upload` - Repeated `POST /api/v1/documents/{document_id}/reprocess` Evidence: - Upload endpoint has size limits but no rate/volume quota checks: `backend/app/api/routes_documents.py:665-985`. - Reprocess endpoint has no rate/cooldown checks: `backend/app/api/routes_documents.py:958-985`. - A Redis rate limiter exists but is currently used for markdown export only: `backend/app/services/rate_limiter.py:16-42` and `backend/app/api/routes_documents.py:100-120`. Remediation: - Add per-user and per-IP rate limiting to upload and reprocess endpoints. - Add per-user daily/rolling quotas (documents, bytes, reprocess calls). - Add queue depth backpressure and reject or defer requests when saturated. - Add alerting for anomalous request and job-enqueue rates. ### 2) Medium - API docs and schema are exposed by default Impact: - Unauthenticated endpoint discovery and contract reconnaissance are easier (`/docs`, `/redoc`, `/openapi.json`). Exploit path: - Remote probing of public API metadata when service is internet-reachable. Evidence: - FastAPI app is created with default docs behavior and no production gating: `backend/app/main.py:37`. Remediation: - Disable docs in production (`docs_url=None`, `redoc_url=None`, `openapi_url=None`) or - Restrict these routes at reverse proxy / edge to trusted admin networks. ### 3) Medium - Bearer token stored in browser sessionStorage Impact: - Any successful XSS on the frontend origin can steal bearer tokens and replay them. Exploit path: - Malicious script execution on app origin reads `sessionStorage` and exfiltrates `Authorization` token. Evidence: - Token persisted in sessionStorage and injected into `Authorization` header: `frontend/src/lib/api.ts:39-42`, `frontend/src/lib/api.ts:61-67`, `frontend/src/lib/api.ts:84-95`, `frontend/src/lib/api.ts:103-112`. Remediation: - Prefer HttpOnly Secure SameSite cookies for session auth, plus CSRF protection. - If bearer-in-JS remains, enforce strict CSP, remove inline script execution, and add strong dependency hygiene. ## MUST KNOW (User-Dependent, Non-Blocking Per Request) ### A) Current `.env` is development-oriented and exposed beyond localhost Why this matters: - Service currently binds to all interfaces and uses development settings, which is unsafe for direct internet exposure. Evidence: - `APP_ENV=development`: `.env:1` - `HOST_BIND_IP=0.0.0.0`: `.env:2` - `PUBLIC_BASE_URL=http://...`: `.env:33` - Broad CORS for localhost and LAN host: `.env:34` Action: - Set production values (`APP_ENV=production`, HTTPS base URL, strict CORS, host binding behind hardened reverse proxy). ### B) Provider SSRF protections are disabled in active env Why this matters: - Allowing HTTP, private network targets, and empty allowlist can permit unsafe outbound model-provider endpoints. Evidence: - Active `.env`: `PROVIDER_BASE_URL_ALLOW_HTTP=true`, `PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK=true`, `PROVIDER_BASE_URL_ALLOWLIST=[]` at `.env:29-31`. - Compose defaults are permissive if env is not hardened: `docker-compose.yml:55-57`, `docker-compose.yml:100-102`. Action: - For production set: `ALLOW_HTTP=false`, `ALLOW_PRIVATE_NETWORK=false`, explicit host allowlist. ### C) Sensitive model and payload text logging is enabled in active env Why this matters: - Prompt/response and payload text may include confidential document content and credentials, increasing breach impact. Evidence: - Active `.env` enables both flags: `.env:22-23`. - Code defaults are safer (`false`): `backend/app/core/config.py:60-61`. Action: - Disable in production unless explicitly required for short-term diagnostics. - Apply strict retention and access controls if temporarily enabled. ### D) Redis transport/auth hardening depends on env mode and URL Why this matters: - Current env uses `redis://` with auto security/tls mode; this is not suitable for untrusted network paths. Evidence: - Active `.env`: `REDIS_URL=redis://...`, `REDIS_SECURITY_MODE=auto`, `REDIS_TLS_MODE=auto` at `.env:10-12`. - Strict checks are triggered by production mode when auto is used: `backend/app/core/config.py:157-171`. Action: - In production use `rediss://`, `REDIS_SECURITY_MODE=strict`, `REDIS_TLS_MODE=required`. ### E) Frontend container runs a development server Why this matters: - Vite dev server is not intended as a hardened production serving layer. Evidence: - Frontend container command runs `npm run dev`: `frontend/Dockerfile:20`. - `dev` script maps to Vite dev mode: `frontend/package.json:7`. Action: - Build static assets and serve behind a production-grade web server/reverse proxy. ### F) Login throttle IP identity depends on proxy topology Why this matters: - Throttle identity uses `request.client.host`; if a proxy masks client IPs, lockout behavior may be inaccurate. Evidence: - IP extraction uses transport client host directly: `backend/app/api/routes_auth.py:32-35`. Action: - Ensure trusted proxy configuration preserves real client IP semantics before internet deployment. ## Validation Commands and Outcomes Preflight: - `command -v git` -> passed (`/usr/bin/git`) - `git rev-parse --is-inside-work-tree` -> passed (`true`) - `git status --short` -> clean before work Security-related backend tests: - `PYTHONDONTWRITEBYTECODE=1 /Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_security_controls.py` -> passed (34 tests) - `PYTHONDONTWRITEBYTECODE=1 /Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_upload_request_size_middleware.py` -> passed (3 tests) - `PYTHONDONTWRITEBYTECODE=1 /Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_app_settings_provider_resilience.py` -> passed (6 tests) - `PYTHONDONTWRITEBYTECODE=1 /Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_processing_log_retention_settings.py` -> passed (5 tests) Frontend auth-client test: - `npm run test` (in `frontend/`) -> passed Note: - `pytest` is not installed in `/Users/bedas/Developer/Python/global_venv/bin/python`, so direct module execution via `unittest` entrypoints was used for backend test files. ## Residual Risk and Coverage Limits - No dynamic penetration test was run against a live deployed stack. - No dependency CVE audit (`pip-audit`, `npm audit`) was run in this turn. - Reverse proxy, firewall, TLS termination, and cloud/network policy were not reviewed.