Skip to content

Conversation

@matejsmycka
Copy link
Contributor

@matejsmycka matejsmycka commented Oct 29, 2025

Proposed changes

This PR implements IP in the DNS findings. I see reasons why this was not implemented. There is no easy, perfect solution. However, I would go for a "best effort" solution, see changes.

If you have any recommendations that I should implement, please say so.

Checklist

  • Pull request is created against the dev branch
  • All checks passed (lint, unit/integration/regression tests etc.) with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)

Summary by CodeRabbit

  • New Features

    • DNS response events now include resolved IP addresses alongside domain information, enabling IP-based matching and richer event output.
    • Added DNS resolution utilities that prefer IPv4 but support IPv6 and handle CNAME resolution.
  • Tests

    • Expanded test coverage for DNS operators and resolver utilities to validate IP handling, resolution outcomes, and error scenarios.

@auto-assign auto-assign bot requested a review from Mzack9999 October 29, 2025 15:33
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 29, 2025

Walkthrough

The DNS protocol now resolves and propagates IP addresses through request execution, DSL mapping, and result events. A new internal resolver helper was added and tests were updated to pass and assert the propagated IP value.

Changes

Cohort / File(s) Change Summary
DNS Execution & Operator Integration
pkg/protocols/dns/request.go, pkg/protocols/dns/operators.go
Thread IP through execution: request.go resolves or accepts IP and passes it into internal execute calls; responseToDSLMap signature updated to accept ip and inject it into the DSL event map. MakeResultEventItem now reads ip and populates output.ResultEvent.IP.
DNS Resolution Utilities
pkg/protocols/dns/utils.go
New file: adds IPNotFoundError sentinel and tryToResolveHost helper that resolves a domain via retryabledns.Client, preferring A over AAAA and returning the first address or IPNotFoundError.
Tests — Operators
pkg/protocols/dns/operators_test.go
Updated tests to provide the new ip argument (e.g., "1.1.1.1") to responseToDSLMap and adjusted expected event shapes and counts accordingly.
Tests — Resolver
pkg/protocols/dns/utils_test.go
New tests for tryToResolveHost covering A/AAAA resolution, non-existent hosts, invalid domains, and nil resolver panic using a test resolver.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Request Handler
    participant Resolver as tryToResolveHost
    participant Executor as execute()
    participant Operators as responseToDSLMap
    participant DSL as DSL Event Map
    rect rgb(240,248,255)
    Client->>Resolver: ask for IP (domain or input)
    Resolver-->>Client: returns ip (or error)
    end
    rect rgb(245,255,240)
    Client->>Executor: execute(domain, ip, ...)
    Executor->>Operators: responseToDSLMap(req, resp, host, ip, matched)
    Operators->>DSL: inject fields (host, matched, ip, records...)
    DSL-->>Operators: DSL event with ip field
    Operators-->>Executor: return event
    Executor-->>Client: final ResultEvent (includes IP)
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay special attention to pkg/protocols/dns/request.go for correct IP resolution, trimming of trailing dots, and consistent passing of ip across threaded and unthreaded execution paths.
  • Verify all call sites of responseToDSLMap in operators.go and tests were updated to match the new signature.
  • Review tryToResolveHost for error handling, A vs AAAA selection logic, and behavior when resolver is nil.
  • Spot-check updated tests (operators_test.go, utils_test.go) for correctness of expected event shapes and failure cases.

Poem

🐰 I found an IP in tunnels deep,
I carried it along each hop and sweep,
From query bloom to event’s end,
The tiny number now a friend.
Hop, hop—now every result knows where to sleep. 🥕✨

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 17.65% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Feat: Add IP to results in DNS protocol" accurately and directly describes the primary objective of the changeset. The changes across all modified and new files—including IP resolution logic in request.go, IP propagation in the DSL mapping through operators.go, IP inclusion in the result event output, and new DNS resolution utilities—collectively implement exactly what the title claims: adding IP information to DNS protocol results. The title is concise, specific, and clear enough that a teammate reviewing the commit history would immediately understand the main change without needing to inspect the full diff.
✨ 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
Contributor

@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: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
pkg/protocols/dns/request.go (1)

186-193: Use the same dnsClient instance for Trace as for Do()

You build/use a local dnsClient for requests, but Trace calls request.dnsClient. This can diverge or be nil.

-		traceData, err = request.dnsClient.Trace(domain, request.question, request.TraceMaxRecursion)
+		traceData, err = dnsClient.Trace(domain, request.question, request.TraceMaxRecursion)
🧹 Nitpick comments (8)
pkg/protocols/dns/utils.go (2)

20-24: Deterministic selection when multiple A/AAAA are present

Using the “first” element can be nondeterministic across resolvers. Optionally sort before selection to stabilize results (especially useful for tests).

-	if ips.A != nil && len(ips.A) > 0 {
-		return ips.A[0], nil
-	}
+	if n := len(ips.A); n > 0 {
+		sort.Strings(ips.A)
+		return ips.A[0], nil
+	}
-	if ips.AAAA != nil && len(ips.AAAA) > 0 {
-		return ips.AAAA[0], nil
-	}
+	if n := len(ips.AAAA); n > 0 {
+		sort.Strings(ips.AAAA)
+		return ips.AAAA[0], nil
+	}

(Remember to add import "sort".)


8-8: Exported sentinel should have a doc comment

Add a brief GoDoc so it shows up in package docs.

-var IPNotFoundError = errors.New("no A or AAAA record found")
+// IPNotFoundError is returned when neither A nor AAAA records are found.
+var IPNotFoundError = errors.New("no A or AAAA record found")
pkg/protocols/dns/operators.go (1)

84-101: Include “ip” only when available (optional)

To keep events compact, consider setting "ip" only when non‑empty. Current tests expect the key; do this only if you also update tests.

 	ret := output.InternalEvent{
 		"host":          host,
-		"ip":            ip,
+	}
+	if ip != "" {
+		ret["ip"] = ip
+	}
+	for k, v := range map[string]interface{}{
+		"matched":       matched,
+		"request":       req.String(),
+		"rcode":         resp.Rcode,
+		"question":      questionToString(resp.Question),
+		"extra":         rrToString(resp.Extra),
+		"answer":        rrToString(resp.Answer),
+		"ns":            rrToString(resp.Ns),
+		"raw":           resp.String(),
+		"template-id":   request.options.TemplateID,
+		"template-info": request.options.TemplateInfo,
+		"template-path": request.options.TemplatePath,
+		"type":          request.Type().String(),
+		"trace":         traceToString(traceData, false),
+	} {
+		ret[k] = v
 	}
pkg/protocols/dns/operators_test.go (2)

48-51: Strengthen assertions: check “ip” explicitly; avoid brittle length checks

Assert the "ip" field value directly and drop reliance on total map size which changes when event shape evolves.

-	event := request.responseToDSLMap(req, resp, "one.one.one.one", "1.1.1.1", "one.one.one.one", nil)
-	require.Len(t, event, 16, "could not get correct number of items in dsl map")
+	event := request.responseToDSLMap(req, resp, "one.one.one.one", "1.1.1.1", "one.one.one.one", nil)
+	require.Equal(t, "1.1.1.1", event["ip"], "ip must be propagated")

82-92: Add an assertion for “ip” in each scenario

Each test constructs the DSL map with an explicit IP; assert it’s present to guard regressions.

Example:

 event := request.responseToDSLMap(req, resp, "one.one.one.one", "1.1.1.1", "one.one.one.one", nil)
+require.Equal(t, "1.1.1.1", event["ip"])

Apply similarly in other test blocks.

Also applies to: 149-163, 194-205, 266-275

pkg/protocols/dns/utils_tests.go (3)

12-18: Avoid hard dependency on public resolvers in unit tests

Tests hitting 1.1.1.1:53 can flake in CI. Prefer a stub/mocked resolver, an integration-test build tag, or gate by env var.

Example gating:

 func newTestResolver(t *testing.T) *retryabledns.Client {
+	if os.Getenv("NUCLEI_ENABLE_DNS_NET_TESTS") == "" {
+		t.Skip("networked DNS tests disabled (set NUCLEI_ENABLE_DNS_NET_TESTS=1 to enable)")
+	}

(Remember import "os".)


35-46: Use a more stable AAAA target

ipv6.google.com has changed historically. Consider one.one.one.one which has well-known AAAA records.

- ip, err := tryToResolveHost("ipv6.google.com", resolver)
+ ip, err := tryToResolveHost("one.one.one.one", resolver)

67-75: Prefer error return over panic for nil resolver

Panic-based testing couples callers to crash behavior. If you adopt a nil guard in tryToResolveHost, update this test to assert an error instead.

-func TestTryToResolveHost_NilResolver(t *testing.T) {
-	defer func() {
-		if r := recover(); r == nil {
-			t.Fatal("expected panic with nil resolver, but did not panic")
-		}
-	}()
-	_, _ = tryToResolveHost("example.com", nil)
-	require.Fail(t, "expected panic due to nil resolver")
-}
+func TestTryToResolveHost_NilResolver(t *testing.T) {
+	_, err := tryToResolveHost("example.com", nil)
+	require.Error(t, err)
+}
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 3be27b9 and 49b6adc.

📒 Files selected for processing (5)
  • pkg/protocols/dns/operators.go (2 hunks)
  • pkg/protocols/dns/operators_test.go (5 hunks)
  • pkg/protocols/dns/request.go (5 hunks)
  • pkg/protocols/dns/utils.go (1 hunks)
  • pkg/protocols/dns/utils_tests.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/protocols/dns/utils.go
  • pkg/protocols/dns/operators_test.go
  • pkg/protocols/dns/request.go
  • pkg/protocols/dns/utils_tests.go
  • pkg/protocols/dns/operators.go
pkg/protocols/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()

Files:

  • pkg/protocols/dns/utils.go
  • pkg/protocols/dns/operators_test.go
  • pkg/protocols/dns/request.go
  • pkg/protocols/dns/utils_tests.go
  • pkg/protocols/dns/operators.go
🧬 Code graph analysis (3)
pkg/protocols/dns/request.go (4)
pkg/protocols/common/contextargs/metainput.go (1)
  • MetaInput (18-30)
pkg/protocols/dns/dns.go (1)
  • Request (20-99)
pkg/protocols/common/contextargs/contextargs.go (1)
  • Context (22-33)
pkg/output/output.go (1)
  • InternalEvent (93-93)
pkg/protocols/dns/utils_tests.go (1)
pkg/protocols/dns/utils.go (1)
  • IPNotFoundError (8-8)
pkg/protocols/dns/operators.go (2)
pkg/protocols/protocols.go (1)
  • Request (308-333)
pkg/output/output.go (1)
  • InternalEvent (93-93)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Lint
🔇 Additional comments (2)
pkg/protocols/dns/request.go (1)

195-205: responseToDSLMap propagation looks good

IP is now propagated into the event and downstream. No issues spotted here.

pkg/protocols/dns/operators.go (1)

114-131: No issues found—IP field is correctly defined

The ResultEvent struct in pkg/output/output.go (line 187) includes IP string with the JSON tag json:"ip,omitempty". The code in pkg/protocols/dns/operators.go correctly assigns this field.

Comment on lines 12 to 26
func tryToResolveHost(domain string, resolver *retryabledns.Client) (string, error) {
// resolve is recursive so CNAMEs are resolved automatically
ips, err := resolver.Resolve(domain)
if err != nil {
return "", err
}
// It returns first A or AAAA record found in DNS chain
// This means that domains with multiple A records will return the first one only, also ipv4 is preferred over ipv6
if ips.A != nil && len(ips.A) > 0 {
return ips.A[0], nil
}
if ips.AAAA != nil && len(ips.AAAA) > 0 {
return ips.AAAA[0], nil
}
return "", IPNotFoundError
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 29, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Guard against nil resolver to avoid panics in callers

tryToResolveHost assumes resolver is non‑nil. A nil resolver will panic at runtime. Either validate at call site or guard here.

Minimal defensive fix:

 func tryToResolveHost(domain string, resolver *retryabledns.Client) (string, error) {
+	if resolver == nil {
+		return "", errors.New("nil resolver")
+	}
 	// resolve is recursive so CNAMEs are resolved automatically
 	ips, err := resolver.Resolve(domain)
📝 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
func tryToResolveHost(domain string, resolver *retryabledns.Client) (string, error) {
// resolve is recursive so CNAMEs are resolved automatically
ips, err := resolver.Resolve(domain)
if err != nil {
return "", err
}
// It returns first A or AAAA record found in DNS chain
// This means that domains with multiple A records will return the first one only, also ipv4 is preferred over ipv6
if ips.A != nil && len(ips.A) > 0 {
return ips.A[0], nil
}
if ips.AAAA != nil && len(ips.AAAA) > 0 {
return ips.AAAA[0], nil
}
return "", IPNotFoundError
func tryToResolveHost(domain string, resolver *retryabledns.Client) (string, error) {
if resolver == nil {
return "", errors.New("nil resolver")
}
// resolve is recursive so CNAMEs are resolved automatically
ips, err := resolver.Resolve(domain)
if err != nil {
return "", err
}
// It returns first A or AAAA record found in DNS chain
// This means that domains with multiple A records will return the first one only, also ipv4 is preferred over ipv6
if ips.A != nil && len(ips.A) > 0 {
return ips.A[0], nil
}
if ips.AAAA != nil && len(ips.AAAA) > 0 {
return ips.AAAA[0], nil
}
return "", IPNotFoundError
🤖 Prompt for AI Agents
In pkg/protocols/dns/utils.go around lines 12 to 26, guard against a nil
resolver to avoid panics: at the start of tryToResolveHost check if resolver ==
nil and return a clear error (e.g., a new ErrNilResolver or a wrapped error with
context) instead of proceeding; keep the rest of the logic unchanged so callers
receive an explicit error rather than causing a runtime panic.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

should not happen

Copy link
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find all call sites of tryToResolveHost to verify resolver is never nil
rg -n -A5 -B5 'tryToResolveHost\(' --type=go

Length of output: 4095


@matejsmycka You're right! I can see the only production call site in pkg/protocols/dns/request.go (line 63-65) guards with else if request.dnsClient != nil before calling tryToResolveHost, so the resolver will never be nil in practice. Since this is a private function with a single protected call site, the nil check would be redundant. Thanks for the clarification!

Copy link
Contributor

@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: 0

🧹 Nitpick comments (5)
pkg/protocols/dns/utils_test.go (5)

20-32: Simplify by removing redundant error checks.

Lines 24-29 duplicate the validation performed by require assertions on lines 30-31. Using require consistently throughout would be cleaner.

Apply this diff to simplify:

 func TestTryToResolveHost_SuccessARecord(t *testing.T) {
 	resolver := newTestResolver(t)
 
 	ip, err := tryToResolveHost("example.com", resolver)
-	if err != nil {
-		t.Fatalf("expected no error, got %v", err)
-	}
-	if ip == "" {
-		t.Fatal("expected non-empty IP for example.com")
-	}
+	require.NoError(t, err)
 	require.NotEmpty(t, ip)
 	require.Contains(t, ip, ".")
 }

34-46: Simplify by removing redundant error checks.

Same pattern as the previous test—manual checks duplicate the require assertions.

Apply this diff:

 func TestTryToResolveHost_SuccessAAAARecord(t *testing.T) {
 	resolver := newTestResolver(t)
 
 	ip, err := tryToResolveHost("ipv6.google.com", resolver)
-	if err != nil {
-		t.Fatalf("expected no error, got %v", err)
-	}
-	if ip == "" {
-		t.Fatal("expected non-empty IPv6 address for ipv6.google.com")
-	}
+	require.NoError(t, err)
 	require.NotEmpty(t, ip)
 	require.Contains(t, ip, ":")
 }

48-55: Clarify error assertion logic.

The condition on line 52 is confusing. Since the test expects either IPNotFoundError or any DNS error, simply asserting that an error occurred would be clearer.

Apply this diff:

 func TestTryToResolveHost_IPNotFound(t *testing.T) {
 	resolver := newTestResolver(t)
 
 	_, err := tryToResolveHost("nonexistent-subdomain.ef37979f-9fff-43f8-b267-822108d4291c.com", resolver)
-	if !errors.Is(err, IPNotFoundError) && err == nil {
-		t.Fatalf("expected IPNotFoundError or DNS error, got: %v", err)
-	}
+	require.Error(t, err, "expected IPNotFoundError or DNS error for non-existent domain")
 }

57-65: Remove redundant error check.

Lines 61-63 duplicate the require.Error assertion on line 64.

Apply this diff:

 func TestTryToResolveHost_InvalidDomain(t *testing.T) {
 	resolver := newTestResolver(t)
 
 	_, err := tryToResolveHost("invalid_domain_###", resolver)
-	if err == nil {
-		t.Fatal("expected error for invalid domain name, got nil")
-	}
 	require.Error(t, err)
 }

67-75: Remove unreachable code.

Line 74 is unreachable: if the panic occurs (expected path), execution never reaches line 74; if no panic occurs, the deferred function fails the test at line 70 before line 74 can execute.

Apply this diff:

 func TestTryToResolveHost_NilResolver(t *testing.T) {
 	defer func() {
 		if r := recover(); r == nil {
 			t.Fatal("expected panic with nil resolver, but did not panic")
 		}
 	}()
 	_, _ = tryToResolveHost("example.com", nil)
-	require.Fail(t, "expected panic due to nil resolver")
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 49b6adc and a9b4a0b.

📒 Files selected for processing (2)
  • pkg/protocols/dns/utils.go (1 hunks)
  • pkg/protocols/dns/utils_test.go (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/protocols/dns/utils.go
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/protocols/dns/utils_test.go
pkg/protocols/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()

Files:

  • pkg/protocols/dns/utils_test.go
🧬 Code graph analysis (1)
pkg/protocols/dns/utils_test.go (1)
pkg/protocols/dns/utils.go (1)
  • IPNotFoundError (8-8)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Tests (ubuntu-latest)
  • GitHub Check: Tests (windows-latest)
  • GitHub Check: Tests (macOS-latest)
🔇 Additional comments (2)
pkg/protocols/dns/utils_test.go (2)

1-9: LGTM!

Package declaration and imports are appropriate for the test functionality.


11-18: External DNS dependency noted.

The helper uses Cloudflare's public DNS server (1.1.1.1:53), creating an external dependency that could make tests flaky in restricted network environments. This is acceptable for DNS protocol testing but be aware tests may fail without internet access.

Copy link
Contributor

@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: 0

🧹 Nitpick comments (1)
pkg/protocols/dns/utils_test.go (1)

37-42: Consider checking for specific error type.

The test validates error handling but doesn't verify that the returned error is specifically IPNotFoundError (mentioned in the AI summary). While checking for any error is acceptable for this "best effort" approach, asserting the specific error type would provide stronger guarantees.

If you want to verify the specific error type, you could update the test:

 func TestTryToResolveHost_IPNotFound(t *testing.T) {
 	resolver := newTestResolver(t)
 
 	_, err := tryToResolveHost("nonexistent-subdomain.ef37979f-9fff-43f8-b267-822108d4291c.com", resolver)
-	require.Error(t, err, "expected IPNotFoundError or DNS error for non-existent domain")
+	require.Error(t, err)
+	require.ErrorIs(t, err, IPNotFoundError, "expected IPNotFoundError for non-existent domain")
 }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a9b4a0b and a036295.

📒 Files selected for processing (2)
  • pkg/protocols/dns/request.go (4 hunks)
  • pkg/protocols/dns/utils_test.go (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.go: Format Go code using go fmt
Run static analysis with go vet

Files:

  • pkg/protocols/dns/utils_test.go
  • pkg/protocols/dns/request.go
pkg/protocols/**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

Each protocol implementation must provide a Request interface with methods Compile(), ExecuteWithResults(), Match(), and Extract()

Files:

  • pkg/protocols/dns/utils_test.go
  • pkg/protocols/dns/request.go
🧬 Code graph analysis (1)
pkg/protocols/dns/request.go (4)
pkg/protocols/common/contextargs/metainput.go (1)
  • MetaInput (18-30)
pkg/protocols/dns/dns.go (1)
  • Request (20-99)
pkg/protocols/common/contextargs/contextargs.go (1)
  • Context (22-33)
pkg/output/output.go (1)
  • InternalEvent (93-93)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Tests (windows-latest)
🔇 Additional comments (9)
pkg/protocols/dns/utils_test.go (5)

11-17: LGTM! Helper function is well-structured.

The test resolver helper properly initializes the DNS client with appropriate error handling.


19-26: Test coverage for A record resolution looks good.

The test appropriately validates successful IPv4 resolution. Note that this test has an external dependency on DNS infrastructure and example.com availability, which is acceptable for integration testing but may occasionally be flaky in restricted network environments.


44-49: LGTM! Invalid domain test is appropriate.

The test correctly validates error handling for malformed domain names.


51-58: Design relies on implicit nil contracts via defensive caller checks rather than explicit validation.

Verification confirms defensive nil-checks exist in request.go (line 63) and DO guard before calling tryToResolveHost. The panic behavior occurs via nil pointer dereference at resolver.Resolve() (line 14 of utils.go), not explicit validation. The function has no internal nil guard—it delegates this responsibility entirely to callers.

While this pattern works (all production calls are guarded), it's implicit rather than explicit. Consider whether the function should include an explicit nil check with a meaningful error message, or add documentation clarifying that resolver must never be nil. This would make the contract explicit rather than relying on caller discipline.


28-35: Document IPv6 requirement or add conditional skip for robustness.

The test uses real DNS resolution via Cloudflare's 1.1.1.1 to query ipv6.google.com for AAAA records. It will fail in environments where IPv6 is disabled or unavailable. No skip conditions exist in the codebase to handle this. Either document that full test suite requires IPv6 support, or add a skip condition when IPv6 is unavailable.

pkg/protocols/dns/request.go (4)

59-69: IP resolution logic correctly implements "best effort" approach.

The implementation properly handles the key scenarios:

  • Direct IP passthrough when input is already an IP
  • Defensive nil-check for dnsClient before resolution (addressing previous review feedback)
  • Trailing dot normalization for resolver compatibility (addressing previous review feedback)
  • Silent failure on resolution errors, leaving ip empty

The placement before the payload loop is correct since the domain variable is set from the original input and doesn't vary per payload iteration.


103-103: LGTM! IP parameter propagation is consistent and concurrency-safe.

The ip variable is correctly passed to execute() in both execution paths:

  • Threaded payload execution (line 103): The ip variable is safely captured in the goroutine closure since it's read-only and set before the loop begins.
  • Non-payload execution (line 116): Direct call with the resolved IP.

This ensures consistent IP propagation regardless of whether payloads are used.

Also applies to: 116-116


121-121: Function signature update is clean and backward-compatible.

The addition of the ip parameter to the internal execute() function is well-placed between related target parameters (domain and metadata). Since this is an internal function, there's no public API impact, and all call sites have been properly updated.


193-193: IP successfully propagated to DSL mapping for matcher/extractor access.

The updated call to responseToDSLMap completes the IP propagation chain from resolution through to the output event. According to the related changes in operators.go, this enables matchers and extractors to reference the resolved IP in templates.

@Mzack9999
Copy link
Member

@matejsmycka Thanks for your PR! Would it be possible to provide more context about the use case? In other protocols the ip is reported as it's the one nuclei instance connected to, whereas in case of dns we just interact with the dns resolver in most cases.

@matejsmycka
Copy link
Contributor Author

@matejsmycka Thanks for your PR! Would it be possible to provide more context about the use case? In other protocols the ip is reported as it's the one nuclei instance connected to, whereas in case of dns we just interact with the dns resolver in most cases.

Hi, we are using it for asset discovery, and we are using IP addresses as assets (for example, like the Shodan). This makes it easy to track back each finding to IP hosts. I could create my own resolver for Nuclei finding.host attribute, that would work for all protocols AFAIK. However, I thought it made sense to contribute since probably more people will expect IP in the JSON findings to match the resolved target.

@matejsmycka
Copy link
Contributor Author

If this feature is not wanted, we will add a resolver on our side.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants