Skip to content

feat: auto wildcard domain detection for multi-domain runs (#924)#949

Open
Jujubee-LLM wants to merge 6 commits intoprojectdiscovery:devfrom
Jujubee-LLM:feat/auto-wildcard-924
Open

feat: auto wildcard domain detection for multi-domain runs (#924)#949
Jujubee-LLM wants to merge 6 commits intoprojectdiscovery:devfrom
Jujubee-LLM:feat/auto-wildcard-924

Conversation

@Jujubee-LLM
Copy link

@Jujubee-LLM Jujubee-LLM commented Feb 24, 2026

Summary

Adds optional per-host wildcard root detection for multi-domain input.

Previously, wildcard checks relied on a single configured --wildcard-domain, which is not ideal when inputs span multiple root domains.

With --auto-wildcard, dnsx derives wildcard root domain per host (eTLD+1) and applies wildcard filtering against that resolved domain.

Closes #924

Key changes

  • add --auto-wildcard (Options.AutoWildcard)
  • resolve wildcard domain per host in runner logic:
    • use explicit --wildcard-domain when provided
    • otherwise derive via publicsuffix.EffectiveTLDPlusOne when --auto-wildcard is enabled
  • pass both host and resolved wildcard domain through wildcard worker pipeline (wildcardTask)
  • update wildcard API: IsWildcard(host) -> IsWildcard(host, wildcardDomain)
  • keep wildcard filtering usable with --stream by falling back to buffered execution path when wildcard flags are enabled
  • normalize host/domain comparison and skip wildcard checks for apex host consistently

Tests

Added/updated tests for:

  • explicit wildcard-domain path
  • auto-detection path (including trailing dot/whitespace normalization)
  • IP/invalid/host:port handling
  • disabled auto-wildcard behavior
  • apex host matching behavior

Notes

No output format changes. Existing non-wildcard stream behavior remains unchanged.

@neo-by-projectdiscovery-dev
Copy link

neo-by-projectdiscovery-dev bot commented Feb 24, 2026

Neo - PR Security Review

No security issues found

Highlights

  • Adds --auto-wildcard flag for per-host wildcard domain resolution
  • Implements getWildcardDomainForHost using publicsuffix.EffectiveTLDPlusOne for safe domain parsing
  • Updates wildcard detection to accept both host and derived wildcard domain parameters
  • Adds input validation for ports and empty strings in domain resolution
Hardening Notes
  • Consider adding rate limiting specifically for wildcard detection queries in getWildcardDomainForHost at runner.go:526-537 to prevent excessive DNS queries when processing large domain lists with high wildcard thresholds
  • Add debug logging for publicsuffix.EffectiveTLDPlusOne errors in getWildcardDomainForHost at runner.go:600-602 to help users understand why domain parsing failed for troubleshooting

Comment @neo help for available commands. · Open in Neo

@coderabbitai
Copy link

coderabbitai bot commented Feb 24, 2026

Walkthrough

Adds an optional AutoWildcard mode and wiring to auto-detect and handle wildcard root domains per host. Introduces Options.AutoWildcard, --auto-wildcard flag, wildcardTask carrying host+domain, host→wildcardDomain mappings, and changes IsWildcard to accept an explicit wildcard domain.

Changes

Cohort / File(s) Summary
Options & CLI
internal/runner/options.go
Added AutoWildcard bool to Options, registered --auto-wildcard CLI flag, and adjusted stream-mode validation to allow wildcard handling via buffered path.
Runner core & tasking
internal/runner/runner.go
Added wildcardTask type, changed wildcard worker channel to chan wildcardTask, added normalizeHostname, isWildcardApexHost, and getWildcardDomainForHost(host); propagate domain from pre-resolution into worker, filtering, and output; treat AutoWildcard as enabling A queries when no QTypes set.
Wildcard detection
internal/runner/wildcard.go
Changed signature IsWildcard(host string) boolIsWildcard(host, wildcardDomain string) bool; updated internal host-generation and checks to use supplied wildcardDomain parameter.
Tests
internal/runner/runner_test.go
Added tests for getWildcardDomainForHost and isWildcardApexHost covering explicit wildcard, AutoWildcard-derived eTLD+1, IP/port rejection, trimming, and apex comparisons.
Misc / Manifest
go.mod
Minor test/module manifest updates referenced by the diff.

Sequence Diagram(s)

sequenceDiagram
  participant Runner as Runner
  participant TaskQ as WildcardWorker
  participant DNS as DNSResolver
  participant Results as ResultProcessor

  Runner->>Runner: enumerate hosts
  Runner->>Runner: getWildcardDomainForHost(host) / normalizeHostname
  Runner->>TaskQ: send wildcardTask(host, wildcardDomain)
  TaskQ->>DNS: query host (A/other qtypes as configured)
  DNS-->>TaskQ: response (records/wildcard indicators)
  TaskQ-->>Runner: report wildcard detection for host
  Runner->>Results: apply host->wildcardDomain map, filter/mark apex wildcards
  Results-->>Runner: output filtered results
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐇
I hop from host to host with care,
sniffing wildcards hiding there.
One tiny task, a domain in tow,
I mark and skip the noisy show.
Hooray for tidy DNS flow! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% 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 accurately summarizes the main change: adding auto wildcard detection support via a new flag, directly addressing issue #924.
Linked Issues check ✅ Passed All objectives from issue #924 are met: auto-detection across multiple domains without per-domain flags, automatic filtering of wildcard results, and a simple --auto-wildcard flag with preserved default behavior.
Out of Scope Changes check ✅ Passed All changes are directly related to implementing auto wildcard detection. No unrelated modifications to other features or systems were introduced.

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

✨ Finishing Touches
🧪 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.

🧹 Nitpick comments (2)
internal/runner/runner.go (2)

517-524: Consider adding debug logging when hosts are skipped from wildcard processing.

When getWildcardDomainForHost returns false (e.g., for IP addresses or hosts with ports), those hosts silently bypass wildcard filtering and pass through in the output loop. While this is correct behavior, adding a debug log would aid troubleshooting when users encounter unexpected pass-through hosts.

📝 Suggested logging
 					wildcardDomain, ok := r.getWildcardDomainForHost(host)
 					if !ok {
+						gologger.Debug().Msgf("Skipping wildcard check for host (no valid domain): %s\n", host)
 						continue
 					}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 517 - 524, Add a debug log when
getWildcardDomainForHost(host) returns false so skipped hosts are visible;
specifically, in the loop around getWildcardDomainForHost (the code that
populates hostToWildcardDomain, seen, and sends wildcardTask on
r.wildcardworkerchan), emit a debug-level message indicating the host was
skipped and the reason (e.g., "no wildcard domain" or "IP/contains port") before
continue so operators can trace why a host was not queued for wildcard
processing.

35-38: Consider adding a doc comment for the new type.

The wildcardTask struct is introduced as an internal pipeline message. A brief comment explaining its purpose would improve readability, especially clarifying that domain is the derived wildcard root (eTLD+1 or explicit).

📝 Suggested documentation
+// wildcardTask carries a host and its associated wildcard domain
+// through the wildcard detection pipeline.
 type wildcardTask struct {
 	host   string
 	domain string
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 35 - 38, Add a brief doc comment
immediately above the wildcardTask type declaration explaining that wildcardTask
is an internal pipeline message carrying the originating host (host) and the
derived wildcard root (domain — eTLD+1 or explicit), describing its purpose and
when/why domain is derived; place the comment directly above the type
declaration for wildcardTask to improve readability.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@internal/runner/runner.go`:
- Around line 517-524: Add a debug log when getWildcardDomainForHost(host)
returns false so skipped hosts are visible; specifically, in the loop around
getWildcardDomainForHost (the code that populates hostToWildcardDomain, seen,
and sends wildcardTask on r.wildcardworkerchan), emit a debug-level message
indicating the host was skipped and the reason (e.g., "no wildcard domain" or
"IP/contains port") before continue so operators can trace why a host was not
queued for wildcard processing.
- Around line 35-38: Add a brief doc comment immediately above the wildcardTask
type declaration explaining that wildcardTask is an internal pipeline message
carrying the originating host (host) and the derived wildcard root (domain —
eTLD+1 or explicit), describing its purpose and when/why domain is derived;
place the comment directly above the type declaration for wildcardTask to
improve readability.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05b82ba and cc8b7ed.

📒 Files selected for processing (1)
  • internal/runner/runner.go

@Jujubee-LLM Jujubee-LLM changed the title feat(wildcard): add auto wildcard detection mode for multi-domain runs (#924) feat: Support auto wildcard detection similar to PureDNS (#924) Feb 28, 2026
@Jujubee-LLM Jujubee-LLM changed the title feat: Support auto wildcard detection similar to PureDNS (#924) feat: auto wildcard domain detection for multi-domain runs (#924) Mar 3, 2026
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

🧹 Nitpick comments (1)
internal/runner/runner.go (1)

129-132: Avoid duplicate A queries when auto-wildcard is enabled.

Line 131 appends dns.TypeA unconditionally. If A is already selected (e.g., -a or --recon), this duplicates the same query type and adds unnecessary DNS traffic.

🔧 Proposed refactor
 	// If no option is specified or wildcard filter has been requested use query type A
 	if len(questionTypes) == 0 || options.WildcardDomain != "" || options.AutoWildcard {
 		options.A = true
-		questionTypes = append(questionTypes, dns.TypeA)
+		if !sliceutil.Contains(questionTypes, dns.TypeA) {
+			questionTypes = append(questionTypes, dns.TypeA)
+		}
 	}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 129 - 132, The code unconditionally
appends dns.TypeA which can duplicate A queries; modify the block around
questionTypes and options (the variables questionTypes, options.A,
options.WildcardDomain, options.AutoWildcard) so that after setting options.A =
true you only append dns.TypeA if it is not already present in questionTypes
(e.g., check for existence using a small loop or helper like
containsType(questionTypes, dns.TypeA) before append). Ensure dns.TypeA is added
exactly once to avoid duplicate DNS traffic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/runner/runner.go`:
- Around line 60-62: The hostname normalization is currently not lowercasing,
causing case-sensitive apex comparisons to fail; update normalizeHostname to
return strings.ToLower(strings.TrimSuffix(strings.TrimSpace(host), ".")) and
ensure any apex comparisons use normalizeHostname(...) for both sides (where
apex/host are compared) so DNS labels are compared case-insensitively.
- Around line 451-454: When falling back for wildcard filtering in the block
that checks r.options.WildcardDomain or r.options.AutoWildcard, disable stream
mode on the options before invoking r.run() so prepareInput() consumes stdin
once and startWorkers() picks the buffered worker path instead of
InputWorkerStream(); in other words, set r.options.Stream = false (or call the
buffered/run variant) prior to calling r.run() to avoid re-reading exhausted
stdin by InputWorkerStream(), referencing r.run(), prepareInput(),
startWorkers(), InputWorkerStream(), and r.options.Stream.

---

Nitpick comments:
In `@internal/runner/runner.go`:
- Around line 129-132: The code unconditionally appends dns.TypeA which can
duplicate A queries; modify the block around questionTypes and options (the
variables questionTypes, options.A, options.WildcardDomain,
options.AutoWildcard) so that after setting options.A = true you only append
dns.TypeA if it is not already present in questionTypes (e.g., check for
existence using a small loop or helper like containsType(questionTypes,
dns.TypeA) before append). Ensure dns.TypeA is added exactly once to avoid
duplicate DNS traffic.

ℹ️ Review info

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1674779 and ffd7b98.

📒 Files selected for processing (3)
  • internal/runner/options.go
  • internal/runner/runner.go
  • internal/runner/runner_test.go

Comment on lines +60 to +62
func normalizeHostname(host string) string {
return strings.TrimSuffix(strings.TrimSpace(host), ".")
}
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

Normalize host/domain case before apex comparison.

Line 608 currently compares case-sensitively. DNS labels are case-insensitive, so Example.COM vs example.com should still match.

🔧 Proposed fix
 func normalizeHostname(host string) string {
-	return strings.TrimSuffix(strings.TrimSpace(host), ".")
+	return strings.TrimSuffix(strings.ToLower(strings.TrimSpace(host)), ".")
 }

Also applies to: 607-609

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 60 - 62, The hostname normalization
is currently not lowercasing, causing case-sensitive apex comparisons to fail;
update normalizeHostname to return
strings.ToLower(strings.TrimSuffix(strings.TrimSpace(host), ".")) and ensure any
apex comparisons use normalizeHostname(...) for both sides (where apex/host are
compared) so DNS labels are compared case-insensitively.

Comment on lines +451 to +454
if r.options.WildcardDomain != "" || r.options.AutoWildcard {
gologger.Warning().Msgf("Wildcard filtering enabled in stream mode: falling back to buffered execution")
return r.run()
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Stream fallback currently breaks stdin-backed wildcard runs.

At Line 453, r.run() is called while options.Stream remains true. run() calls prepareInput() (which consumes stdin), but startWorkers() then still picks InputWorkerStream(), which re-reads exhausted stdin. Result: zero processed tasks in stdin stream+wildcard mode.

🔧 Proposed fix
 func (r *Runner) Run() error {
 	if r.options.Stream {
 		if r.options.WildcardDomain != "" || r.options.AutoWildcard {
 			gologger.Warning().Msgf("Wildcard filtering enabled in stream mode: falling back to buffered execution")
+			r.options.Stream = false
 			return r.run()
 		}
 		return r.runStream()
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if r.options.WildcardDomain != "" || r.options.AutoWildcard {
gologger.Warning().Msgf("Wildcard filtering enabled in stream mode: falling back to buffered execution")
return r.run()
}
func (r *Runner) Run() error {
if r.options.Stream {
if r.options.WildcardDomain != "" || r.options.AutoWildcard {
gologger.Warning().Msgf("Wildcard filtering enabled in stream mode: falling back to buffered execution")
r.options.Stream = false
return r.run()
}
return r.runStream()
}
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/runner/runner.go` around lines 451 - 454, When falling back for
wildcard filtering in the block that checks r.options.WildcardDomain or
r.options.AutoWildcard, disable stream mode on the options before invoking
r.run() so prepareInput() consumes stdin once and startWorkers() picks the
buffered worker path instead of InputWorkerStream(); in other words, set
r.options.Stream = false (or call the buffered/run variant) prior to calling
r.run() to avoid re-reading exhausted stdin by InputWorkerStream(), referencing
r.run(), prepareInput(), startWorkers(), InputWorkerStream(), and
r.options.Stream.

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

1 participant