Files
FarmaFinder/docs/security/2026-07-22-security-audit-report.md
Antoni Nuñez Romeu 849763896d
Run Tests on Branches / Detect Changes (push) Successful in 12s
Run Tests on Branches / Frontend Tests (push) Successful in 2m12s
Run Tests on Branches / Frontend Mobile Tests (push) Has been skipped
Run Tests on Branches / Parapharmacy API Tests (push) Successful in 2m2s
Run Tests on Branches / PIP Platform Tests (push) Has been skipped
Run Tests on Branches / Backend Tests (push) Successful in 2m8s
security: harden production configuration and routes
2026-07-22 17:24:54 +02:00

133 lines
10 KiB
Markdown

# FarmaFinder Security Audit Report
**Audit date:** 2026-07-22
**Scope:** repository source, configuration, environment files present on disk, Docker Compose files, Git history, Node lockfiles, and Python dependency declarations.
**Status:** **Not ready for an unrestricted production deployment.**
## Executive summary
The most urgent risks are configuration and access-control issues, not evidence of a secret committed to Git:
1. Local ignored `.env` files contain credential-shaped production values. The values must be treated as exposed if they were ever copied to a server, shared, or used in a public environment. Rotate the Expo access token and VAPID private key immediately, and rotate any database, session, n8n, or webhook credentials that may have shared the same deployment.
2. Production Docker Compose has known-secret fallbacks such as `change-me-in-production`, `change-me`, and `pip-secret`. A deployment can therefore start with predictable credentials when variables are missing.
3. The parapharmacy API exposes product create, bulk upsert, update, delete, and scraper-trigger endpoints without authentication. This is a direct integrity and availability risk.
4. The backend silently falls back to a hard-coded session secret. A missing secret must fail closed in production.
5. The verified npm audit reports 79 advisories in the workspace, including 3 critical and 13 high. The most important direct production-adjacent findings include vulnerable OpenTelemetry packages, `bcrypt`/`tar` transitive issues, and the vulnerable `ws`/`brace-expansion` chains. The frontend test toolchain includes a critical Vitest advisory; it must not be exposed as a server in any environment.
## Findings
### F-01 — Secrets present in local environment files
**Severity:** Critical
**Evidence:** `apps/backend/.env` and `apps/parapharmacy-api/.env` exist locally and are ignored by Git. The backend file contains a non-empty Expo access token and VAPID private key; it also contains database/session/ingest credential fields. Values are intentionally not reproduced here.
**Impact:** Push credentials, session signing, database access, or workflow ingestion may be compromised. A VAPID private key can impersonate the application for web push; an Expo token can send notifications through the project account.
**Action:** Rotate the Expo token and VAPID key now. Rotate all non-development credentials in those files. Review CI, deployment hosts, shell history, password managers, and logs for copies. Keep secrets in the deployment secret store only.
### F-02 — Predictable production secret fallbacks
**Severity:** Critical
**Evidence:** `docker-compose.yml:12,34,41,89,137,141,147,176,178` uses defaults including `change-me-in-production`, `change-me`, and a derived PostgreSQL URL. `apps/backend/server.js:83-85` falls back to `farma-clic-secret-key-change-in-production`. `apps/pip-platform/src/infrastructure/config/settings.py:16,24,26` contains default database, broker, and JWT credentials. The PIP Compose files also default `JWT_SECRET_KEY` at `apps/pip-platform/docker-compose.yml:13` and `docker-compose.runtime.yml:13`.
**Impact:** Missing environment injection can enable session forgery, JWT forgery, database access, or n8n takeover.
**Action:** Remove secret defaults. Add startup validation that rejects `NODE_ENV=production`/production deployments when required secrets are absent or match known placeholders. Use `${VAR:?VAR must be set}` in Compose for required values.
### F-03 — Unauthenticated parapharmacy write/delete/scrape APIs
**Severity:** Critical
**Evidence:** `apps/parapharmacy-api/src/routes/products.js:280-320,340-390` exposes bulk write, update, and delete routes without auth. `apps/parapharmacy-api/src/routes/scraper.js:7-24` exposes a scraper trigger without auth. `apps/parapharmacy-api/src/server.js:55-70` applies only global CORS, JSON parsing, logging, and rate limiting.
**Impact:** An unauthenticated caller can poison or delete the catalog, trigger expensive Puppeteer scraping, and cause resource exhaustion.
**Action:** Protect write/delete/scrape routes with a dedicated ingestion/admin API key or service-to-service authentication. Apply a strict body/item limit and a separate low-rate limiter to scraper execution. Return generic errors without echoing scraper exception messages.
### F-04 — Hard-coded backend session secret fallback
**Severity:** High
**Evidence:** `apps/backend/server.js:83-85` uses a fixed fallback secret. The session cookie is HTTP-only and SameSite=Lax (`:87-91`), which is good, but `secure` is opt-in at `COOKIE_SECURE=true`.
**Impact:** Anyone who knows the repository can forge sessions when the deployment omits `SESSION_SECRET`; if `COOKIE_SECURE` is omitted in HTTPS production, cookies may be sent over an accidental HTTP path.
**Action:** Fail startup when production lacks a strong `SESSION_SECRET`; force secure cookies in production and set `app.set('trust proxy', ...)` only to the known proxy topology.
### F-05 — Public infrastructure ports and unauthenticated observability/admin surfaces
**Severity:** High
**Evidence:** `docker-compose.yml:29-30,66-67,75-76,86-89,102-103,122-123,131-132` publishes backend, frontend, exporters, parapharmacy API, MongoDB, and n8n ports. Swagger is mounted at `/api/docs` in `apps/parapharmacy-api/src/server.js:64-69` without an environment guard.
**Impact:** Databases, exporters, n8n, and internal APIs may be reachable from the host network or internet. Swagger reveals mutation endpoints and operational details.
**Action:** Bind internal services to the Docker network or loopback only. Publish only the reverse proxy/frontend. Restrict n8n and Swagger to an authenticated admin network or disable them in production.
### F-06 — Verified npm dependency advisories
**Severity:** Critical/High/Moderate by package and exposure
**Evidence:** `npm audit --json` completed with network access on 2026-07-22.
| Lockfile / scope | Critical | High | Moderate | Low | Total |
|---|---:|---:|---:|---:|---:|
| Workspace root | 3 | 13 | 60 | 3 | 79 |
| Backend | 1 | 11 | 41 | 3 | 56 |
| Frontend | 1 | 3 | 18 | 0 | 22 |
| Parapharmacy API | 0 | 2 | 0 | 1 | 3 |
| Scraper | 0 | 1 | 0 | 0 | 1 |
Notable verified chains include:
- backend: `@opentelemetry/auto-instrumentations-node` high-severity Prometheus exporter crash advisory; `bcrypt` through `@mapbox/node-pre-gyp`/`tar`; vulnerable `ws` and `uuid` transitive chains;
- frontend: critical `vitest` advisory when its UI server is exposed, plus vulnerable Vite/esbuild/Rollup and Grafana Faro/OpenTelemetry chains;
- parapharmacy API: `fast-uri`, `brace-expansion`, and `body-parser` advisories;
- scraper: `brace-expansion` high-severity DoS advisory.
**Action:** Apply targeted non-breaking patches first where available, then upgrade major-version families in isolated branches with tests. Do not use `npm audit fix --force` blindly; the audit indicates major-version changes for several fixes.
### F-07 — Dependency hygiene and likely-unused candidates
**Severity:** Medium (maintenance/security surface)
**Evidence:** A static repository scan found direct dependencies with no source import/reference outside manifests/lockfiles:
- backend: `barcode-detector`, `@opentelemetry/exporter-logs-otlp-grpc`, `@opentelemetry/sdk-logs`, `@opentelemetry/sdk-trace-base`;
- frontend: `@grafana/faro-web-sdk`;
- mobile: `expo-dev-client`, `expo-linking`, `react-native-screens`, `react-native-worklets` (some may be required by Expo/native autolinking);
- parapharmacy API: `jest`, `supertest` (test tooling is declared but no tests were found in that package).
This scan is not proof of unused status because Expo, native autolinking, configuration, and instrumentation can consume packages indirectly. Confirm each candidate with package-manager dependency tracing and a clean build before removal. No package was removed during this audit.
### F-08 — Build/runtime supply-chain hygiene
**Severity:** High
**Evidence:** `docker-compose.yml:22,57,73,84,95,120,129,169` uses mutable `:latest` image tags. `apps/parapharmacy-api/Dockerfile` uses `npm install --omit=dev` instead of `npm ci`; `apps/backend/Dockerfile` uses `npm ci`.
**Impact:** Rebuilds are not reproducible and can silently pull changed or compromised images/dependency resolutions.
**Action:** Pin images by version and digest, use lockfile-enforced `npm ci` for every Node image, and add image/dependency scanning to CI.
### F-09 — Mobile/API keys and telemetry endpoints need restriction
**Severity:** Medium
**Evidence:** `apps/frontend-mobile/google-services.json:18` contains a Firebase API key. This type of key is normally public, but must be restricted by package/bundle identity and API scope in Google Cloud/Firebase. Mobile and frontend env examples point telemetry at public or host endpoints.
**Action:** Verify Firebase key restrictions, avoid treating public client configuration as a secret, and enforce collector authentication/rate limits so telemetry endpoints cannot be abused.
## Positive controls observed
- Git status was clean before this audit.
- `.gitignore` excludes `.env`, dependency directories, build output, and Android signing material.
- Git history inspection found environment examples and placeholder values, but no tracked actual `.env` file or the local Expo/VAPID values.
- Backend uses parameterized SQL for the inspected user/session paths, bcrypt password hashing, HTTP-only cookies, admin middleware, and route-specific rate limiting.
- Production backend and frontend use container builds, and backend uses `npm ci --omit=dev`.
## Verification and limitations
- Source/configuration and Git-history checks were read-only.
- Online npm audit was executed with network access on 2026-07-22; advisory counts are a point-in-time result and should be rerun in CI.
- No production host, running container, database, secret manager, cloud account, or external firewall was inspected.
- No Python lockfile exists for `apps/pip-platform`; the Python audit therefore covered declarations and defaults, not resolved CVEs. Add a lockfile and run `pip-audit`/`uv audit` in CI.
- The audit did not prove that every endpoint is unreachable externally; exposure depends on deployment networking.
## Recommended deployment decision
Do not deploy the current configuration publicly until F-01 through F-05 are addressed. Dependency upgrades and supply-chain pinning should follow immediately. The implementation sequence is documented in [the hotfix plan](../superpowers/plans/2026-07-22-security-hotfixes.md).