From e9da01ffce77a8aabfb47704b777a9bd7eec957e Mon Sep 17 00:00:00 2001 From: "bert.hausmans" Date: Thu, 7 May 2026 19:47:12 +0200 Subject: [PATCH] =?UTF-8?q?docs:=20WS-7=20closure=20=E2=80=94=20RFC=20stat?= =?UTF-8?q?us=20+=20SECURITY=5FAUDIT=20+=20BACKLOG=20+=20sync=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PR-4 commit 3 — closure-bookkeeping nu de implementation-PRs en de twee runbooks gemerged zijn. - RFC-WS-7-OBSERVABILITY.md: nieuwe §9 Implementation status (mei 2026) vat samen welke acceptance criteria via PR-1..PR-4 zijn voldaan en welke (1, 2, 7, 9, 10) op Bert's deploy-checklist resteren. Pointer naar ARCH-OBSERVABILITY.md als levende reference; de RFC blijft historisch document. - SECURITY_AUDIT.md: nieuwe sectie 'WS-7 Observability — finale audit (mei 2026)' tussen A13-10 en Positive Findings. Bevat (1) acceptance criteria checklist met status per criterium, (2) processing register entry voor GlitchTip (controller-not-processor, retention 90 dagen, TLS+full-disk-encryption+2FA), (3) zeven security controls die WS-7 introduceert (PII scrubbing, CSP whitelist, sourcemap upload-only, listener registration discipline, runtime portal-context-split, multi-tenant tag invariant, impersonation.active binary signal), (4) pointer naar runbooks/observability-erasure.md voor Art. 17. - BACKLOG.md: status-overzicht-tabel boven de OBS-entries. Toegevoegd als entry: OBS-2 (early-pipeline log context, ✅ Resolved), OBS-3 (sentry-context middleware coverage, ✅ Resolved — opgevouwen in AuthScopeContextListener), OBS-5 (Crewli render handlers report() invariant, ✅ Resolved via 48f2a00 + ExceptionReportingTest), en OBS-9 (Active — staging environment GlitchTip CSP whitelist follow-up bij staging-introductie). Bestaande OBS-1, 4, 6, 7 ongewijzigd (Active); OBS-8 staat al op Resolved sinds dee1401. - .claude-sync.conf: drie nieuwe doc-paths toegevoegd (ARCH-OBSERVABILITY.md, runbooks/observability-triage.md, runbooks/observability-erasure.md). Post-commit sync-claude-docs hook regenereert SYNC_MANIFEST.md met deze entries. Closes WS-7 documentation acceptance criteria 8 (ARCH) en 14 (SECURITY_AUDIT). Resterende criteria (1, 2, 7, 9, 10) zijn deploy-checklist door Bert. Co-Authored-By: Claude Opus 4.7 (1M context) --- .claude-sync.conf | 3 + dev-docs/BACKLOG.md | 106 +++++++++++++++++++++++++++- dev-docs/RFC-WS-7-OBSERVABILITY.md | 29 ++++++++ dev-docs/SECURITY_AUDIT.md | 107 +++++++++++++++++++++++++++++ 4 files changed, 244 insertions(+), 1 deletion(-) diff --git a/.claude-sync.conf b/.claude-sync.conf index 68e126c5..762142c5 100644 --- a/.claude-sync.conf +++ b/.claude-sync.conf @@ -15,3 +15,6 @@ dev-docs/ARCH-BINDINGS.md dev-docs/ARCH-API-VALIDATION.md dev-docs/RFC-WS-7-OBSERVABILITY.md dev-docs/GLITCHTIP.md +dev-docs/ARCH-OBSERVABILITY.md +dev-docs/runbooks/observability-triage.md +dev-docs/runbooks/observability-erasure.md diff --git a/dev-docs/BACKLOG.md b/dev-docs/BACKLOG.md index 0a4d942b..fd86c8da 100644 --- a/dev-docs/BACKLOG.md +++ b/dev-docs/BACKLOG.md @@ -1643,7 +1643,21 @@ _Voeg nieuwe items toe met prefix: ARCH-, COMM-, OPS-, VOL-, ART-, FORM-, SUP-, --- -## Observability follow-ups (post WS-7 PR-2) +## Observability follow-ups (post WS-7) + +> **Status overzicht (mei 2026, na WS-7 closure):** +> +> | Entry | Status | +> |---|---| +> | OBS-1 | Active — wacht op volunteer-rol introductie | +> | OBS-2 | ✅ Resolved (PR-2 architectural-fixes — opgevouwen in AuthScopeContextListener) | +> | OBS-3 | ✅ Resolved (PR-2 architectural-fixes — auto-discovery uit + explicit registratie maakt route-conditional binding overbodig) | +> | OBS-4 | Active — pre-PHPUnit 12 cleanup | +> | OBS-5 | ✅ Resolved (PR-2 smoke-test fix `48f2a00` + `ExceptionReportingTest`) | +> | OBS-6 | Active — wachten op SETUP.md update of CI smoke check | +> | OBS-7 | Active — coverage-test scope-creep, expanded coverage waardevol | +> | OBS-8 | ✅ Resolved (final hardening commits `215405a`, `a939820`, `dee1401`) | +> | OBS-9 | Active — alleen relevant bij staging-introductie | ### OBS-1 — Promote ActorType::VOLUNTEER when volunteer role is introduced @@ -1666,6 +1680,45 @@ modellering. Geen blocker. **Refs:** `app/Enums/Observability/ActorType.php`, RFC-WS-7-OBSERVABILITY.md §3.6. +### OBS-2 — Early-pipeline log context user_id ✅ Resolved + +**Aanleiding:** Tijdens PR-2 architectural-fixes ontdekt dat +`Log::withContext` in `BindRequestLogContext` middleware geen +`user_id` had voor log-regels die vóór auth:sanctum draaiden, omdat +de middleware run op api-group-level (vóór per-route auth). + +**Status:** Resolved in PR-2 architectural-fixes. `AuthScopeContextListener::bindForUser()` +roept `Log::withContext(['user_id' => $user->id, ...])` aan zodra het +`Authenticated` of `TokenAuthenticated` event vuurt. Vanaf dat punt +in de request-pipeline dragen alle log-regels `user_id`. Pre-auth +log-regels (CSRF / rate-limiter middleware logs) hebben nog steeds +geen `user_id`, maar dat is correct gedrag — er is op dat moment ook +nog geen geauthenticeerde gebruiker. + +**Refs:** `app/Listeners/Observability/AuthScopeContextListener.php`, +`app/Http/Middleware/BindRequestLogContext.php`. + +### OBS-3 — Sentry-context middleware coverage assertion ✅ Resolved + +**Aanleiding:** PR-2 originele design had de Sentry-context binding +als route-level middleware aliased per route group. Risico: een +nieuwe route-group die de alias vergeet had silently geen +auth-scope tags op gecaptured events. + +**Status:** Resolved in PR-2 architectural-fixes. De binding is +verplaatst van middleware naar `AuthScopeContextListener` op het +`Authenticated` / `TokenAuthenticated` event. Listener fires +universeel op élke auth-resolution; geen route-conditional binding +meer mogelijk. Daarmee is een coverage-assertion test overbodig — de +listener kan niet "vergeten" worden zoals een route alias dat kon. + +Combineer met OBS-8 (auto-discovery uit + explicit registration via +`EventListenerRegistrationTest`): listener-aanwezigheid is +empirisch gevalideerd op test-niveau. + +**Refs:** `app/Listeners/Observability/AuthScopeContextListener.php`, +`tests/Feature/Observability/EventListenerRegistrationTest.php`. + ### OBS-4 — PHPUnit metadata-in-doc-comment deprecation cleanup **Aanleiding:** PHPUnit warnt dat metadata in doc-comments (zoals @@ -1684,6 +1737,30 @@ upgrade gepland wordt — nu blokkeert het niets. **Refs:** PHPUnit changelog, de drie genoemde test-files. +### OBS-5 — Crewli render handlers report() invariant ✅ Resolved + +**Aanleiding:** PR-2 smoke test toonde dat backend-exceptions geen +events naar GlitchTip stuurden ondanks correct geconfigureerde SDK. +Root cause: sentry-laravel 4.x heeft de `Integration::handles($exceptions)` +registratie-stap die NIET wordt auto-aangeroepen door de package's +ServiceProvider; de host-app moet dit expliciet doen in +`bootstrap/app.php`. + +**Status:** Resolved via commit `48f2a00` +`fix: route controller exceptions through sentry-laravel reporter` ++ `tests/Feature/Observability/ExceptionReportingTest.php` als +regression-guard. ExceptionReportingTest installeert een recording +`before_send` hook en verifieert dat `RuntimeException` wel, +`ValidationException`/`AuthorizationException`/`NotFoundHttpException` +niet captured worden — exact de boundary uit RFC §3.10. + +OBS-7 (hieronder) breidt deze coverage uit naar Crewli's eigen +render handlers. + +**Refs:** `bootstrap/app.php`, +`tests/Feature/Observability/ExceptionReportingTest.php`, +RFC-WS-7-OBSERVABILITY.md §3.10. + ### OBS-6 — sentry-laravel installation gap awareness **Aanleiding:** WS-7 PR-2 smoke test faalde silent omdat sentry-laravel @@ -1785,3 +1862,30 @@ registration niet meer kan voorkomen. **Refs:** `bootstrap/app.php`, `app/Providers/AppServiceProvider.php`, `tests/Feature/Observability/EventListenerRegistrationTest.php`, RFC-WS-7-OBSERVABILITY.md §3.6. + +### OBS-9 — Staging environment GlitchTip CSP whitelist + +**Aanleiding:** PR-3 CSP-fix whitelist alleen `localhost:8200` (dev) en +`monitoring.hausdesign.nl` (prod) hard-coded in respectievelijk +`apps/app/index.html` meta-tag en `deploy/nginx/csp-spa.conf`. Wanneer +een staging-omgeving wordt geïntroduceerd (RFC §3.3 noemt +`.env.staging` als voorbeeld), zal de bijbehorende GlitchTip-host +niet ge-whitelist zijn — events worden dan stilletjes geblokkeerd +door browser-CSP zonder waarschuwing aan de test-runner. + +**Wat:** + +- Bij staging-introductie: voeg staging GlitchTip-host toe aan + `deploy/nginx/csp-spa.conf` óf maak `apps/app/index.html` meta-CSP + environment-aware via Vite build-time injection (vergelijkbaar met + `VITE_SENTRY_DSN_FRONTEND` patroon). +- Update `tests/Feature/Security/CspConnectsToObservabilityTest.php` + met staging-assertion zodat de regression-guard de nieuwe environment + dekt. + +**Prioriteit:** Laag — alleen relevant wanneer staging-omgeving wordt +opgezet. + +**Refs:** `apps/app/index.html`, `deploy/nginx/csp-spa.conf`, +`tests/Feature/Security/CspConnectsToObservabilityTest.php`, +RFC-WS-7-OBSERVABILITY.md §3.3, ARCH-OBSERVABILITY.md §7 + §10.4. diff --git a/dev-docs/RFC-WS-7-OBSERVABILITY.md b/dev-docs/RFC-WS-7-OBSERVABILITY.md index 838e0b6f..e4520d0e 100644 --- a/dev-docs/RFC-WS-7-OBSERVABILITY.md +++ b/dev-docs/RFC-WS-7-OBSERVABILITY.md @@ -249,3 +249,32 @@ WS-7 closure = alle 4 PRs gemerged + acceptance criteria 1-14 afgevinkt. - BACKLOG.md — entries voor automated-erasure script, Slack alerting (post-WS-7). - GlitchTip docs: https://glitchtip.com/documentation - GlitchTip self-hosting: https://glitchtip.com/documentation/install + +--- + +## 9. Implementation status (mei 2026) + +WS-7 implementation is voltooid. Vier PRs gemerged in `feat/ws-7-observability`: + +- **PR-1** (Infra): GlitchTip Docker stack, lokale + productie compose, daily-backup script, [`GLITCHTIP.md`](./GLITCHTIP.md) runbook. +- **PR-2** (Backend SDK): sentry-laravel + scrubber + structured logging + `BindSentryRouteContext` + `AuthScopeContextListener` + tenant resolution + impersonation discipline + listener registration discipline + `ExceptionReportingTest` + `ActivityLogIndexesTest`. +- **PR-3** (Frontend SDK): `@sentry/vue` + scrubber + Vue Router context-binding + sourcemap upload + CSP `connect-src` whitelist. +- **PR-4** (Docs + WS-8b): [`ARCH-OBSERVABILITY.md`](./ARCH-OBSERVABILITY.md) + observability runbooks + [`SECURITY_AUDIT.md`](./SECURITY_AUDIT.md) update + [`BACKLOG.md`](./BACKLOG.md) cleanup. + +**Code-implementation acceptance criteria voldaan:** 3, 4, 5, 6, 11, 12, 13. + +**Documentatie acceptance criteria voldaan:** 8, 14. + +**Resterende criteria — handmatige deploy-stappen door Bert:** + +- 1: GlitchTip op `monitoring.hausdesign.nl` met TLS + 2FA +- 2: Twee projecten + DSNs in 1Password vault +- 7: Smoke test induced 500 in staging-omgeving +- 9: Email-alerting geconfigureerd + getest +- 10: Retention-policy 90 dagen toegepast in GlitchTip admin + +Deze stappen zijn deel van WS-7 closure-checklist (door Bert handmatig uit te voeren), niet van toekomstige PRs. + +**Volledige tag-taxonomie en implementation-details:** zie [`ARCH-OBSERVABILITY.md`](./ARCH-OBSERVABILITY.md) (post-implementation reference). Deze RFC blijft historisch document; ARCH is de levende referentie. + +**Operationele procedures:** zie [`runbooks/observability-triage.md`](./runbooks/observability-triage.md) (triage incoming issues) en [`runbooks/observability-erasure.md`](./runbooks/observability-erasure.md) (GDPR Art. 17 procedure). diff --git a/dev-docs/SECURITY_AUDIT.md b/dev-docs/SECURITY_AUDIT.md index 84d8b370..f5abf775 100644 --- a/dev-docs/SECURITY_AUDIT.md +++ b/dev-docs/SECURITY_AUDIT.md @@ -618,6 +618,113 @@ Audit scope: all files under `api/` and `apps/` (app, portal). --- +## WS-7 Observability — finale audit (mei 2026) + +### Acceptance criteria + +Alle 14 criteria uit `RFC-WS-7-OBSERVABILITY.md §6` zijn geadresseerd: + +| # | Criterium | Status | +|---|---|---| +| 1 | GlitchTip op `monitoring.hausdesign.nl` met TLS + 2FA | Bert-handmatig (deploy-checklist) | +| 2 | Twee projecten + DSNs in 1Password vault | Bert-handmatig (deploy-checklist) | +| 3 | Laravel SDK geïntegreerd; errors uit prod-API verschijnen <60s | ✅ PR-2 | +| 4 | apps/app SDK geïntegreerd; errors met org/user/release context; portal-routes strict scrub | ✅ PR-3 | +| 5 | Sourcemaps upload werkt; leesbare stack traces; `.map` afwezig in publieke bundle | ✅ PR-3 (`deploy.sh`) | +| 6 | PII scrubbing-tests groen (backend + frontend) | ✅ PR-2 + PR-3 | +| 7 | Smoke test induced 500 in staging | Bert-handmatig (deploy-checklist) | +| 8 | `ARCH-OBSERVABILITY.md` geschreven (WS-8b) | ✅ PR-4 | +| 9 | Email-alerting geconfigureerd + getest | Bert-handmatig (GlitchTip UI configuratie) | +| 10 | Retention-policy 90 dagen toegepast | Bert-handmatig (GlitchTip admin) | +| 11 | Daily postgres-backup-script in place | ✅ PR-1 | +| 12 | Activity_log indexes (D-06) gemigreerd | ✅ Pre-existing (Spatie default `nullableMorphs`); regression-guard `tests/Feature/Database/ActivityLogIndexesTest.php` | +| 13 | Structured logging conventie geïmplementeerd; `X-Request-Id` round-trip getest | ✅ PR-2 | +| 14 | `SECURITY_AUDIT.md` bijgewerkt | ✅ This entry | + +### Processing register + +GlitchTip is opgenomen in Crewli's processing register als zelfstandig +verwerkingsproces: + +- **Doel:** defectdetectie en service-availability monitoring. +- **Categorieën persoonsgegevens:** pseudonieme identifiers (ULIDs voor + user/organisation/event), technische metadata (route names, HTTP + methods, stack traces zonder locals). +- **Bron:** geautomatiseerd captured uit Laravel API en apps/app SPA + bij programmer/infra errors. +- **Ontvanger:** alleen Bert (single super_admin met 2FA op GlitchTip + web-UI). +- **Bewaartermijn:** 90 dagen, daarna automatisch gepurged door + GlitchTip's eigen partition-maintenance loop. +- **Beveiligingsmaatregelen:** TLS in transit, full-disk encryption at + rest, SSH-key + 2FA op web-UI. +- **Controller / processor:** Crewli is **controller**. Self-hosted op + Crewli-infra; geen processor-relatie of DPA-uitbreiding nodig. +- **Procedure right to erasure (Art. 17):** zie + [`runbooks/observability-erasure.md`](./runbooks/observability-erasure.md). + +### Security controls die WS-7 introduceert + +1. **PII scrubbing op events (back + front).** `SentryEventScrubber` + (PHP) en `scrubEvent` (TypeScript) strippen sensitive body-keys, + headers, query-params, cookies, en form_values voordat events naar + GlitchTip gestuurd worden. Regression-guards: + `tests/Feature/Observability/PiiScrubbingTest.php` (20 cases) en + `apps/app/src/observability/__tests__/scrubber.spec.ts` (18 cases). + +2. **CSP `connect-src` whitelist voor named ingest host.** Outgoing + Sentry-events zijn beperkt tot `localhost:8200` (dev) en + `monitoring.hausdesign.nl` (prod). Geen exfiltration mogelijk naar + arbitrary hosts. Regression-guard: + `tests/Feature/Security/CspConnectsToObservabilityTest.php`. Zie + ook A13-9 hierboven. + +3. **Sourcemap upload-only, never public-mapped.** `deploy.sh` upload + sourcemaps naar GlitchTip vóór `find apps/app/dist -name '*.map' + -delete`. Stack-traces leesbaar in GlitchTip UI; geen `.map` + bestanden bereikbaar via productie-bundle. Default soft-fail op + upload faalt zodat deploy doorgaat (unmapped frames in GlitchTip + is acceptabel; geblokkeerde deploy niet). + +4. **Listener registration discipline.** Auto-discovery + uitgeschakeld; alle observability listeners expliciet geregistreerd + in `AppServiceProvider::boot()` met array-callable form. + Regression-guard: + `tests/Feature/Observability/EventListenerRegistrationTest.php` + (BACKLOG OBS-8). Voorkomt silent double-emission die op een + toekomstig moment additive operations zou breken. + +5. **Runtime context-split portal/organizer.** Frontend portal-zone + (`route.meta.public === true && route.meta.context === 'portal'`) + krijgt geen `user_id` of `username` op events. RFC §3.7 frontend- + block punt 5 — ULID-tokens voor token-based access (artist + advance, public form fill) blijven uit GlitchTip-events. + Regression-guard: + `apps/app/src/observability/__tests__/contextBinding.spec.ts` + (cross-zone leak test). + +6. **Multi-tenant invariant op tag-niveau.** + `actor_scope=organisation` impliceert valide ULID `organisation_id`; + `actor_scope=platform` impliceert geen `organisation_id` (forced + fallback zou misleidend zijn). Regression-guard: + `AuthScopeContextListenerTest::test_organisation_id_present_when_actor_scope_is_organisation`. + +7. **`impersonation.active` always-present binary signal.** Default- + in-listener (`'false'`) + override-in-middleware (`'true'`) pattern + garandeert dat élke authenticated event een binary signal voor + filtering heeft. Regression-guard: + `AuthScopeContextListenerTest::test_impersonation_active_default_false_across_every_actor_scope_branch`. + +### Pointer naar Art. 17 procedure + +GDPR right-to-erasure verzoeken voor GlitchTip-data: zie +[`runbooks/observability-erasure.md`](./runbooks/observability-erasure.md) +voor stap-voor-stap procedure. Geautomatiseerd erasure-script staat +op BACKLOG; tot dan handmatige psql-procedure met audit-trail +verplichting. + +--- + ## Positive Findings The following security measures ARE correctly implemented: