Skip to content

feat: add auto-wildcard detection flag#935

Open
erdogan98 wants to merge 3 commits intoprojectdiscovery:devfrom
erdogan98:feature/auto-wildcard
Open

feat: add auto-wildcard detection flag#935
erdogan98 wants to merge 3 commits intoprojectdiscovery:devfrom
erdogan98:feature/auto-wildcard

Conversation

@erdogan98
Copy link

@erdogan98 erdogan98 commented Feb 2, 2026

/claim #924

Summary

Implements automatic wildcard detection similar to PureDNS, as requested in #924.

New Flags

  • --auto-wildcard (-aw): Enable automatic wildcard detection and filtering
  • --auto-wildcard-tests: Number of random subdomain tests (default 3)

How It Works

  1. For each domain being resolved, extracts parent domains
  2. Tests parent domains with random subdomains (using xid for randomness)
  3. If random subdomains resolve to the same IPs, marks the domain as wildcard
  4. Filters out any resolved subdomains matching wildcard IPs

Usage

# Enable auto-wildcard detection
echo "subdomain.example.com" | dnsx -a --auto-wildcard

# Adjust number of test queries (default 3)
cat subdomains.txt | dnsx -a -aw --auto-wildcard-tests 5

Implementation Details

  • Thread-safe implementation with proper mutex handling
  • Caches wildcard detection results to avoid redundant queries
  • Prints summary at end: number of wildcard roots found and subdomains filtered
  • Debug logging available with -debug flag

Testing

Tested with various wildcard domains and verified:

  • Random subdomains are properly generated
  • Wildcard IPs are correctly identified
  • Matching subdomains are filtered from output

Closes #924

Summary by CodeRabbit

  • New Features

    • Automatic wildcard DNS detection: detects and filters wildcard subdomains during scans with configurable test count.
    • New CLI flags: --auto-wildcard to enable detection and --auto-wildcard-tests to set test count.
    • Summary reporting: shows detected wildcard roots and filtered subdomain count at completion.
  • Notes

    • Disabled in stream mode and incompatible with the wildcard-domain option.

Implements automatic wildcard detection similar to PureDNS (projectdiscovery#924).

New flags:
- --auto-wildcard (-aw): Enable automatic wildcard detection and filtering
- --auto-wildcard-tests: Number of random subdomain tests (default 3)

How it works:
1. For each domain being resolved, extracts parent domains
2. Tests parent domains with random subdomains (using xid for randomness)
3. If random subdomains resolve to same IPs, marks domain as wildcard
4. Filters out any resolved subdomains matching wildcard IPs

This approach is similar to PureDNS wildcard detection and helps reduce
false positives when scanning domains with wildcard DNS records.
@coderabbitai
Copy link

coderabbitai bot commented Feb 2, 2026

Walkthrough

Adds an AutoWildcardDetector that performs concurrent, per-parent wildcard DNS detection at runtime, integrates it into the Runner to filter wildcard-derived results during processing, and exposes flags to enable/configure the behavior and report detected roots and filtered counts.

Changes

Cohort / File(s) Summary
AutoWildcardDetector Implementation
internal/runner/autowildcard.go
New type implementing lazy, concurrency-safe wildcard detection: probes multiple random subdomains via dnsx, aggregates A/AAAA results into per-parent wildcard IP sets, and exposes detection, root listing, and filtered counters.
Configuration & Flags
internal/runner/options.go
Added AutoWildcard bool and AutoWildcardTestCount int to Options. New CLI flags --auto-wildcard and --auto-wildcard-tests; validation disallows stream mode and concurrent use with wildcard-domain.
Runtime Integration
internal/runner/runner.go
Runner gains autoWildcardDetector *AutoWildcardDetector, created when enabled. Worker loop calls DetectAndFilter() to skip wildcard-matched domains and increments filtered counters; final summary prints wildcard root count and filtered count.
Module deps
go.mod
Promoted golang.org/x/net to a direct dependency (v0.47.0).

Sequence Diagram

sequenceDiagram
    participant Runner
    participant Detector as AutoWildcardDetector
    participant DNSX
    participant Output

    Runner->>Detector: New(dnsxClient, testCount)
    activate Detector
    Detector->>Detector: init maps (wildcardRoots, testedDomains, inFlight)
    deactivate Detector

    loop For each domain
        Runner->>Detector: DetectAndFilter(host, hostIPs)
        activate Detector
        Detector->>Detector: getParentDomains(host)
        loop For each parent
            Detector->>Detector: ensureWildcardTested(parent)
            alt Parent not tested
                Detector->>DNSX: Query multiple random subdomains
                activate DNSX
                DNSX-->>Detector: A/AAAA records (IPs)
                deactivate DNSX
                Detector->>Detector: aggregate wildcard IPs for parent
            end
        end
        Detector->>Detector: isWildcardMatch(host, hostIPs)
        alt Match found
            Detector->>Detector: IncrementFilteredCount()
            Detector-->>Runner: true (filter)
        else No match
            Detector-->>Runner: false (process)
        end
        deactivate Detector
    end

    Runner->>Detector: GetWildcardRoots() / GetFilteredCount()
    Detector-->>Runner: results
    Runner->>Output: Print summary (root count, filtered count)
Loading

Estimated Code Review Effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 I hopped through zones and chased each stray IP,

Random names I planted where wild answers slip,
Roots unmasked their tricks, the false addresses shown,
Filtered fields now tidy — a rabbit’s quiet throne.

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add auto-wildcard detection flag' accurately summarizes the main change, which introduces the --auto-wildcard and --auto-wildcard-tests CLI flags for automatic wildcard detection.
Linked Issues check ✅ Passed The pull request fully implements all coding requirements from issue #924: automatic wildcard detection across multiple domains, automatic filtering of wildcard results, and new CLI flags (--auto-wildcard and --auto-wildcard-tests).
Out of Scope Changes check ✅ Passed All changes directly support auto-wildcard detection: AutoWildcardDetector implementation, Options struct extensions, Runner integration, and golang.org/x/net dependency addition for publicsuffix support.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@internal/runner/autowildcard.go`:
- Around line 168-188: getParentDomains currently includes public suffixes like
"co.uk" as parents; change it to compute the registrable domain using
golang.org/x/net/publicsuffix.EffectiveTLDPlusOne and stop generating parents at
that boundary so you never return the public suffix itself. Update
getParentDomains to call publicsuffix.EffectiveTLDPlusOne(host) (handle and log
the error case) to get the base registrable domain, derive parent labels only up
to that registrable domain, and import the publicsuffix package; ensure any
error falls back to the current behavior but avoids appending pure public
suffixes like "co.uk".
🧹 Nitpick comments (2)
internal/runner/autowildcard.go (1)

86-113: Consider simplifying the IP collection logic.

The ipCounts map and count >= 1 check are redundant. Since any IP that resolves for a randomly-generated subdomain indicates a wildcard, you could simplify to directly add IPs to wildcardIPs.

♻️ Simplified implementation
 func (d *AutoWildcardDetector) testWildcard(parent string) map[string]struct{} {
 	wildcardIPs := make(map[string]struct{})
-	ipCounts := make(map[string]int)
 
 	// Query multiple random subdomains
 	for i := 0; i < d.testCount; i++ {
 		randomHost := xid.New().String() + "." + parent
 		result, err := d.dnsx.QueryOne(randomHost)
 		if err != nil || result == nil {
 			continue
 		}
 
 		for _, ip := range result.A {
-			ipCounts[ip]++
+			wildcardIPs[ip] = struct{}{}
 		}
 	}
 
-	// An IP is considered a wildcard if it appears in at least one random subdomain query
-	// (if a random subdomain resolves, it indicates wildcard)
-	for ip, count := range ipCounts {
-		if count >= 1 {
-			wildcardIPs[ip] = struct{}{}
-		}
-	}
-
 	return wildcardIPs
 }
internal/runner/runner.go (1)

756-763: Consider also checking AAAA records for wildcard detection.

Currently, wildcard detection only uses A records (dnsData.A). If a wildcard domain is configured with only AAAA records (IPv6), it won't be detected or filtered. Consider extending the detection to include AAAA records if the --aaaa flag is enabled or if the user expects IPv6 wildcard detection.

♻️ Example extension to include AAAA records
 		// auto wildcard detection and filtering
 		if r.options.AutoWildcard && r.autoWildcardDetector != nil {
-			if r.autoWildcardDetector.DetectAndFilter(domain, dnsData.A) {
+			// Combine A and AAAA records for wildcard detection
+			allIPs := append([]string{}, dnsData.A...)
+			allIPs = append(allIPs, dnsData.AAAA...)
+			if r.autoWildcardDetector.DetectAndFilter(domain, allIPs) {
 				r.autoWildcardDetector.IncrementFilteredCount()
 				gologger.Debug().Msgf("Filtered wildcard subdomain: %s\n", domain)
 				continue
 			}
 		}

Note: The testWildcard method in autowildcard.go would also need to collect AAAA records from DNS responses for this to work fully.

- Use publicsuffix.EffectiveTLDPlusOne to stop at registrable domain
  (prevents treating public suffixes like 'co.uk' as wildcard parents)
- Simplify testWildcard(): remove redundant ipCounts map, add IPs directly
- Add AAAA record support for IPv6 wildcard detection
@erdogan98
Copy link
Author

Thanks for the review feedback! I've addressed all the points:

  1. CRITICAL fix - publicsuffix handling: Now using golang.org/x/net/publicsuffix.EffectiveTLDPlusOne() to determine the registrable domain boundary. getParentDomains() will no longer return public suffixes like co.uk as parents - it stops at the eTLD+1 level.

  2. Simplified IP collection: Removed the redundant ipCounts map in testWildcard(). IPs are now added directly to the wildcardIPs set since any IP from a random subdomain resolution indicates wildcard behavior.

  3. Added AAAA record support: Also collecting IPv6 addresses from AAAA records for more complete wildcard detection.

Ready for another review!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@internal/runner/autowildcard.go`:
- Around line 87-110: The testWildcard function currently unions IPs from each
successful random-subdomain probe, causing false positives; change it to collect
the IP set for each successful probe (using d.dnsx.QueryOne in
AutoWildcardDetector), track only successful probes, compute the intersection of
IP sets across probes, and only declare wildcard IPs if there are at least two
successful probes and the intersection is non-empty; return that intersection
map[string]struct{} instead of the current union and keep using d.testCount to
drive probe attempts.
- Around line 57-85: ensureWildcardTested currently sets testedDomains before
calling testWildcard which allows other goroutines to skip testing and read
incomplete wildcardRoots; change the flow to track in-flight tests: add an
in-flight map (e.g., inflight map[string]chan struct{} or similar) guarded by
d.mutex, so when a goroutine wants to test parent it first checks testedDomains,
then checks inflight—if an inflight channel exists it waits for it to close and
returns (or reads the completed wildcardRoots), otherwise it creates and stores
a new channel in inflight, releases the lock, runs d.testWildcard(parent), then
re-acquires the lock to set d.testedDomains[parent], set d.wildcardRoots[parent]
if any, close the inflight channel and delete the entry; update
ensureWildcardTested to use these symbols (testedDomains, inflight,
wildcardRoots, testWildcard, d.mutex) and handle locking/unlocking around waits
and updates.

…se positives

1. Fix race condition in ensureWildcardTested:
   - Added inFlight map to track domains currently being tested
   - Concurrent goroutines now wait for in-flight tests to complete
   - testedDomains is now set AFTER testWildcard completes
   - Prevents race where wildcardRoots isn't populated yet

2. Fix false positives in testWildcard:
   - Changed from union-based to intersection-based IP collection
   - Track IP sets per successful probe separately
   - Require at least 2 successful probes before declaring wildcard
   - Only IPs consistent across ALL probes are considered wildcard IPs
   - Added intersectIPSets helper function

These changes ensure:
- Thread-safe wildcard detection with proper synchronization
- Reduced false positives by requiring consistent DNS responses
@erdogan98
Copy link
Author

CodeRabbit Feedback Addressed ✅

I've pushed commit f06d2ed to address the two major issues identified:

1. Concurrency Bug Fix (ensureWildcardTested)

Problem: testedDomains was set BEFORE testWildcard completed, causing a race condition where concurrent goroutines could see the domain as "tested" but wildcardRoots wasn't populated yet.

Solution:

  • Added inFlight map[string]chan struct{} to track domains currently being tested
  • Concurrent goroutines now wait on the channel until testing completes
  • testedDomains is now set AFTER testWildcard completes
  • Channel is closed to signal all waiting goroutines

2. False Positives Fix (testWildcard)

Problem: Union-based IP collection was wrong - if probes returned different IPs, all were considered wildcards.

Solution:

  • Track IP sets per successful probe separately
  • Use intersection instead of union - only IPs present in ALL probes are wildcards
  • Require at least 2 successful probes before declaring wildcard
  • Added intersectIPSets helper function

These changes ensure thread-safe wildcard detection with proper synchronization and significantly reduce false positives by requiring consistent DNS responses across multiple probes.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@internal/runner/autowildcard.go`:
- Around line 23-35: NewAutoWildcardDetector currently clamps testCount to 3
only when <1, but testWildcard requires at least 2 probes so passing 1 silently
disables detection; update the clamping in NewAutoWildcardDetector to ensure
testCount is at least 2 (not 1) — i.e. if testCount < 2 set testCount = 2 — and
keep references to dnsx, testCount, wildcardRoots, testedDomains, and inFlight
initialization unchanged so testWildcard receives a usable probe count.

Comment on lines +23 to +35
// NewAutoWildcardDetector creates a new auto wildcard detector
func NewAutoWildcardDetector(dnsxClient *dnsx.DNSX, testCount int) *AutoWildcardDetector {
if testCount < 1 {
testCount = 3
}
return &AutoWildcardDetector{
dnsx: dnsxClient,
testCount: testCount,
wildcardRoots: make(map[string]map[string]struct{}),
testedDomains: make(map[string]struct{}),
inFlight: make(map[string]chan struct{}),
}
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clamp testCount to ≥2 to avoid a silent no‑op.

testWildcard requires at least two successful probes, so a value of 1 can never succeed and effectively disables detection.

🛠️ Proposed fix
-	if testCount < 1 {
+	if testCount < 2 {
 		testCount = 3
 	}
🤖 Prompt for AI Agents
In `@internal/runner/autowildcard.go` around lines 23 - 35,
NewAutoWildcardDetector currently clamps testCount to 3 only when <1, but
testWildcard requires at least 2 probes so passing 1 silently disables
detection; update the clamping in NewAutoWildcardDetector to ensure testCount is
at least 2 (not 1) — i.e. if testCount < 2 set testCount = 2 — and keep
references to dnsx, testCount, wildcardRoots, testedDomains, and inFlight
initialization unchanged so testWildcard receives a usable probe count.

@dogancanbakir dogancanbakir self-requested a review February 3, 2026 15:52
@Ice3man543
Copy link
Member

I think we should try to extend or import the logic from https://github.com/projectdiscovery/shuffledns/tree/dev/pkg/wildcards which already has a fairly stable wildcard implementation rather than re-implementing it? cc @dogancanbakir

@tarunKoyalwar tarunKoyalwar removed their request for review February 17, 2026 14:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support auto wildcard detection similar to PureDNS

2 participants

Comments