feat(content-policy): Product.content_policy + AVG checker (sub-project C, Phase 0) #16
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "feat/copilot-content-policy"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Sub-project C, Phase 0 — the canonical foundation for the per-product AVG content-policy gate.
What
prisma/schema.prisma:Product.content_policy Json?— additive, nullable (null= no restriction; no behaviour change for existing products).lib/content-policy.ts(dependency-free):parseContentPolicy— fail-closed on malformed JSON and on a self-contradictory policy (anallowedFieldTermthat would mask its own forbidden field).checkContentPolicy— standalone, boundary-aware allowlist masking + substring field/feature match. Pure string-ops (no RegExp built from policy data → no injection/ReDoS), code-point-correct boundaries, anti-evasionnormalize(zero-width/format-char strip + whitespace-collapse).__tests__/content-policy.test.ts— 23 tests.Policy shape
{ forbiddenFields: string[], forbiddenFeatureTerms: string[], allowedFieldTerms: string[] } | nullReview record
3 codex rounds + 3 independent adversarial rounds; every finding reproduced against the real code and fixed — 1×P1 (prefix-compound bypass), 4×P2 (incl. allowlist-side re-open + non-BMP/astral boundary), several P3. codex final verdict: akkoord, geen P0–P3 bevindingen.
npm run verifygreen (131/131).Next (NOT in this PR)
Canonical schema + checker only. The live-DB migration is 154's lane (Phase 1: bump the consumer submodule →
prisma migrate devauthors the SQL →prisma migrate deployon the live DB, migrate-first), after which the enforcement gates land in scrum4me-mcp + Scrum4Me-web (Phases 2/3). Design: spec rev3.1; plan: the C implementation plan.🤖 Generated with Claude Code
Adversarial review found two bypasses in the lexical checker: - invisible chars (ZWSP/BOM/soft-hyphen) inside a forbidden field passed - multi-word feature terms split by double-space/newline/tab missed (call-sites join title+\n+description, so a split phrase silently missed) normalize() now strips \p{Cf}+U+00AD and collapses whitespace. +2 regression tests.