Commit Graph

12 Commits

Author SHA1 Message Date
a2be5d5488 feat(core-cms): register audit-logs + wire GDPR audit erasure
The audit-logs collection was never registered (record() would throw),
bindAudit/createAuditErasureHook were unused, and DSR cascade-hard never
touched the audit trail (audit finding A6). core-cms now registers the
collection and wires a req-scoped afterDelete erasure hook on users;
bindAllProduction binds the audit log into consent/DSR; cascade-hard
pseudonymizes the subject's audit entries; the action select accepts
the full AuditAction enum so consent/DSR entries pass validation.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
7b0c2ea590 fix(auth): declare users email/username/displayName in DSR pii map
The DSR walkers read the COLLECTION-level custom.pii map, which the
users collection never declared — Art. 15 export returned bare ids and
Art. 17 soft delete redacted nothing; the auth-injected email field in
particular was invisible (audit finding A5). Declares email (auto-added
by Payload auth: true), username and displayName as exportable +
restrictable; walker tests pin a users-shaped collection end to end.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
413ac0273c feat(core-shared): grace-purge soft-deleted rows + boot registration
The retention purge job gated its whole body on activeRetention while
every collection declares only postDeletion, and no app ever called
registerRetentionPurgeJobs — retention was dead end to end (audit
findings A2 + A3). The DSR soft delete now stamps a deletedAt tombstone
on postDeletion collections (kept distinct from processingRestrictedAt
so an Art. 18 restriction never feeds the purge), the job grace-purges
tombstoned rows past postDeletion.duration with the declared action,
core-cms injects the tombstone field + Payload task definitions, and
bindAllProduction enqueues the first purge cycle at boot.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
49241845b5 feat(web-next): resolve the session user + live compliance context
The tRPC createContext was () => ({}) — the mounted dsr/consent routers
401'd every call and the dsr singleton stub threw (audit finding A11).
createTrpcContext now accepts an app resolveUser hook; web-next resolves
the session cookie through the auth feature's validateSession (denylist
included) plus a role snapshot, and threads bindProductionDsr/Consent
(or dev-seed) bindings into every request. The dsr router resolves its
binding from ctx.dsrBinding per request instead of a throwing proxy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
e2a4657471 fix(core-dsr): scope DSR operations to the caller's own subject
Non-admin callers may export/rectify/restrict/delete ONLY their own
subjectId; a mismatch is rejected with FORBIDDEN instead of being
honored verbatim (IDOR, audit finding A1). Cross-subject operations
require the admin role; cascade-hard stays admin-only on top.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
6f25b1699c build: align @trpc/* on a single workspace version
@trpc/client resolved to 11.16.0 in some packages while @trpc/server
resolved to 11.17.0 in others, tripping the exact-version peer check.
Update every @trpc/* range in-range so the whole workspace resolves to
one version (11.18.0); install is peer-warning-free again.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 18:25:46 +02:00
5f38a3efce build: bump vitest past GHSA-5xrq-8626-4rwp; coverage-v8 everywhere
pnpm audit --prod --audit-level=critical (the new CI gate) failed on
vitest <3.2.6 (critical, arbitrary file read/execute via the UI
server). Bump vitest + @vitest/coverage-v8 in-range across the
workspace and add @vitest/coverage-v8 to every package that runs
vitest but lacked it (core-audit, core-cms, core-eslint, core-testing,
core-trpc, apps/cms, web-next, web-tanstack, turbo/generators) so
'pnpm test -- --coverage' works in every package. No compound test
scripts exist, so the vitest-last pass-through concern does not apply.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:44:48 +02:00
6fd0a7ad4b test(core-dsr): cover dsrRouter singleton guard proxy path
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:36:42 +00:00
46e575a5a6 feat(core-dsr): handlers, dsrRouter, integration tests
Add four protocol-agnostic handlers (export, delete, rectify, restrict)
returning normalized { status, body, headers } responses, and a tRPC
dsrRouter via createDsrRouter(binding) following the factory pattern.

Auth checks: requireAuthenticated middleware gates all four procedures;
cascade-hard delete additionally requires admin role. Integration tests
assert happy-path response shapes, UNAUTHORIZED/FORBIDDEN error codes,
and error passthrough from the DSR service layer.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:34:51 +00:00
cbea635fac fix(core-dsr): flip processingRestrictedAt in soft-delete for self role
PayloadDataDelete.deleteSubjectData('soft') was NULLing PII fields and
emitting RESTRICT audit entries, but never setting processingRestrictedAt
on self-kind rows — violating GDPR Art. 17 restriction semantics.

softRedactOwnerRows now accepts optional extraData; processOwnerRows
passes { processingRestrictedAt: new Date().toISOString() } when
kind === 'self' and mode === 'soft'. Owner-kind rows are intentionally
excluded (restriction flag belongs on the subject record, not owned rows).

Added two dedicated tests: one asserting the field is present for self,
one asserting it is absent for owner.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:12:41 +00:00
6606b59d1e feat(core-dsr): Payload impls, recording doubles, DI binders, contract tests
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 20:05:06 +00:00
e378c950a9 feat(core-dsr): scaffold package + GDPR DSR interfaces and types
- Add pnpm turbo gen core-package dsr generator template and register
  dsr in CORE_PACKAGE_GENERATORS / choices list
- Run generator to produce packages/core-dsr/ shell
- Define IDataExport (Art. 15/20), IDataDelete (Art. 17),
  IDataRectify (Art. 16), IProcessingRestriction (Art. 18) interfaces
- Add UserDataBundle and DeletionCertificate types in dsr-types.ts
- Ship core-dsr/contexts/user-data.jsonld schema.org JSON-LD @context
- Wire @repo/core-dsr into transpilePackages (web-next)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-19 19:32:03 +00:00