mirror of
https://github.com/yogeshojha/rengine.git
synced 2026-10-20 15:21:30 +02:00
dnsx answers every -axfr query with an object — {"host": …} when refused,
{"host": …, "chain": [{…, "all": [zone lines]}]} when it succeeded — and
parse_dnsx_record nulled the whole field out because it was typed list[str]:
if isinstance(raw.get("axfr"), dict):
raw["axfr"] = None
So a successful transfer was discarded on the way in, and the Toolbox's DNS
tool printed "Zone transfer: refused" unconditionally. Measured against
zonetransfer.me: 45 names came back, 0 were kept. SubdomainSource.ZONE_TRANSFER
had been declared with no producer since the enum was written.
- DnsxAxfr models what dnsx actually returns. `transferred` is
`any(entry.all …)`, never the truthiness of the object, and
`zone_transferred` / `zone_names` are the one place every caller asks.
- hostnames() reads the presentation format by its tab-separated columns, not
a whitespace split (a TXT value may contain the word NS), taking each
record's owner and the names CNAME/NS/PTR/MX/SRV/DNAME point at, dropping
`*` owners.
- subdomain_discovery asks each apex's own nameservers before it guesses
(SubdomainConfig.zone_transfer, on by default, gated on intensity exactly as
bruteforce is). The names merge through merge_and_filter like every other
source, so they are scoped, resolved and probed.
- stages/zone_transfer/ reads back the hosts whose sources name the transfer
and writes one HIGH rengine-zone-transfer finding per zone, so it inherits
triage, is:new, alerting and reports. It is deliberately its own stage:
putting vulnerabilities in subdomain_discovery.produces would make every
name-only scan claim to cover that dimension and report "0 findings" where
the honest answer is "never scanned".
- to_db_records writes one AXFR row stating the outcome and the true count,
never a row per name — that would be a second, capped copy of an inventory
that already lives in subdomains.
Measured end to end through the real client: zonetransfer.me 45 names in 6.0s,
35 in scope; hamropatro.com refused in 4.2s with no error. 21 tests, each
proven to fail against 8 mutations including the original discard and the
truthiness bug.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013dKUV96WkQQETZBMoAG98C