Purpose
Per README.md, the tool's job is reliable DNS-based enumeration ("Deduplicated output — sorted unique FQDNs that resolved successfully"). This finding is a defect against that purpose: every failure mode — NXDOMAIN, resolver outage, timeout, unexpected error — is collapsed into "not found", so a DNS outage prints Found 0 subdomain(s) exactly like a clean domain. Results become unverifiable, not a tradeoff.
Location
subdomain_enum.py:35-39:
try:
await asyncio.wait_for(loop.getaddrinfo(fqdn, None), timeout=timeout)
return fqdn, True
except Exception:
return fqdn, False
Repro (no network)
import asyncio, socket, sys
sys.path.insert(0, '/tmp/opencode/scratch/subdomain-enumerator')
import subdomain_enum as se
from unittest import mock
async def raise_gai(*a, **k): raise socket.gaierror(8, 'Name or service not known')
async def raise_boom(*a, **k): raise RuntimeError('simulated resolver outage')
async def main():
sem = asyncio.Semaphore(5)
with mock.patch.object(asyncio.BaseEventLoop, 'getaddrinfo', raise_gai):
r1 = await se.resolve('www', 'example.com', sem, 2.0)
with mock.patch.object(asyncio.BaseEventLoop, 'getaddrinfo', raise_boom):
r2 = await se.resolve('www', 'example.com', sem, 2.0)
print(r1, r2)
asyncio.run(main())
Observed
('www.example.com', False) ('www.example.com', False)
A name that does not exist and a total resolver failure are indistinguishable. A user mid-outage gets a confident-looking Found 0 subdomain(s) and may conclude the target has no attack surface.
Expected
Catch only expected lookup failures (socket.gaierror, asyncio.TimeoutError/TimeoutError) as not-found; let unexpected exceptions propagate (or count them as errors, report to stderr, and exit non-zero when the error rate is high).
Severity
Medium — silent conflation of "absent" with "unknown"; misleads the primary result.
Safe remediation
Narrow the except to (socket.gaierror, asyncio.TimeoutError, TimeoutError); add an error counter surfaced in the final summary. No change to the success path.
Purpose
Per README.md, the tool's job is reliable DNS-based enumeration ("Deduplicated output — sorted unique FQDNs that resolved successfully"). This finding is a defect against that purpose: every failure mode — NXDOMAIN, resolver outage, timeout, unexpected error — is collapsed into "not found", so a DNS outage prints
Found 0 subdomain(s)exactly like a clean domain. Results become unverifiable, not a tradeoff.Location
subdomain_enum.py:35-39:Repro (no network)
Observed
A name that does not exist and a total resolver failure are indistinguishable. A user mid-outage gets a confident-looking
Found 0 subdomain(s)and may conclude the target has no attack surface.Expected
Catch only expected lookup failures (
socket.gaierror,asyncio.TimeoutError/TimeoutError) as not-found; let unexpected exceptions propagate (or count them as errors, report to stderr, and exit non-zero when the error rate is high).Severity
Medium — silent conflation of "absent" with "unknown"; misleads the primary result.
Safe remediation
Narrow the
exceptto(socket.gaierror, asyncio.TimeoutError, TimeoutError); add an error counter surfaced in the final summary. No change to the success path.