# Security Remediation Plan Based on the External Data Handling security audit (April 2026). 17 findings total — 0 Critical, 2 High, 6 Medium, 6 Low, 3 Informational. Ordered by priority based on real-world exploitability and effort. --- ## Phase 1 — Data Exposure & XSS (High Priority) ### 1. L-4: Authenticate /uploads static file access **Location:** `server.js:127` **Risk:** Uploaded documents (vulnerability data, compliance files) served without authentication. Anyone with the URL can access them. **Fix:** Replace `express.static('/uploads')` with a route handler that runs `requireAuth(db)` before streaming the file. Use `res.sendFile()` with the validated path. **Effort:** Small — single route change. ### 2. M-6: Sanitize Mermaid SVG output with DOMPurify **Location:** `frontend/src/components/KnowledgeBaseViewer.js:38` **Risk:** Mermaid renders SVG which is injected via `innerHTML`. If KB content contains malicious markup, this is a stored XSS vector. **Fix:** Install `dompurify`, sanitize the SVG string before assigning to `innerHTML`. Use `DOMPurify.sanitize(svgString, { USE_PROFILES: { svg: true } })`. **Effort:** Small — add dependency, wrap one line. ### 3. M-4: Strip server file paths from compliance preview response **Location:** `backend/routes/compliance.js:278` **Risk:** Full server-side file path returned to client. Helps attackers map the filesystem. **Fix:** Return only the filename (use `path.basename()`) instead of the full path. Or return a reference ID that maps to the file server-side. **Effort:** Small — one-line change. --- ## Phase 2 — Deployment & Setup Hygiene ### 4. H-2: Add SESSION_SECRET to .env.example and setup-env.sh **Location:** `backend/.env.example`, `backend/setup-env.sh` **Risk:** Fresh deployments fail with no guidance on required env vars. **Fix:** Add `SESSION_SECRET=` to `.env.example` with a comment explaining it should be a random 64+ character string. Add generation logic to `setup-env.sh` (e.g., `openssl rand -hex 32`). **Effort:** Small. ### 5. I-3: Set user_group on default admin in setup.js **Location:** `backend/setup.js:180` **Risk:** Default admin created without `user_group`, potentially locked out of `requireGroup`-protected routes on fresh install. **Fix:** Set `user_group = 'Admin'` in the INSERT statement for the default admin user. **Effort:** Trivial — one column added to the INSERT. --- ## Phase 3 — Error Message Sanitization (Batch) ### 6. L-2: Sanitize Python parser error messages **Location:** `backend/routes/compliance.js:284` **Risk:** Stack traces and server paths leaked to client when Python parser fails. **Fix:** Catch the error, log the full details server-side, return a generic "Compliance file parsing failed" message to the client. **Effort:** Small. ### 7. L-3: Sanitize Ivanti API error responses **Location:** `backend/routes/ivantiFpWorkflow.js:393` **Risk:** Raw Ivanti API error body forwarded to client, potentially exposing internal API details. **Fix:** Log the raw error server-side, return a generic "Ivanti API request failed" message to the client. **Effort:** Small. ### 8. L-6: Remove group name from requireGroup error response **Location:** `backend/middleware/auth.js:60` **Risk:** Error response leaks the user's current group name, which is minor info disclosure. **Fix:** Change the error message from something like "User group 'Viewer' not authorized" to "Insufficient permissions." **Effort:** Trivial. --- ## Phase 4 — Security Headers ### 9. M-1: Add Content-Security-Policy header **Location:** `server.js:107-113` **Risk:** No CSP means no browser-side XSS mitigation layer. **Fix:** Add a CSP header via middleware. Start with a report-only policy to avoid breaking things, then tighten. Suggested baseline: ``` default-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:; font-src 'self'; connect-src 'self' ``` Note: `'unsafe-inline'` for styles is needed because the app uses inline style objects extensively. Evaluate whether `script-src 'self'` breaks anything (it shouldn't with CRA). **Effort:** Medium — needs testing to ensure nothing breaks. ### 10. M-2: Add Strict-Transport-Security (HSTS) header **Location:** `server.js:107-113` **Risk:** No HSTS means browsers don't enforce HTTPS on subsequent visits. **Fix:** Add `Strict-Transport-Security: max-age=31536000; includeSubDomains` header. Only apply when running behind HTTPS (check `req.secure` or a trusted proxy header). Do NOT enable if the app is accessed over plain HTTP. **Effort:** Small, but verify deployment is HTTPS-only first. --- ## Phase 5 — Operational Maintenance ### 11. L-5: Add expired session cleanup **Location:** `backend/middleware/auth.js:271` **Risk:** Sessions table grows indefinitely. Not a security exploit, but degrades performance over time. **Fix:** Add a cleanup function that runs on server startup (and optionally on a setInterval) to DELETE sessions where `expires_at < CURRENT_TIMESTAMP`. Run once at boot, then every 6 hours. **Effort:** Small. --- ## Phase 6 — Session Signing (Larger Effort) ### 12. H-1: Use SESSION_SECRET for HMAC-signed session tokens **Location:** `server.js:33` **Risk:** Session tokens are random bytes stored in DB with no signing. An attacker with DB read access can replay any session. For self-hosted SQLite, DB access already implies full compromise, so this is a defense-in-depth measure. **Fix:** When creating a session, generate a random token and store its HMAC (using SESSION_SECRET) in the DB. On validation, recompute the HMAC and compare. This means a DB dump alone isn't enough to forge sessions — the attacker also needs the secret. **Effort:** Medium — touches session creation, validation, and requires SESSION_SECRET to actually be wired in. --- ## Phase 7 — Investigate Before Changing ### 13. M-3: Review application/octet-stream in MIME allowlist **Location:** `server.js:62` **Risk:** Allows uploads that bypass MIME type checking. May be intentional for specific file types. **Action:** Check what file types are uploaded that resolve to `application/octet-stream`. If none are legitimate, remove it from the allowlist. If some are (e.g., `.db` files, binary exports), consider adding those specific MIME types instead. **Effort:** Investigation first, then trivial change. ### 14. M-5: Evaluate CORS HTTP origin policy **Location:** `server.js:38-40` **Risk:** CORS allows HTTP origins, no HTTPS enforcement. **Action:** Check if production runs behind a reverse proxy with HTTPS termination. If yes, the backend legitimately sees HTTP origins from the proxy. If production traffic is ever plain HTTP end-to-end, restrict CORS to HTTPS origins only. **Effort:** Investigation first, then small config change. --- ## Phase 8 — Low Priority / Monitor ### 15. L-1: Add startup warning for IVANTI_SKIP_TLS=true **Location:** `backend/helpers/ivantiApi.js:28` **Risk:** TLS validation disabled silently. Acceptable in dev, risky if accidentally left on in production. **Fix:** Add a `console.warn('⚠ IVANTI_SKIP_TLS is enabled — TLS certificate validation is disabled')` at startup when the flag is set. **Effort:** Trivial. ### 16. I-1: Monitor react-scripts version **Location:** `frontend/package.json` **Risk:** Build-time only, not runtime. No immediate action needed. **Action:** Upgrade to latest react-scripts when convenient. Consider migrating to Vite if a major frontend overhaul is planned. ### 17. I-2: Monitor xlsx dependency **Location:** `frontend/package.json` **Risk:** Community fork, unmaintained since 2022. Used for spreadsheet parsing. **Action:** Monitor for security advisories. If a vulnerability is found, evaluate alternatives (e.g., `exceljs`, `sheetjs` pro). No immediate action needed unless a CVE is published against it. --- ## Summary | Phase | Items | Effort | Impact | |-------|-------|--------|--------| | 1 — Data Exposure & XSS | L-4, M-6, M-4 | Small | High | | 2 — Deployment Hygiene | H-2, I-3 | Small | Medium | | 3 — Error Sanitization | L-2, L-3, L-6 | Small | Low-Medium | | 4 — Security Headers | M-1, M-2 | Medium | Medium | | 5 — Session Cleanup | L-5 | Small | Low | | 6 — Session Signing | H-1 | Medium | Medium | | 7 — Investigate | M-3, M-5 | Investigation | TBD | | 8 — Monitor | L-1, I-1, I-2 | Trivial | Low |