Problem Statement. Models in app/backend/prisma/schema.prisma's Campaign.metadata,
Claim.recipientRef, Session.metadata, SessionStep.input/output, and
InternalNote.content accept arbitrary PII (the README and SECURITY.md both flag PII as
in-scope for hardening). Some of these fields are forwarded to the AI service which, per the
AI service's own design, anonymises input text but cannot undo PII that lives in
metadata blobs.
Why it matters. Humanitarian PII leakage is a humanitarian incident. The platform's
private-by-design stance is only as strong as its widest write path.
Technical Context. The backend does not run the same PII scrubber that
pii_scrubber.py does. Either it accepts a JSON metadata blob unchanged, or it sends a
recipient address as a raw string.
Expected Outcome. A PiiScrubInterceptor (backend) strips common PII (email, phone,
personal-name patterns, IDs) from metadata-typed payload fields before storage or before
forwarding to the AI service. A redact mode replaces values with structured tokens; a strict
mode rejects the request with a 422 listing the offending field paths.
Acceptance Criteria.
- An interceptor that traverses any object tree, identifies a configurable list of
high-risk keys (email, phone, name, nin, etc.), and either redacts or rejects.
- Unit tests cover deeply nested cases and arrays.
- An opt-out knob
PII_SCRUB_MODE=redact|reject|off (default redact).
- The interceptor is registered after
ValidationPipe in main.ts.
Implementation Notes. Share a Redis-cached, periodically-refreshed regex set from
/api/v1/pii/patterns. Consider a subtree-level allowlist (e.g. raw recipientRef from a
known-good JWT subject).
Files or modules likely to be affected. src/common/interceptors/pii-scrub.interceptor.ts
(new), src/main.ts, app/backend/test/pii-interceptor.spec.ts (new),
app/ai-service/services/pii_scrubber.py (reuse logic).
Dependencies. None.
Difficulty. Medium
Estimated effort. M
Backlog item #5 from `docs/maintainer-issue-backlog.md.
Problem Statement. Models in
app/backend/prisma/schema.prisma'sCampaign.metadata,Claim.recipientRef,Session.metadata,SessionStep.input/output, andInternalNote.contentaccept arbitrary PII (the README and SECURITY.md both flag PII asin-scope for hardening). Some of these fields are forwarded to the AI service which, per the
AI service's own design, anonymises input text but cannot undo PII that lives in
metadatablobs.Why it matters. Humanitarian PII leakage is a humanitarian incident. The platform's
private-by-design stance is only as strong as its widest write path.
Technical Context. The backend does not run the same PII scrubber that
pii_scrubber.pydoes. Either it accepts a JSON metadata blob unchanged, or it sends arecipient address as a raw string.
Expected Outcome. A
PiiScrubInterceptor(backend) strips common PII (email, phone,personal-name patterns, IDs) from
metadata-typed payload fields before storage or beforeforwarding to the AI service. A redact mode replaces values with structured tokens; a strict
mode rejects the request with a 422 listing the offending field paths.
Acceptance Criteria.
high-risk keys (
email,phone,name,nin, etc.), and either redacts or rejects.PII_SCRUB_MODE=redact|reject|off(defaultredact).ValidationPipeinmain.ts.Implementation Notes. Share a Redis-cached, periodically-refreshed regex set from
/api/v1/pii/patterns. Consider a subtree-level allowlist (e.g. rawrecipientReffrom aknown-good JWT subject).
Files or modules likely to be affected.
src/common/interceptors/pii-scrub.interceptor.ts(new),
src/main.ts,app/backend/test/pii-interceptor.spec.ts(new),app/ai-service/services/pii_scrubber.py(reuse logic).Dependencies. None.
Difficulty. Medium
Estimated effort. M
Backlog item #5 from `docs/maintainer-issue-backlog.md.