docs: update security production readiness report

This commit is contained in:
2026-03-01 21:07:49 -03:00
parent 4c27fd6483
commit 32b4589b28

129
REPORT.md
View File

@@ -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.