Two pieces of housekeeping that had gone quiet.
**A code-scanning alert nobody had looked at.** CodeQL flagged js/log-injection
in the preflight handler: a request value interpolated straight into a log line.
Log entries are newline-delimited, so a value containing a line break forges
additional entries — and an audit trail its own subject can write into is worse
than none, because it is believed.
The interesting part is that the risk had already been recognised. Two sites in
the clients routes strip carriage returns inline, ad hoc, and the third was
simply missed. That is what a shared helper is for, so there is now a `logSafe`
utility with tests, used at all three. It replaces control characters with a
visible marker rather than stripping them, so a tampered value reads as tampered
instead of being quietly tidied, and truncates absurd input so one field cannot
flood the log.
**Twelve Dependabot pull requests, the oldest six weeks old.** Nine had passing
checks, but those runs were from July against a main that has moved a long way,
so rather than merging stale branches serially the eight safe ones are applied
here as one change and verified together: bullmq, pg, helmet, @types/supertest,
tailwind-merge, vite, actions/setup-node and github/codeql-action.
Deliberately excluded, and left open:
* bcryptjs 2.4.3 to 3.0.3 (#79) — a major on the password-hashing path.
Dependabot groups it with routine bumps; it is not one, and it deserves
its own verification rather than riding along in a batch.
* typescript 5.9 to 7.0 (#81), tailwindcss 3.4 to 4.3 (#76) and
eslint-plugin-react-hooks (#78) — all three already fail their own checks.
Verified beyond the gates: the application was rebuilt and exercised on the new
dependencies — authentication, the BullMQ-backed poller, Postgres reads and
helmet's headers all behave.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The Security Audit job failed because new advisories dropped against existing
deps and `npm audit --audit-level=high` audits the whole tree (incl. dev/build
tooling that isn't shipped).
- Switch both audit steps to `audit-ci --high --skip-dev`, so the gate reflects
what's actually deployed (backend ships --omit=dev; frontend ships a static
build) while still failing on real high/critical production advisories.
- Allowlist GHSA-qwww-vcr4-c8h2 (React Router "RSC Mode CSRF") for the frontend:
this is a client-side SPA using BrowserRouter — RSC mode is not used — and
there is no forward-compatible fix for react-router-dom 7.x (only a downgrade).
- `npm audit fix` on both lockfiles to patch non-breaking transitive dev
advisories (js-yaml, postcss, brace-expansion, minimatch). No direct/prod
dependency versions changed; app runtime is unchanged.
Verified: backend 114 tests pass, frontend builds, and audit-ci passes for both
projects locally.
- SECURITY.md with vulnerability disclosure policy and self-hosting guidance
- Dependabot config for weekly dependency updates (npm, Docker, Actions)
- ESLint v9 flat config for frontend and backend, with eslint-plugin-security
- GitHub Actions CI: lint, typecheck, build, and npm audit on every push/PR
- CodeQL workflow for automated security scanning (runs on push and weekly)