Files
FarmaFinder/docs/superpowers/plans/2026-07-22-security-hotfixes.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

8.8 KiB

FarmaFinder Security Hotfixes Implementation Plan

For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (- [ ]) syntax for tracking.

Goal: Remove production-blocking secret, access-control, network-exposure, and dependency risks identified by the 2026-07-22 security audit.

Architecture: Make production configuration fail closed, centralize service authentication for parapharmacy ingestion operations, and keep internal infrastructure private behind the reverse proxy. Upgrade dependency families in isolated batches with lockfile and runtime verification.

Tech Stack: Node.js 20/24, Express, express-session, MongoDB/Mongoose, Docker Compose, npm lockfiles, Python/Pydantic settings, Jest/Vitest.


Files and responsibilities

  • Modify apps/backend/server.js: production configuration validation, secure session cookie defaults, and any shared service-auth helper integration.
  • Create apps/backend/src/config/required-env.js and apps/backend/src/middleware/service-auth.js: fail-closed environment validation and constant-time service-key verification.
  • Modify apps/parapharmacy-api/src/server.js, apps/parapharmacy-api/src/routes/products.js, and apps/parapharmacy-api/src/routes/scraper.js: protect mutation/scrape routes, tighten limits, and hide Swagger in production.
  • Modify docker-compose.yml and apps/pip-platform/docker-compose*.yml: remove secret fallbacks, stop publishing internal ports, and pin image/dependency behavior.
  • Modify apps/pip-platform/src/infrastructure/config/settings.py: reject placeholder production secrets and wildcard credentialed CORS.
  • Modify apps/backend/.env.example, .env.example, and relevant README/docs: document required secret generation without real values.
  • Add/extend apps/backend/__tests__/server.test.js and create apps/parapharmacy-api/__tests__/security.test.js: regression coverage for fail-closed behavior and route protection.
  • Update relevant package.json and lockfiles only through package-manager commands after the security tests are in place.

Task 1: Rotate and inventory credentials before code changes

Files: Deployment secret store and local ignored .env files; no repository source changes required.

  • Revoke the Expo access token found in the local environment and issue a replacement with the minimum project scope.
  • Generate a new VAPID key pair and replace both public/private values wherever deployed.
  • Rotate SESSION_SECRET, PG_PASSWORD, REDIS_PASSWORD, N8N_PASSWORD, INGEST_API_KEY, MongoDB credentials, and PIP JWT_SECRET_KEY if their values were used outside local development.
  • Search deployment logs, CI variables, shell history, and backups for the old values; record the revocation date without writing secret values to Git.
  • Confirm git ls-files contains no actual .env file before continuing.

Task 2: Add fail-closed production configuration validation

Files: Create apps/backend/src/config/required-env.js; modify apps/backend/server.js, apps/pip-platform/src/infrastructure/config/settings.py, and both PIP Compose files.

  • Add a backend validator that rejects production startup when SESSION_SECRET, CORS_ORIGIN, and PG_URL/PG_PASSWORD are missing or equal to a known placeholder. Use crypto.timingSafeEqual only for fixed-length key comparisons; validation itself should compare exact placeholder strings.
  • Replace the backend fallback at server.js:84 with a required value from the validator.
  • Set cookie.secure to true whenever NODE_ENV === 'production', while retaining an explicit development override only for local HTTP.
  • Replace every Compose ${SECRET:-placeholder} expression with ${SECRET:?SECRET must be set} for production-required secrets.
  • In PIP settings, reject JWT_SECRET_KEY=change-me-in-production, reject default database/broker credentials in production, and reject CORS_ORIGINS=['*'] when credentials are enabled.
  • Add tests that start configuration with missing/placeholder secrets and assert a clear startup error.

Task 3: Authenticate parapharmacy ingestion and admin mutation routes

Files: Create apps/parapharmacy-api/src/middleware/service-auth.js; modify apps/parapharmacy-api/src/server.js, src/routes/products.js, and src/routes/scraper.js; create apps/parapharmacy-api/__tests__/security.test.js.

  • Require INGEST_API_KEY for POST /api/products, POST /api/products/bulk, and POST /api/scrape using Authorization: Bearer <key> or a dedicated internal header. Reject missing/malformed keys with 401 and compare supplied keys in constant time.
  • Require a separate admin credential for PUT /api/products/:id and DELETE /api/products/:id; do not reuse a public frontend session unless the API is intentionally integrated with that session.
  • Add a route-specific limiter for /api/scrape, cap queries/sources to bounded arrays, cap product bulk size, and lower express.json to the smallest limit required by real payloads.
  • Return { error: 'Scraping failed' } without error.message in production.
  • Mount Swagger only when NODE_ENV !== 'production' or protect it with the same admin control.
  • Test 401 for unauthenticated mutation/scrape requests, 403/401 for invalid keys, and successful behavior for a valid key. Test that public GET search endpoints remain available.

Task 4: Close infrastructure network exposure

Files: Modify docker-compose.yml, apps/pip-platform/docker-compose.yml, apps/pip-platform/docker-compose.runtime.yml, and apps/frontend/nginx.conf as needed.

  • Remove host ports for Redis, PostgreSQL, MongoDB, exporters, and n8n; use 127.0.0.1:host:container only when local operator access is explicitly required.
  • Publish only the intended frontend/reverse-proxy port and route internal API traffic through the proxy or a private Docker network.
  • Add a dedicated internal network and keep database/exporter services off any public-facing network.
  • Add production security headers at the reverse proxy, including HSTS only when HTTPS is guaranteed, and verify proxy headers are preserved.
  • Disable or protect /api/docs in production and verify an external request cannot reach n8n, MongoDB, PostgreSQL, Redis, or exporter ports.

Task 5: Make builds reproducible and remove mutable image inputs

Files: apps/backend/Dockerfile, apps/parapharmacy-api/Dockerfile, docker-compose.yml, PIP Compose files, CI workflow files.

  • Replace npm install --omit=dev in the parapharmacy Dockerfile with npm ci --omit=dev.
  • Pin all runtime image tags to approved versions and digests; record the update date in the deployment documentation.
  • Add CI checks for npm ci, npm audit --audit-level=high, image vulnerability scanning, and secret scanning.
  • Generate a Python lockfile and run pip-audit or uv audit against resolved dependencies.

Task 6: Upgrade vulnerable dependency families in batches

Files: package.json, package-lock.json, each app package.json/lockfile touched by the audit.

  • First update patch/minor-compatible vulnerable transitive packages and regenerate lockfiles with npm install --package-lock-only; inspect the diff for unrelated upgrades.
  • Upgrade backend OpenTelemetry packages as one compatible family, then bcrypt/SQLite-related packages, and rerun backend tests.
  • Upgrade parapharmacy API mongoose/fast-uri and transitive packages, then rerun API security tests.
  • Upgrade frontend Vite/Rollup/esbuild and Vitest. Keep Vitest UI disabled in production and verify no UI server is started by CI or deployment.
  • Upgrade scraper dependencies and confirm Puppeteer still uses the intended Chromium binary.
  • Re-run npm audit --json for the workspace and each lockfile; document any accepted residual advisory with package, path, exploitability, and owner.

Task 7: Verification gate before deployment

Files: No source changes unless verification exposes a regression.

  • Run git diff --check.
  • Run backend tests: npm test --prefix apps/backend.
  • Run frontend tests: npm test --prefix apps/frontend.
  • Run the parapharmacy security tests and all available package tests.
  • Render Compose configuration with production-like variables and confirm no placeholder values remain: docker compose config.
  • Build every production image with lockfile-enforced installs.
  • Run a black-box smoke test proving public GET endpoints work, protected mutations return 401 without a key, and internal infrastructure is not host-published.
  • Re-run secret scanning and npm audit --audit-level=high; block deployment on any critical/high issue without an explicit documented exception.