Files
atomic-design-poc/docs/project/archive/refactor-backlog-setup/refactor-backlog/implementation/rb-03.md
T
ehoandClaude Opus 5 12f17d9d73 docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every
WP done) and `docs/project/refactor-backlog-setup/` (the arc before it).
Move both under `docs/project/archive/` with `git mv`, so history stays
intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them,
because it points at the now-archived backlog README.

Add `docs/project/archive/README.md`. It states that these trees are
historical and names the two directories that are still live.

Repoint every inbound reference named in RD-30's Files table: CLAUDE.md,
the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the
`document-feature` and `new-ssp` skills, and the readable-codebase PLAN,
README, and RD-19 ticket. Fix two upward-relative links inside the moved
WP files (WP-68, WP-69) that gained a directory level and would otherwise
break. Repoint `.prettierignore`'s two agent-prompt exclusions to their
new path, so prettier keeps leaving those files' exact wording alone.

Mark RD-30 done and check off its acceptance criteria; flip its README
row to done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:00:38 +02:00

2.4 KiB

RB-03 — mask the owner BSN on the cross-owner case lists

Status: implemented · 2026-08-27 · Source findings: 07-bio2-compliance.md BIO-003 · 99-backlog.md RB-03

What was wrong

Mappers.ToAdminSummaryDto set Owner = a.Owner — the raw BSN. Two endpoints consume it, both cross-owner lists read by someone who is not the subject:

  • GET /admin/cases (cases:manage)
  • GET /werkvoorraad (aanvraag:beoordelen)

GET /beoordeling/{id} — the detail view of the same data — already masked. So the detail screen showed ******782 while the list one click earlier showed the whole BSN.

What changed

File Change
Domain/People/Pii.cs newPii.MaskTail, moved out of Program.cs
Contracts/Mappers.cs Owner = Pii.MaskTail(a.Owner, 3)
Program.cs local MaskTail deleted; two call sites point at Pii.MaskTail
AdminCasesTests.cs asserts the masked value and that DemoOwner does not appear
WerkvoorraadTests.cs same assertion, replacing the IsNullOrEmpty one

Masked in the mapper, not at the endpoints. The point of the ticket is that both lists inherit it, so a third cross-owner list cannot be added that forgets to mask.

MaskTail moved to Domain/People/Pii.cs because it now has three callers across three folders (Contracts, Program.cs, and Data once RB-04 lands), and a second hand-rolled copy is how one of them drifts into leaking. It is documented as idempotent, which is what lets /beoordeling/{id} keep its own call: IZaakSource has a second implementation (OpenZaakZaakSourceZgwZaakMapper, which maps Owner from the zaak identificatie), so that endpoint's guarantee should not depend on which source answered.

Blast radius on the frontend — none

Both consumers use the value for display only (admin-cases.page.ts:101, beoordeling-view.ts:40, werkvoorraad-item-view.ts:28); the parse* boundaries require a non-empty string, which a masked BSN still is. Nothing keys, filters or looks up by owner.

Verification

dotnet format --verify-no-changes clean. dotnet test: 251 passed, 1 failed — the pre-existing OpenZaakIntegrationTests.Admin_cases_…, which needs a live container.