Skip to content

fix(cp): bound and anchor the name-normalization rules - #300

Merged
netravnen merged 1 commit into
dev-nextfrom
fix/cp-name-normalization-guards
Aug 19, 2026
Merged

netravnen merged 1 commit into
dev-nextfrom
fix/cp-name-normalization-guards

Conversation

@netravnen

Copy link
Copy Markdown
Contributor

Four defects in lib/cp-name-normalization.js let third-party RDAP strings
either freeze the tab or silently corrupt an organization name that an
admin then approves. All four were reproduced by execution before being
fixed, and each guard was proven to fail without its fix.

A separator-dense name backtracks quadratically through the legal-form
patterns -- 6,400 characters measured about 1.9 seconds, 20,000 took 20.
Two other rules matched in the wrong place: the trading-as extraction was
unanchored, so "DBA Systems Inc" became "Systems Inc" and "Sundba Media
Group" became "Media Group"; and the Polish civil-partnership rule let a
leading S.C. match with nothing before it, so Romania's "Societate
Comerciala" prefix collapsed "S.C. Digital Cable Systems Romania SRL" to
"S.C". The "Trade Me" false-positive guard compared the function's input
rather than the working candidate, so it held for the bare brand and
evaporated on the commoner registered form, leaving "Trade".

Changes:

  • Add MAX_NORMALIZABLE_NAME_LENGTH (200) and return early from
    stripCompanyTypeSuffix above it. The quadratic patterns remain but are
    unreachable with a pathological input; rewriting 150+ jurisdiction
    patterns would risk exactly the silent regressions this module's
    characterization suite exists to catch.
  • Extract the false-positive list into KNOWN_INTACT_NAMES plus an
    isKnownIntactName() helper, and consult it on every pass of the
    suffix-strip loop rather than once on the input.
  • Anchor sanitizeRdapOrgName's trading-as regex and take its second
    group, matching the already-correct twin in parseRdapTradingAsIdentity
    instead of keeping a second, divergent copy.
  • Require a company part before the marker in parsePolishScPartnerIdentity
    (.+?\s+S.C. rather than .*?\bS.C.).

Security:

  • Removes a denial-of-service reachable from unvalidated third-party RDAP
    data: a long org name froze the admin's tab for seconds per call.
  • The two anchoring fixes remove silent data corruption on a path whose
    output an admin approves into PeeringDB; a truncated or mis-split legal
    name previously looked like a legitimate normalization result.

Testing:

  • node --test: 505 tests, 504 pass, 1 skipped (live tests are opt-in).
  • Five new cases in tests/cp-name-normalization.test.js covering all four
    defects plus the inputs that must keep working: the genuine Polish
    "NET-KONT@KT S.C." split, a real "trading as" extraction, and a
    long-but-plausible name that must still normalize.
  • Each fix reverted individually and confirmed red: the length guard
    (20s, then failure), the per-layer Trade Me guard, the anchored
    trading-as regex, and the required S.C. company part.
  • build_userscripts.py regenerated CP and reports up to date; all three
    .user.js pass node --check.

Backwards Compatibility:

  • No API or storage changes. Behavior changes only for inputs that were
    previously mishandled; the existing characterization suite, which locks
    in current output for every other shape, is unchanged and still green.

Assisted-by: Claude:claude-opus-5


Stack created with GitHub Stacks CLI • Give Feedback 💬

@netravnen
netravnen force-pushed the fix/cp-name-normalization-guards branch from 1bedc3e to cd6edae Compare August 19, 2026 22:27
@netravnen
netravnen force-pushed the fix/cp-name-normalization-guards branch from cd6edae to 2131890 Compare August 19, 2026 22:28
@netravnen
netravnen force-pushed the fix/cp-name-normalization-guards branch from 2131890 to 90f8124 Compare August 19, 2026 22:29
@netravnen
netravnen force-pushed the fix/cp-name-normalization-guards branch from 90f8124 to fd35790 Compare August 19, 2026 22:31
Base automatically changed from fix/cp-pdbfetch-throws to dev-next August 19, 2026 23:05
Four defects in lib/cp-name-normalization.js let third-party RDAP strings
either freeze the tab or silently corrupt an organization name that an
admin then approves. All four were reproduced by execution before being
fixed, and each guard was proven to fail without its fix.

A separator-dense name backtracks quadratically through the legal-form
patterns -- 6,400 characters measured about 1.9 seconds, 20,000 took 20.
Two other rules matched in the wrong place: the trading-as extraction was
unanchored, so "DBA Systems Inc" became "Systems Inc" and "Sundba Media
Group" became "Media Group"; and the Polish civil-partnership rule let a
*leading* S.C. match with nothing before it, so Romania's "Societate
Comerciala" prefix collapsed "S.C. Digital Cable Systems Romania SRL" to
"S.C". The "Trade Me" false-positive guard compared the function's input
rather than the working candidate, so it held for the bare brand and
evaporated on the commoner registered form, leaving "Trade".

Changes:
- Add MAX_NORMALIZABLE_NAME_LENGTH (200) and return early from
  stripCompanyTypeSuffix above it. The quadratic patterns remain but are
  unreachable with a pathological input; rewriting 150+ jurisdiction
  patterns would risk exactly the silent regressions this module's
  characterization suite exists to catch.
- Extract the false-positive list into KNOWN_INTACT_NAMES plus an
  isKnownIntactName() helper, and consult it on every pass of the
  suffix-strip loop rather than once on the input.
- Anchor sanitizeRdapOrgName's trading-as regex and take its second
  group, matching the already-correct twin in parseRdapTradingAsIdentity
  instead of keeping a second, divergent copy.
- Require a company part before the marker in parsePolishScPartnerIdentity
  (`.+?\s+S.C.` rather than `.*?\bS.C.`).

Security:
- Removes a denial-of-service reachable from unvalidated third-party RDAP
  data: a long org name froze the admin's tab for seconds per call.
- The two anchoring fixes remove silent data corruption on a path whose
  output an admin approves into PeeringDB; a truncated or mis-split legal
  name previously looked like a legitimate normalization result.

Testing:
- node --test: 505 tests, 504 pass, 1 skipped (live tests are opt-in).
- Five new cases in tests/cp-name-normalization.test.js covering all four
  defects plus the inputs that must keep working: the genuine Polish
  "NET-KONT@KT S.C." split, a real "trading as" extraction, and a
  long-but-plausible name that must still normalize.
- Each fix reverted individually and confirmed red: the length guard
  (20s, then failure), the per-layer Trade Me guard, the anchored
  trading-as regex, and the required S.C. company part.
- build_userscripts.py regenerated CP and reports up to date; all three
  .user.js pass node --check.

Backwards Compatibility:
- No API or storage changes. Behavior changes only for inputs that were
  previously mishandled; the existing characterization suite, which locks
  in current output for every other shape, is unchanged and still green.

Assisted-by: Claude:claude-opus-5
@netravnen
netravnen force-pushed the fix/cp-name-normalization-guards branch from fd35790 to fddc4a7 Compare August 19, 2026 23:05
@netravnen
netravnen marked this pull request as ready for review August 19, 2026 23:06
@netravnen
netravnen merged commit 5a5d791 into dev-next Aug 19, 2026
3 checks passed
@netravnen
netravnen deleted the fix/cp-name-normalization-guards branch August 20, 2026 09:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant