diff --git a/REPORT.md b/REPORT.md index 1f4fae5..b72065d 100644 --- a/REPORT.md +++ b/REPORT.md @@ -1,90 +1,97 @@ # Security Production Readiness Report -Date: 2026-03-01 +Date: 2026-03-02 Repository: /Users/bedas/Developer/GitHub/dcm -Assessment type: Static code/configuration review with local security-test execution +Assessment type: Static code and configuration review with targeted local security test execution ## Verdict Not production ready. -Reason: one blocking, code-level security issue was found. +Reason: one blocking, code-level access-control and data-disclosure issue was found. -## Scope -- Backend API and worker code in `backend/app` -- Frontend auth/API client code in `frontend/src` -- Runtime/deployment configuration in `.env` and `docker-compose.yml` +## Preflight Results +- `command -v git` -> pass (`/usr/bin/git`) +- `git rev-parse --is-inside-work-tree` -> pass (`true`) +- `git status --short` -> clean before analysis ## Validation Commands And Outcomes -- `command -v git` -> pass -- `git rev-parse --is-inside-work-tree` -> pass (`true`) -- `git status --short` -> clean working tree before analysis -- `/Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_security_controls.py` -> pass (24 tests) -- `/Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_app_settings_provider_resilience.py` -> pass (6 tests) -- `/Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_processing_log_retention_settings.py` -> pass (5 tests) -- `/Users/bedas/Developer/Python/global_venv/bin/python backend/tests/test_upload_request_size_middleware.py` -> failed to run (import error in test setup) -- `/Users/bedas/Developer/Python/global_venv/bin/python -m pytest ...` -> not runnable in current venv (`No module named pytest`) +- `/Users/bedas/Developer/Python/global_venv/bin/python -m unittest backend/tests/test_security_controls.py` -> pass (32 tests) +- `/Users/bedas/Developer/Python/global_venv/bin/python -m unittest backend/tests/test_upload_request_size_middleware.py` -> pass (3 tests) +- `/Users/bedas/Developer/Python/global_venv/bin/python -m unittest backend/tests/test_app_settings_provider_resilience.py` -> pass (6 tests) ## Blocking Security Findings -### High: No brute-force protection on authentication login +### High: Non-global catalog presets are exposed to all authenticated users - Severity: High (blocking) -- Why this is blocking: `/api/v1/auth/login` accepts unlimited credential attempts with no per-IP or per-username throttling, no lockout, and no backoff. This leaves credential stuffing and password guessing defenses incomplete for production. -- Impact: online account takeover risk increases substantially when passwords are weak, reused, leaked, or defaulted. -- Exploit path: repeated automated POST requests to `/api/v1/auth/login` until valid credentials are found. +- Why this is blocking: + - The settings model supports `global_shared` scope for predefined paths and tags, but the user-accessible discovery endpoints return all predefined entries without filtering by this scope. + - This breaks intended discoverability boundaries and leaks admin-curated non-global taxonomy metadata to standard users. +- Impact: + - Information disclosure across role boundaries for internal path and tag catalogs. + - Reduced separation between admin-only and user-visible metadata. +- Exploit path: + - Any authenticated non-admin user calls `GET /api/v1/documents/paths` and `GET /api/v1/documents/tags`. + - Endpoint responses include every predefined path or tag value regardless of `global_shared` state. - Evidence: - - `backend/app/api/routes_auth.py:34` defines login endpoint - - `backend/app/api/routes_auth.py:42` to `backend/app/api/routes_auth.py:51` performs auth check and returns 401 only - - `backend/app/api/routes_documents.py:75` to `backend/app/api/routes_documents.py:95` shows rate limiting exists but is applied only to content export, not login - - `backend/app/services/rate_limiter.py:16` to `backend/app/services/rate_limiter.py:42` contains reusable limiter logic currently not used by auth routes + - `backend/app/api/routes_documents.py:399-403` and `backend/app/api/routes_documents.py:423-427` include all predefined tags and paths. + - `backend/app/schemas/settings.py:145` and `backend/app/schemas/settings.py:159` define global discoverability scope. + - `backend/app/services/app_settings.py:709-710`, `backend/app/services/app_settings.py:730`, `backend/app/services/app_settings.py:758-759`, and `backend/app/services/app_settings.py:779` preserve `global_shared` state in normalized settings. - Required remediation: - - add login throttling keyed by username and source IP - - add escalating delay or temporary lockout on repeated failures - - return a stable error message and status on throttled attempts - - log and monitor failed auth attempt rates - -## Additional Code-Level Findings (Non-blocking) - -### Low: One security middleware regression test is currently not executable -- Severity: Low -- Risk: reduced confidence in continued enforcement of upload middleware behavior. -- Evidence: - - `backend/tests/test_upload_request_size_middleware.py` currently errors at import time when run directly in this environment -- Recommendation: - - fix test stubs/import assumptions so this test is runnable in CI and local developer environments + - Filter predefined entries returned by user-facing discovery endpoints by role and `global_shared`. + - Keep full catalog visibility for admins only. + - Add regression tests for non-admin path/tag discovery with mixed `global_shared` values. ## MUST KNOW User-Dependent Risks (Not Blocking Per Request) -These are deployment/operator-controlled and therefore reported as must-know, not blocking findings. +These items are deployment, environment, or proxy dependent and are therefore not marked blocking per request requirements. -1. Environment is set to development posture with broad host binding. -- Evidence: `.env:1` (`APP_ENV=development`), `.env:2` (`HOST_BIND_IP=0.0.0.0`) -- Risk: exposed surface and relaxed defaults if used outside isolated development network. +### High: Development-first runtime defaults can be promoted to production if not overridden +- Evidence: + - `.env.example:5` (`APP_ENV=development`) + - `.env.example:36-38` (`PROVIDER_BASE_URL_ALLOW_HTTP=true`, `PROVIDER_BASE_URL_ALLOW_PRIVATE_NETWORK=true`, empty allowlist) + - `.env.example:14-16` (`redis://`, `REDIS_SECURITY_MODE=compat`, `REDIS_TLS_MODE=allow_insecure`) + - `.env.example:40-41` (`PUBLIC_BASE_URL` on HTTP, local CORS defaults) + - `docker-compose.yml:56-57`, `docker-compose.yml:64-66`, `docker-compose.yml:101-102`, `docker-compose.yml:106-107` +- Risk: + - Weak outbound provider constraints, plaintext internal transport defaults, and non-production environment posture can persist in live deployments. -2. Bootstrap credentials appear placeholder/weak and must be replaced before live use. -- Evidence: `.env:15`, `.env:17` -- Risk: straightforward credential compromise if unchanged. +### Medium: Login throttle IP identity depends on proxy trust model +- Evidence: + - `backend/app/api/routes_auth.py:32-35` uses `request.client.host` only. +- Risk: + - Behind reverse proxies, all clients may collapse to proxy IP, increasing lockout abuse and reducing attribution quality. -3. Sensitive log text persistence is enabled in current `.env`. -- Evidence: `.env:22`, `.env:23`; code path at `backend/app/services/processing_logs.py:47` and `backend/app/services/processing_logs.py:128` -- Risk: OCR/model prompts, responses, and payload text can be stored and later exposed to admins or backups. +### Medium: API documentation endpoints are exposed by default +- Evidence: + - `backend/app/main.py:37` creates `FastAPI(...)` with default docs behavior. + - README explicitly references `/docs` as available. +- Risk: + - Public endpoint inventory and schema visibility in exposed deployments. -4. Provider outbound network controls are permissive in current `.env`. -- Evidence: `.env:29` to `.env:31` -- Risk: admin-configured provider endpoints can target non-HTTPS/private hosts if account compromise occurs. +### Medium: Bearer token is stored in browser `sessionStorage` +- Evidence: + - `frontend/src/lib/api.ts:41`, `frontend/src/lib/api.ts:61-67`, `frontend/src/lib/api.ts:84-94` +- Risk: + - Any successful frontend XSS can exfiltrate active session tokens. -5. Public base URL is HTTP and CORS includes LAN origin. -- Evidence: `.env:33`, `.env:34` -- Risk: transport security and origin trust are weaker than production HTTPS allowlist posture. +### Low: Typesense transport defaults to HTTP on internal network +- Evidence: + - `docker-compose.yml:66-67`, `docker-compose.yml:107-108` +- Risk: + - Acceptable for isolated local networks, but not suitable for untrusted or cross-host network links. -6. Redis transport is non-TLS in current env posture. -- Evidence: `.env:10` to `.env:12` -- Risk: acceptable for isolated local stack, unsafe for untrusted networks. +## Security Controls Confirmed Present +- Login brute-force throttling with lockout and Retry-After handling. +- Owner-scoped access checks for non-admin document operations. +- Provider base URL validation with allowlist and DNS revalidation hooks. +- Upload request size and archive extraction guardrails. +- Processing log redaction and metadata-only persistence defaults. -7. Compose defaults are development-first for several sensitive controls. -- Evidence: `docker-compose.yml:40`, `docker-compose.yml:52`, `docker-compose.yml:53`, `docker-compose.yml:60`, `docker-compose.yml:68` -- Risk: if overrides are missed, deployment may run with weaker network and provider policies. +## Coverage Limits +- No dependency CVE audit was executed in this run. +- This report is based on repository code, configuration templates, and available local tests. ## Production Decision -Current state is not production ready due to the blocking auth brute-force gap. +Current state is not production ready because of the blocking catalog discoverability exposure. -After fixing the blocking issue, production readiness still depends on secure operator configuration of `.env` and runtime network perimeter. +After remediating that issue, production readiness still depends on strict deployment choices for environment variables, proxy trust configuration, TLS, and frontend hardening.