Summary
Entity canonical_name values can be stored containing embedded newlines (and other control whitespace) — extraction artifacts that survive normalization. Any line-oriented consumer then shears: psql -A output splits one row across lines, log lines break, exports corrupt. Measured on a production bank (2026-08-08): multiple entities with \n inside their canonical names, found when a diagnostic query's rows stopped parsing.
Fix implemented
Candidate entity names are whitespace-normalized (all internal runs of \s+ collapsed to a single space, ends stripped) at the resolution intake choke point (_prepare_entities_for_resolution), before matching or creation; case handling untouched. Fix PR: #3277 (27 tests, attack cases first).
Note: this stops new artifacts; rows already stored with embedded newlines remain until cleaned (renormalizing them can collide with the (bank_id, LOWER(canonical_name)) uniqueness, so cleanup is a merge, not an UPDATE).
Summary
Entity
canonical_namevalues can be stored containing embedded newlines (and other control whitespace) — extraction artifacts that survive normalization. Any line-oriented consumer then shears:psql -Aoutput splits one row across lines, log lines break, exports corrupt. Measured on a production bank (2026-08-08): multiple entities with\ninside their canonical names, found when a diagnostic query's rows stopped parsing.Fix implemented
Candidate entity names are whitespace-normalized (all internal runs of
\s+collapsed to a single space, ends stripped) at the resolution intake choke point (_prepare_entities_for_resolution), before matching or creation; case handling untouched. Fix PR: #3277 (27 tests, attack cases first).Note: this stops new artifacts; rows already stored with embedded newlines remain until cleaned (renormalizing them can collide with the
(bank_id, LOWER(canonical_name))uniqueness, so cleanup is a merge, not an UPDATE).