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

10 KiB

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.

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.