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.jsandapps/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, andapps/parapharmacy-api/src/routes/scraper.js: protect mutation/scrape routes, tighten limits, and hide Swagger in production. - Modify
docker-compose.ymlandapps/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.jsand createapps/parapharmacy-api/__tests__/security.test.js: regression coverage for fail-closed behavior and route protection. - Update relevant
package.jsonand 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 PIPJWT_SECRET_KEYif 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-filescontains no actual.envfile 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, andPG_URL/PG_PASSWORDare missing or equal to a known placeholder. Usecrypto.timingSafeEqualonly for fixed-length key comparisons; validation itself should compare exact placeholder strings. - Replace the backend fallback at
server.js:84with a required value from the validator. - Set
cookie.securetotruewheneverNODE_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 rejectCORS_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_KEYforPOST /api/products,POST /api/products/bulk, andPOST /api/scrapeusingAuthorization: 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/:idandDELETE /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 lowerexpress.jsonto the smallest limit required by real payloads. - Return
{ error: 'Scraping failed' }withouterror.messagein 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
portsfor Redis, PostgreSQL, MongoDB, exporters, and n8n; use127.0.0.1:host:containeronly 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/docsin 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=devin the parapharmacy Dockerfile withnpm 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-auditoruv auditagainst 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-uriand 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 --jsonfor 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.