mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 15:21:30 +02:00
Target correlation returned nothing for every target in a real project, and three of its four signals could not have worked. A registrant is matched on what two records must share to be the same party: case, punctuation and the company suffix are stripped by `registrant_key`, whose SQL half sits beside it (`registrant_key_sql`) because a match invisible to one half is a relation that never appears. Both halves refuse a redacted name, so "Redacted for Privacy" cannot group every privacy-protected domain in the instance into one false cluster. Measured on shopify.com and shopifycloud.com: one relation, on the registrant, and nothing between the four unrelated estates beside them. A registrar is dropped as a correlation. MarkMonitor registers for thousands of unrelated brands, so "same registrar" asserts nothing - shopify.com and uber.com share it and are not related. It stays available as a deliberate lookup in the correlation dialog, which is a question the operator asks, not a relation reNgine claims. The case-sensitive exact match it used would have missed those two anyway: `MarkMonitor Inc.` against `Markmonitor Inc.`. A nameserver is the one strong signal here - `a.ns.hackerone.com` is identifying, `gold.foundationdns.com` is Cloudflare's enterprise DNS and identifies nothing. Fifteen managed providers join the suppression list, foundationdns and markmonitor among them. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01YYop31s6BqDUt4TBs5mbwa
167 lines
3.5 KiB
Python
167 lines
3.5 KiB
Python
import re
|
|
|
|
from sqlalchemy import case, func, literal
|
|
|
|
_WHITESPACE_RE = re.compile(r"\s+")
|
|
|
|
_NON_IDENTIFYING_EXACT = frozenset(
|
|
{
|
|
"",
|
|
"-",
|
|
"--",
|
|
"n/a",
|
|
"na",
|
|
"none",
|
|
"null",
|
|
"nil",
|
|
"no",
|
|
"tbd",
|
|
"unknown",
|
|
"not applicable",
|
|
"not available",
|
|
"not disclosed",
|
|
}
|
|
)
|
|
|
|
_REDACTED_NAME_MARKERS = frozenset(
|
|
{
|
|
"redacted",
|
|
"redact",
|
|
"privacy",
|
|
"whoisguard",
|
|
"data protected",
|
|
"data protection",
|
|
"non-public data",
|
|
"withheld for",
|
|
"domains by proxy",
|
|
"by proxy",
|
|
"proxy protection",
|
|
"identity protection",
|
|
"domain protection",
|
|
"private registration",
|
|
"registration private",
|
|
"registrant private",
|
|
"gdpr",
|
|
"statutory masking",
|
|
"masking enabled",
|
|
"obscured whois",
|
|
"privacydotlink",
|
|
"anonymize",
|
|
"anonymised",
|
|
"anonymized",
|
|
}
|
|
)
|
|
|
|
_REDACTED_EMAIL_MARKERS = frozenset(
|
|
{
|
|
"redacted",
|
|
"privacy",
|
|
"whoisguard",
|
|
"proxy",
|
|
"withheld",
|
|
"gdpr",
|
|
"anonym",
|
|
}
|
|
)
|
|
|
|
|
|
def _normalize(value: str) -> str:
|
|
return _WHITESPACE_RE.sub(" ", value).strip().lower()
|
|
|
|
|
|
def is_redacted_name(value: str | None) -> bool:
|
|
if not value:
|
|
return True
|
|
normalized = _normalize(value)
|
|
if normalized in _NON_IDENTIFYING_EXACT:
|
|
return True
|
|
return any(marker in normalized for marker in _REDACTED_NAME_MARKERS)
|
|
|
|
|
|
def is_redacted_email(value: str | None) -> bool:
|
|
if not value:
|
|
return True
|
|
normalized = value.strip().lower()
|
|
if "@" not in normalized:
|
|
return True
|
|
return any(marker in normalized for marker in _REDACTED_EMAIL_MARKERS)
|
|
|
|
|
|
def clean_name(value: str | None) -> str:
|
|
if not value or is_redacted_name(value):
|
|
return ""
|
|
return value.strip()
|
|
|
|
|
|
def clean_email(value: str | None) -> str:
|
|
if not value or is_redacted_email(value):
|
|
return ""
|
|
return value.strip().lower()
|
|
|
|
|
|
CORPORATE_SUFFIXES = (
|
|
"incorporated",
|
|
"corporation",
|
|
"limited",
|
|
"holdings",
|
|
"company",
|
|
"group",
|
|
"gmbh",
|
|
"corp",
|
|
"llc",
|
|
"ltd",
|
|
"inc",
|
|
"plc",
|
|
"sas",
|
|
"sarl",
|
|
"bv",
|
|
"nv",
|
|
"ab",
|
|
"as",
|
|
"oy",
|
|
"pty",
|
|
"pte",
|
|
"srl",
|
|
"spa",
|
|
"kk",
|
|
"co",
|
|
"sa",
|
|
)
|
|
|
|
|
|
def registrant_key(value: str | None) -> str:
|
|
"""A registrant name reduced to what two records must share to be the same party."""
|
|
name = clean_name(value)
|
|
if not name:
|
|
return ""
|
|
words = [w for w in re.split(r"[^a-z0-9]+", name.lower()) if w]
|
|
while words and words[-1] in CORPORATE_SUFFIXES:
|
|
words.pop()
|
|
return "".join(words)
|
|
|
|
|
|
def redacted_name_sql(column):
|
|
"""The SQL half of is_redacted_name."""
|
|
collapsed = func.btrim(func.regexp_replace(func.lower(column), r"\s+", " ", "g"))
|
|
markers = "|".join(sorted(_REDACTED_NAME_MARKERS))
|
|
return func.coalesce(collapsed, "").in_(
|
|
sorted(_NON_IDENTIFYING_EXACT)
|
|
) | collapsed.op("~")(markers)
|
|
|
|
|
|
def registrant_key_sql(column):
|
|
"""The SQL half of registrant_key: the two must agree, or a match is invisible."""
|
|
suffixes = "|".join(CORPORATE_SUFFIXES)
|
|
key = func.regexp_replace(
|
|
func.regexp_replace(
|
|
func.lower(column),
|
|
f"([^a-z0-9]+({suffixes}))+[^a-z0-9]*$",
|
|
"",
|
|
"g",
|
|
),
|
|
"[^a-z0-9]",
|
|
"",
|
|
"g",
|
|
)
|
|
return case((redacted_name_sql(column), literal("")), else_=key)
|