Support auto wildcard detection similar to PureDNS#958
Support auto wildcard detection similar to PureDNS#958addidea wants to merge 1 commit intoprojectdiscovery:devfrom
Conversation
Neo - PR Security ReviewNo security issues found Highlights
Hardening Notes
Comment |
WalkthroughIntroduces a new CLI tool with auto-wildcard detection capability. The main function parses a Changes
Sequence Diagram(s)sequenceDiagram
participant User as User/CLI
participant Main as Main Function
participant CheckWildcard as checkWildcard()
participant DNS as DNS Resolver<br/>(dnsx.Query)
User->>Main: dnsx -auto-wildcard domain.com
Main->>Main: Parse -auto-wildcard flag
Main->>CheckWildcard: checkWildcard("domain.com")
loop Probe Subdomains
CheckWildcard->>DNS: Query www.domain.com
DNS-->>CheckWildcard: Response (A/AAAA or NXDOMAIN)
CheckWildcard->>DNS: Query ftp.domain.com
DNS-->>CheckWildcard: Response
Note over CheckWildcard: ... probe mail, api, test ...
end
CheckWildcard->>CheckWildcard: If any non-NXDOMAIN → wildcard=true
CheckWildcard-->>Main: Return wildcard status
alt Wildcard Detected & Flag Enabled
Main->>User: Print info message & skip domain
else No Wildcard or Flag Disabled
Main->>User: Print "Checking domain" message
end
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@cmd/dnsx/main.go`:
- Around line 4-9: The import path for the dnsx package is incorrect; update the
import statement in the import block to use the correct module path
"github.com/projectdiscovery/dnsx/libs/dnsx" so references to the dnsx package
(e.g., usages of package symbol dnsx in main.go) resolve to the internal
library; ensure the import alias remains dnsx and run `go build` to verify there
are no unresolved references.
- Around line 26-38: The main function currently assumes a module-level
dnsx.Query; replace that with a dnsx client: instantiate a client via
dnsx.New(...) before looping over domains, then call
client.QueryOne()/client.QueryMultiple() (or the appropriate client method) and
handle the returned (result, error) values; also add a guard after domains :=
flag.Args() to return a logged error if domains is empty/nil, and ensure
checkWildcard(domain) errors are handled if it can return an error. Update
references in the loop to use the new client instance and propagate/log DNS
errors instead of ignoring them.
- Around line 12-24: The checkWildcard function is using a non-existent
dnsx.Query and treating its return as a string; replace this by reusing or
adapting the existing IsWildcard logic from internal/runner/wildcard.go:
create/obtain a *dnsx.DNSX instance and call its QueryOne/QueryMultiple methods
(they return (*retryabledns.DNSData, error)), handle the error, extract
A-records from the returned *retryabledns.DNSData and compare results (or use
the random-subdomain + A-record comparison used in IsWildcard) instead of
probing fixed subdomains; update checkWildcard to return false on query errors
and to perform proper DNS data comparison per IsWildcard.
In `@test/wildcard_test.go`:
- Around line 9-26: Test is non-deterministic and failing to compile because it
calls checkWildcard which does real DNS queries and is inaccessible; refactor
checkWildcard to accept a DNS client interface (e.g., type DNSQuerier with
QueryOne(hostname string) (*retryabledns.DNSData, error)) or make it
exported/accessible, update function signature checkWildcard(client DNSQuerier,
domain string) and adjust callers, then rewrite TestCheckWildcard to use a
mockDNSClient implementing DNSQuerier (with a responses map and QueryOne
returning preset DNSData or NXDOMAIN) and assert deterministic true/false
outcomes instead of querying real domains.
- Around line 1-6: The test cannot access the unexported function checkWildcard
because it's in a different package; either move the test into the same package
as checkWildcard (so the test file lives alongside the implementation and can
call checkWildcard directly) or make checkWildcard exported (rename to
CheckWildcard) and refactor it into a testable package (e.g., an internal
package) so the test can import it; also remove the unused
github.com/projectdiscovery/dnsx import from the test file.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a8da7c19-dbee-4203-ab7f-cdececf5ad3f
📒 Files selected for processing (2)
cmd/dnsx/main.gotest/wildcard_test.go
| import ( | ||
| "flag" | ||
| "fmt" | ||
| "strings" | ||
| "github.com/projectdiscovery/dnsx" // Assuming this package handles DNS queries | ||
| ) |
There was a problem hiding this comment.
Incorrect import path for dnsx package.
The import "github.com/projectdiscovery/dnsx" does not match the internal library structure. Based on the codebase, the dnsx library is located at libs/dnsx and should be imported as "github.com/projectdiscovery/dnsx/libs/dnsx".
Suggested fix
import (
"flag"
"fmt"
"strings"
- "github.com/projectdiscovery/dnsx" // Assuming this package handles DNS queries
+ "github.com/projectdiscovery/dnsx/libs/dnsx"
)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dnsx/main.go` around lines 4 - 9, The import path for the dnsx package is
incorrect; update the import statement in the import block to use the correct
module path "github.com/projectdiscovery/dnsx/libs/dnsx" so references to the
dnsx package (e.g., usages of package symbol dnsx in main.go) resolve to the
internal library; ensure the import alias remains dnsx and run `go build` to
verify there are no unresolved references.
| func checkWildcard(domain string) bool { | ||
| // Common subdomains to check for wildcard | ||
| subdomains := []string{"www", "ftp", "mail", "api", "test"} | ||
|
|
||
| for _, subdomain := range subdomains { | ||
| query := subdomain + "." + domain | ||
| result := dnsx.Query(query) // Assuming dnsx.Query performs DNS queries | ||
| if strings.Contains(result, "NXDOMAIN") { | ||
| return false | ||
| } | ||
| } | ||
| return true | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the dnsx package API to confirm available methods
echo "=== Checking dnsx package exports ==="
ast-grep --pattern 'func ($CLIENT *DNSX) Query$_($$$) ($$$) {
$$$
}'
echo ""
echo "=== Checking existing wildcard implementation ==="
cat internal/runner/wildcard.goRepository: projectdiscovery/dnsx
Length of output: 1873
🏁 Script executed:
#!/bin/bash
# Search for dnsx methods and functions more broadly
echo "=== Searching for dnsx.Query usage in codebase ==="
rg "dnsx\.Query" --type go -A 2 -B 2
echo ""
echo "=== Searching for dnsx method definitions ==="
rg "func \(.*\*DNSX\) Query" --type go -A 5
echo ""
echo "=== Finding dnsx package location and structure ==="
fd -e go -path "*dnsx*" | head -20
echo ""
echo "=== Check if dnsx.Query function exists (package-level) ==="
rg "^func Query\(" --type go -B 2 -A 5Repository: projectdiscovery/dnsx
Length of output: 2456
🏁 Script executed:
#!/bin/bash
# Verify the exact content of the problematic function in cmd/dnsx/main.go
echo "=== Content of cmd/dnsx/main.go around the checkWildcard function ==="
head -30 cmd/dnsx/main.goRepository: projectdiscovery/dnsx
Length of output: 927
dnsx.Query() does not exist and return type is incorrect — code will not compile.
Three critical issues prevent compilation and function:
-
Non-existent API:
dnsx.Query(query)is not a valid package-level function. The dnsx package exposesQueryOne()andQueryMultiple()as methods on a*DNSXinstance only. -
Type mismatch: These methods return
(*retryabledns.DNSData, error), not a string. You cannot callstrings.Contains()on this type. -
Logic differs from existing implementation: The existing
IsWildcard()method ininternal/runner/wildcard.gouses random subdomain queries and A-record comparison, which is more reliable than probing known subdomains. The hardcoded subdomain approach can produce false positives for domains that legitimately host www/ftp/mail/api/test subdomains.
Reuse the existing IsWildcard() implementation or adapt it for this context.
Corrected approach (requires further adaptation)
-func checkWildcard(domain string) bool {
- subdomains := []string{"www", "ftp", "mail", "api", "test"}
-
- for _, subdomain := range subdomains {
- query := subdomain + "." + domain
- result := dnsx.Query(query)
- if strings.Contains(result, "NXDOMAIN") {
- return false
- }
- }
- return true
-}
+func checkWildcard(dnsxClient *dnsx.DNSX, domain string) bool {
+ randomSub := xid.New().String() + "." + domain
+ result, err := dnsxClient.QueryOne(randomSub)
+ if err != nil || result == nil {
+ return false
+ }
+ return len(result.A) > 0
+}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dnsx/main.go` around lines 12 - 24, The checkWildcard function is using a
non-existent dnsx.Query and treating its return as a string; replace this by
reusing or adapting the existing IsWildcard logic from
internal/runner/wildcard.go: create/obtain a *dnsx.DNSX instance and call its
QueryOne/QueryMultiple methods (they return (*retryabledns.DNSData, error)),
handle the error, extract A-records from the returned *retryabledns.DNSData and
compare results (or use the random-subdomain + A-record comparison used in
IsWildcard) instead of probing fixed subdomains; update checkWildcard to return
false on query errors and to perform proper DNS data comparison per IsWildcard.
| func main() { | ||
| autoWildcard := flag.Bool("auto-wildcard", false, "Enable automatic wildcard detection") | ||
| flag.Parse() | ||
|
|
||
| domains := flag.Args() | ||
| for _, domain := range domains { | ||
| if *autoWildcard && checkWildcard(domain) { | ||
| fmt.Printf("[INFO] Wildcard detected for domain: %s\n", domain) | ||
| continue // Skip the wildcard domain | ||
| } | ||
| fmt.Printf("Checking domain: %s\n", domain) | ||
| } | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check how other commands in this repo initialize the dnsx client
ast-grep --pattern 'dnsx.New($$$)'Repository: projectdiscovery/dnsx
Length of output: 47
🏁 Script executed:
cat -n cmd/dnsx/main.go | head -60Repository: projectdiscovery/dnsx
Length of output: 1407
🏁 Script executed:
fd -t f -name "*.go" cmd/ | head -20Repository: projectdiscovery/dnsx
Length of output: 295
🏁 Script executed:
rg "dnsx\." --type go -A 3 -B 1 | head -100Repository: projectdiscovery/dnsx
Length of output: 5154
🏁 Script executed:
rg "func.*Query" libs/dnsx/ --type go -B 2 -A 5Repository: projectdiscovery/dnsx
Length of output: 931
🏁 Script executed:
cat -n cmd/dnsx/main.goRepository: projectdiscovery/dnsx
Length of output: 1407
🏁 Script executed:
rg "^func Query" libs/dnsx/ --type goRepository: projectdiscovery/dnsx
Length of output: 47
🏁 Script executed:
rg "dnsx\.Query\(" . --type goRepository: projectdiscovery/dnsx
Length of output: 164
DNSX client must be initialized and proper query methods used.
The code calls the non-existent dnsx.Query() function. The dnsx package requires:
- Creating a DNSX client instance via
dnsx.New()with options - Calling query methods on that client (e.g.,
QueryMultiple(),QueryOne()), not module-level functions - Handling the returned error from DNS operations
Additionally, add error handling for empty domain arguments (when flag.Args() returns nil/empty slice).
The -auto-wildcard flag format is correct per Go conventions.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@cmd/dnsx/main.go` around lines 26 - 38, The main function currently assumes a
module-level dnsx.Query; replace that with a dnsx client: instantiate a client
via dnsx.New(...) before looping over domains, then call
client.QueryOne()/client.QueryMultiple() (or the appropriate client method) and
handle the returned (result, error) values; also add a guard after domains :=
flag.Args() to return a logged error if domains is empty/nil, and ensure
checkWildcard(domain) errors are handled if it can return an error. Update
references in the loop to use the new client instance and propagate/log DNS
errors instead of ignoring them.
| package main | ||
|
|
||
| import ( | ||
| "testing" | ||
| "github.com/projectdiscovery/dnsx" | ||
| ) |
There was a problem hiding this comment.
Critical: Test file cannot access checkWildcard — package structure is incorrect.
In Go, packages are determined by directory, not by package declaration alone. This test file in test/wildcard_test.go is a separate package from cmd/dnsx/main.go, even though both declare package main. The unexported function checkWildcard is inaccessible from here.
Additionally:
- The
dnsximport is unused and should be removed. - CI runs
go test ./...which won't compile this test together withcmd/dnsx/main.go.
To fix, either:
- Move the test to
cmd/dnsx/main_test.go(same directory as the function), or - Export the function and refactor into a testable package (e.g.,
internal/wildcard).
Option 1: Move test to cmd/dnsx/
-// File: test/wildcard_test.go
+// File: cmd/dnsx/wildcard_test.go
package main
import (
"testing"
- "github.com/projectdiscovery/dnsx"
)📝 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.
| package main | |
| import ( | |
| "testing" | |
| "github.com/projectdiscovery/dnsx" | |
| ) | |
| package main | |
| import ( | |
| "testing" | |
| ) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/wildcard_test.go` around lines 1 - 6, The test cannot access the
unexported function checkWildcard because it's in a different package; either
move the test into the same package as checkWildcard (so the test file lives
alongside the implementation and can call checkWildcard directly) or make
checkWildcard exported (rename to CheckWildcard) and refactor it into a testable
package (e.g., an internal package) so the test can import it; also remove the
unused github.com/projectdiscovery/dnsx import from the test file.
| func TestCheckWildcard(t *testing.T) { | ||
| tests := []struct { | ||
| domain string | ||
| expected bool | ||
| }{ | ||
| {"example.com", true}, | ||
| {"nonwildcard.com", false}, | ||
| } | ||
|
|
||
| for _, tt := range tests { | ||
| t.Run(tt.domain, func(t *testing.T) { | ||
| got := checkWildcard(tt.domain) | ||
| if got != tt.expected { | ||
| t.Errorf("checkWildcard() = %v, want %v", got, tt.expected) | ||
| } | ||
| }) | ||
| } | ||
| } |
There was a problem hiding this comment.
Test design issues: non-deterministic and lacks mocking.
-
Hardcoded expectations for real domains: The test assumes
example.comis a wildcard domain andnonwildcard.comis not. DNS responses for real domains can change, making this test flaky and non-reproducible. -
No mocking: The function (once fixed) would make real DNS queries. Unit tests should mock external dependencies to ensure deterministic results and avoid network dependency.
-
Compilation will fail: As noted above,
checkWildcardis inaccessible from this package location.
Consider refactoring checkWildcard to accept a DNS client interface that can be mocked in tests.
Suggested approach with interface for testability
// Define interface for DNS querying
type DNSQuerier interface {
QueryOne(hostname string) (*retryabledns.DNSData, error)
}
// Refactor checkWildcard to accept interface
func checkWildcard(client DNSQuerier, domain string) bool {
// implementation
}
// In tests, use a mock implementation
type mockDNSClient struct {
responses map[string]*retryabledns.DNSData
}
func (m *mockDNSClient) QueryOne(hostname string) (*retryabledns.DNSData, error) {
if resp, ok := m.responses[hostname]; ok {
return resp, nil
}
return nil, errors.New("NXDOMAIN")
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/wildcard_test.go` around lines 9 - 26, Test is non-deterministic and
failing to compile because it calls checkWildcard which does real DNS queries
and is inaccessible; refactor checkWildcard to accept a DNS client interface
(e.g., type DNSQuerier with QueryOne(hostname string) (*retryabledns.DNSData,
error)) or make it exported/accessible, update function signature
checkWildcard(client DNSQuerier, domain string) and adjust callers, then rewrite
TestCheckWildcard to use a mockDNSClient implementing DNSQuerier (with a
responses map and QueryOne returning preset DNSData or NXDOMAIN) and assert
deterministic true/false outcomes instead of querying real domains.
Closes #924
Changes
--auto-wildcardflag tocmd/dnsx/main.goimplementing automatic wildcard detection similar to PureDNScheckWildcard()function tests common subdomains against the target domain to identify wildcard DNS recordsTesting
test/wildcard_test.gocovering wildcard detection logic