Security — How it works
Overview
The security engine owns platform security telemetry that has nowhere else to live. Today that is one capability: CSP violation collection and triage (M33-012B), which turns the browser Content Security Policy from a policy that reports into nobody's console into evidence somebody can act on.
It is deliberately small. Authentication, authorization, break-glass and audit belong to
access-control and audit; this engine exists for security signals the platform receives rather
than enforces.
Data model & ownership
| Table | Purpose |
|---|---|
csp_violation_group | One row per distinct CSP problem, keyed on (effective_directive, blocked_uri_origin, document_uri_pattern), with first_seen_at, last_seen_at, a counter, and a triage state. |
Migrations live under core/src/main/resources/db/changelog/security/.
Key rules & invariants
- ⚠⚠ No concrete URL is ever stored. A CSP report carries
document-uri, and this application puts patient ids in URLs. Every URL is reduced to a route pattern (/patients/:id/allergies) or an origin at ingest, before the row is written — reduce, never redact later. - ⚠ The ingest endpoint is anonymous by necessity. Browsers send violation reports as credential-less beacons; authenticating it would not secure it, it would silently stop the reports. What stands in for RBAC is the per-IP rate limit plus the fact that nothing worth reaching is stored.
- ⚠ Aggregate, never one row per occurrence. Extensions, ISP script injection and malware generate large volumes of spurious violations about pages that are fine — the reason naive CSP collectors stop being read within days.
- Triage states are
NEW→EXPECTED/MUST_FIX/IGNORED, and reading or triaging requiressecurity.csp-violation.read/.write. Only ingest is anonymous. - ⚠ The purge never touches
NEWorMUST_FIX— those are the states M33-013's enforcement gate is counted from.
API
| Method | Path | Auth |
|---|---|---|
POST | /api/v1/security/csp-reports | anonymous, rate-limited; accepts application/csp-report and application/reports+json |
GET | /api/v1/security/csp-violations | security.csp-violation.read |
PATCH | /api/v1/security/csp-violations/{id} | security.csp-violation.write |
The ingest endpoint always answers 204 and never echoes input, so it cannot be used as a reflector
and cannot report whether a payload was understood.
Related
- CSP violation collection — the operational detail: how the reducer works, both browser wire formats, retention, and what FE-368 still owes.
- Infrastructure security requirements — the controls this repo verifies but does not implement.
⚠ Deliberately not a link: that page arrives with M33-015 (!531), and a link to a file on another
branch fails the Docusaurus build here with
broken links, which is how this doc first went red.