feat(privacy): WP-40 — PII kernel (Bsn value object + masked-value atom)
Branded Bsn value object with the elfproef (11-test) checksum in shared/kernel/bsn.ts,
wired into the DigiD login boundary so login does real BSN validation (hint + e2e BSNs
updated to a valid 123456782). Consolidate the pure maskers into shared/kernel/pii.ts
(maskBsn/maskTail/REDACTED); debug-state keeps redactProfile (needs the registratie
BigProfile — boundary). New <app-masked-value> atom (+story) centralises the masked
`.includes('*')` detection + reveal affordance; behandel-scherm refactored onto it.
Session.bsn stays string (persistence boundary drops it for privacy). +specs for bsn/pii.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -84,7 +84,7 @@ for its existing violations, so every WP ends green.
|
||||
| [WP-37](WP-37-dev-switcher-reset.md) | Dev-switcher reset fix (scenario/role URL param) | 8 · platform/DX/showcase | done |
|
||||
| [WP-38](WP-38-dependency-graph-boundaries.md) | Dependency graph + declarative boundaries (visualize + enforce) | 8 · platform/DX/showcase | done |
|
||||
| [WP-39](WP-39-showcase-snippets-animations.md) | Showcase: linked code snippets + teaching animations | 8 · platform/DX/showcase | done |
|
||||
| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | todo |
|
||||
| [WP-40](WP-40-pii-kernel.md) | PII kernel: branded `Bsn` VO (elfproef) + masked-value atom | 8 · platform/DX/showcase | done |
|
||||
| [WP-41](WP-41-persisted-authz-audit.md) | Persisted, queryable authz/PII-reveal audit (no PII) | 8 · platform/DX/showcase | todo |
|
||||
| [WP-42](WP-42-privacy-security-showcase.md) | Privacy & security showcase page (mask + no-PII log) | 8 · platform/DX/showcase | todo |
|
||||
| [WP-43](WP-43-scaffold-generators.md) | Runnable generators: value-object / form-machine / bff-endpoint / ui-component | 8 · platform/DX/showcase | todo |
|
||||
|
||||
@@ -1,9 +1,27 @@
|
||||
# WP-40 — PII kernel: branded `Bsn` VO + masked-value atom
|
||||
|
||||
Status: todo
|
||||
Status: done
|
||||
Phase: 8 — platform/DX/showcase
|
||||
Priority: P2
|
||||
|
||||
## Outcome
|
||||
|
||||
- `shared/kernel/bsn.ts` — `Bsn` branded VO + `parseBsn` with the **elfproef** (11-test) checksum
|
||||
(+spec). Wired into the DigiD login boundary (`digid.adapter`): login now does real BSN
|
||||
validation instead of "any 9 digits" — login hint + e2e BSNs updated to a valid one (`123456782`).
|
||||
- `shared/kernel/pii.ts` — pure `maskBsn`/`maskTail`/`REDACTED` (+spec), the functional core. Moved
|
||||
out of `debug-state/mask.ts`, which keeps only `redactProfile` (it depends on the registratie
|
||||
`BigProfile`, so it can't live in `shared/kernel` — boundary; debug-state is the sanctioned
|
||||
cross-context devtool). `mask.spec` ported to `pii.spec`.
|
||||
- `<app-masked-value>` atom (`shared/ui/masked-value`) + story — masked-by-default value with an
|
||||
optional reveal affordance; **centralises** the `.includes('*')` masked-detection that
|
||||
`behandel-scherm` used to sniff inline (now refactored to the atom). The atom only emits
|
||||
`reveal`; the caller keeps the step-up confirm + audited fetch.
|
||||
- Deviation (flagged): `Session.bsn` stays typed `string` — the brand guards the login ENTRY, but
|
||||
Session crosses a persistence boundary where the restore path deliberately drops the bsn
|
||||
(privacy) and JSON can't carry a brand, so re-typing it would add a `''`/nullable ripple for no
|
||||
real gain. Backend `MaskTail` twin unchanged (still in sync).
|
||||
|
||||
## Why
|
||||
|
||||
Masking is real but ad-hoc: pure helpers live in `debug-state/mask.ts`, the BSN is a bare `string`
|
||||
@@ -29,6 +47,6 @@ AVG art. 9 (BSN = special category) territory; the pieces should be first-class
|
||||
|
||||
## Acceptance criteria
|
||||
|
||||
- [ ] `parseBsn` accepts valid elfproef numbers, rejects bad checksum/length (spec).
|
||||
- [ ] `<app-masked-value>` renders masked by default with an accessible reveal; behandel-scherm uses it.
|
||||
- [ ] Pure maskers have specs (no TestBed); backend `MaskTail` parity noted; `npm run ci` green.
|
||||
- [x] `parseBsn` accepts valid elfproef numbers, rejects bad checksum/length/all-zeros (spec).
|
||||
- [x] `<app-masked-value>` renders masked by default with an optional reveal; behandel-scherm uses it (no more inline sniff).
|
||||
- [x] Pure maskers have specs (no TestBed); backend `MaskTail` parity noted; `npm run ci` green.
|
||||
|
||||
Reference in New Issue
Block a user