Enforce name limits server-side and reject hidden runes in model names

Security review follow-ups. The 200-character registry-name cap the UI
forms promise now holds in domain.Validate for categories, tags,
merchants and instruments, so a non-browser client cannot persist an
unbounded name that every subsequent state response would carry. And a
model-supplied merchant or taxonomy name containing control or format
code points — bidi overrides, zero-width characters — is dropped like
an identifier-shaped one: React escaping already prevented injection,
but such names could visually spoof or reorder the review UI the
operator approves from.
This commit is contained in:
Lars Nolden
2026-09-14 12:30:13 +02:00
parent 676065292e
commit b7e5bf26cc
5 changed files with 28 additions and 12 deletions
+2
View File
@@ -75,6 +75,8 @@ func TestDomainRejectsBrokenReferencesAndTaxonomy(t *testing.T) {
{"duplicate identity", func(d *Dataset) { d.Tags[0].ID = "acc_main" }},
{"invalid provenance date", func(d *Dataset) { d.Transactions[0].Enrichment.Classification.Timestamp = "yesterday" }},
{"nonleaf merchant default", func(d *Dataset) { d.Merchants[0].DefaultCategoryID = "cat_food" }},
{"oversized tag name", func(d *Dataset) { d.Tags[0].Name = strings.Repeat("x", 201) }},
{"oversized category name", func(d *Dataset) { d.Categories[2].Name = strings.Repeat("x", 201) }},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {