Skip to content

strict probe - #7622

Open
Mzack9999 wants to merge 2 commits into
devfrom
feat-automatic-scan
Open

strict probe#7622
Mzack9999 wants to merge 2 commits into
devfrom
feat-automatic-scan

Conversation

@Mzack9999

@Mzack9999 Mzack9999 commented Jul 30, 2026

Copy link
Copy Markdown
Member

Adds -strict-probe/-stp: skip templates whose target service isn't reachable — HTTP/headless on hosts httpx can't confirm, and network templates on closed ports. No raw-input fallback, no findings lost.

Supersedes #7592 (extends its strict-probe from HTTP to network reachability). Also carries the http client-pool idle-conn autotune.

Closes #6651

Summary by CodeRabbit

  • New Features

    • Added the -stp / -strict-probe option to skip templates when target services are unreachable, including unavailable HTTP services and closed TCP ports.
    • Added an SDK option to enable per-host rate limiting.
    • Improved HTTP connection handling for workloads with higher concurrency.
  • Documentation

    • Documented the strict probe option across supported README translations.

@Mzack9999 Mzack9999 mentioned this pull request Jul 30, 2026
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The PR adds -strict-probe reachability pruning for HTTP and TCP templates, preserves the option through runner configuration, documents it across README translations, adds related tests, and introduces bounded per-host HTTP idle connection sizing with an SDK configuration helper.

Changes

Strict probe reachability

Layer / File(s) Summary
Strict-probe contract and input behavior
cmd/nuclei/main.go, pkg/types/types.go, pkg/input/transform.go, pkg/input/transform_test.go
Adds the StrictProbe option, CLI aliases, copy propagation, and tests confirming HTTP inputs skip raw fallback only when enabled.
Reachability classification and pruning rules
internal/runner/reachability.go, internal/runner/reachability_test.go
Adds policy-aware HTTP/TCP probing, target and port parsing, TCP template eligibility checks, result classification, and unit coverage.
Runner strict-probe execution
internal/runner/runner.go
Enables strict input handling, excludes web templates when no HTTP service is reachable, and prunes definitively closed TCP templates.
Strict-probe CLI documentation
README*.md
Documents -stp, -strict-probe in the localized command-line help sections.

Per-host HTTP connection capacity

Layer / File(s) Summary
Per-host connection capacity model
lib/config.go, pkg/protocols/http/httpclientpool/clientpool.go
Adds WithPerHostRateLimit and derives bounded HTTP idle connection limits from configured concurrency values.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CLI
  participant Runner
  participant httpx
  participant InputHelper
  participant TemplateExecutor
  CLI->>Runner: enable -strict-probe
  Runner->>httpx: probe target services
  httpx-->>Runner: reachable HTTP targets
  Runner->>InputHelper: enable strict URL resolution
  Runner->>TemplateExecutor: exclude unreachable HTTP and closed TCP templates
Loading

Possibly related PRs

Poem

A rabbit probes ports in the night,
Skips unreachable paths from sight.
HTTP pools softly grow,
With bounded streams below—
Clean scans hop onward, light! 🐇

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The HTTP client-pool tuning and per-host rate-limit helper appear unrelated to strict-probe behavior. Split the HTTP client-pool and rate-limit changes into a separate PR unless they are required for strict-probe.
Docstring Coverage ⚠️ Warning Docstring coverage is 52.94% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title is too vague and does not describe the specific change beyond a generic phrase. Use a concise, specific title like "Add strict-probe flag to skip non-web targets".
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed The strict-probe behavior matches issue #6651 by skipping raw fallback after failed web probing.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat-automatic-scan

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

130-157: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Sequential, blocking port probing can add significant load-time latency.

reachable[p] is computed via nested sequential loops, dialing one host:port at a time with up to a 2s timeout each. For scans with many hosts and several distinct network-template ports, and especially against internet-facing targets where closed ports are often silently dropped (not RST'd) rather than actively refused, this can add a lot of blocking wall-clock time before the scan's normal request phase even begins.

Consider bounding this with a small worker pool (e.g. goroutines + semaphore/errgroup) since Fastdialer.Dial is already safe for concurrent use elsewhere in the codebase.

♻️ Sketch of bounded-concurrency probing
-	reachable := map[string]bool{}
-	for p := range toProbe {
-		for _, h := range hosts {
-			res := r.probe(h, p)
-			if res == portOpen || res == portUnknown {
-				reachable[p] = true
-				break
-			}
-		}
-	}
+	reachable := map[string]bool{}
+	var mu sync.Mutex
+	var wg sync.WaitGroup
+	sem := make(chan struct{}, maxConcurrentProbes)
+	for p := range toProbe {
+		wg.Add(1)
+		go func(p string) {
+			defer wg.Done()
+			sem <- struct{}{}
+			defer func() { <-sem }()
+			for _, h := range hosts {
+				res := r.probe(h, p)
+				if res == portOpen || res == portUnknown {
+					mu.Lock()
+					reachable[p] = true
+					mu.Unlock()
+					return
+				}
+			}
+		}(p)
+	}
+	wg.Wait()
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/runner/reachability.go` around lines 130 - 157, Update the
reachable-port probing loop in the reachability logic to use bounded concurrency
rather than dialing each host:port sequentially. Add a small worker pool or
semaphore around r.probe calls, safely coordinate concurrent updates to
reachable, and preserve the existing rule that a port is reachable when any host
returns portOpen or portUnknown.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@internal/runner/reachability.go`:
- Around line 55-92: Gate the strict reachability probe in
noHTTPServiceReachable (or strictProbeEnabled) on !r.options.DisableHTTPProbe,
so -no-httpx skips the internal httpx probe and web-template prune. In
internal/runner/runner.go lines 682-690, make no further change because the
root-cause guard will make this path no-op; in lines 783-791, make no further
change because the existing warning will then accurately cover both strict-probe
paths.
- Around line 269-281: Update classifyDial to classify transient DNS-resolution
failures as portUnknown rather than portClosed, while preserving portOpen and
existing timeout handling. Detect the relevant DNS/network error condition from
the dial result before the final portClosed fallback, ensuring single-host scans
do not treat temporary resolution failures as definitive closure.

In `@pkg/types/types.go`:
- Line 587: Run go fmt ./... to reformat the struct literal containing
StrictProbe, ensuring its field alignment matches AutomaticScan, Silent, and the
other entries.

In `@README_CN.md`:
- Line 309: Translate the strict-probe description while preserving the flag
name, behavior, and lossless qualifier: update README_CN.md lines 309-309 in
Chinese, README_ES.md lines 309-309 in Spanish, README_ID.md lines 309-309 in
Indonesian, README_JP.md lines 309-309 in Japanese, README_KR.md lines 309-309
in Korean, README_PT-BR.md lines 309-309 in Brazilian Portuguese, and
README_TR.md lines 309-309 in Turkish.

---

Nitpick comments:
In `@internal/runner/reachability.go`:
- Around line 130-157: Update the reachable-port probing loop in the
reachability logic to use bounded concurrency rather than dialing each host:port
sequentially. Add a small worker pool or semaphore around r.probe calls, safely
coordinate concurrent updates to reachable, and preserve the existing rule that
a port is reachable when any host returns portOpen or portUnknown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 4ac41e4e-543e-4042-82a0-6f8a067ec650

📥 Commits

Reviewing files that changed from the base of the PR and between ba05210 and 40bd7f1.

📒 Files selected for processing (17)
  • README.md
  • README_CN.md
  • README_ES.md
  • README_ID.md
  • README_JP.md
  • README_KR.md
  • README_PT-BR.md
  • README_TR.md
  • cmd/nuclei/main.go
  • internal/runner/reachability.go
  • internal/runner/reachability_test.go
  • internal/runner/runner.go
  • lib/config.go
  • pkg/input/transform.go
  • pkg/input/transform_test.go
  • pkg/protocols/http/httpclientpool/clientpool.go
  • pkg/types/types.go

Comment on lines +55 to +92
func (r *Runner) noHTTPServiceReachable() bool {
if r.inputProvider == nil {
return false
}
dialers := protocolstate.GetDialersWithId(r.options.ExecutionId)
if dialers == nil {
return false // cannot probe within policy — keep web templates
}
httpxOptions := httpx.DefaultOptions
if r.options.AliveHttpProxy != "" {
httpxOptions.Proxy = r.options.AliveHttpProxy
} else if r.options.AliveSocksProxy != "" {
httpxOptions.Proxy = r.options.AliveSocksProxy
}
httpxOptions.RetryMax = r.options.Retries
if r.options.Timeout > 0 {
httpxOptions.Timeout = time.Duration(r.options.Timeout) * time.Second
}
httpxOptions.NetworkPolicy = dialers.NetworkPolicy
client, err := httpx.New(&httpxOptions)
if err != nil {
return false // cannot probe — keep web templates
}

anyHTTP := false
r.inputProvider.Iterate(func(mi *contextargs.MetaInput) bool {
if strings.HasPrefix(mi.Input, "http://") || strings.HasPrefix(mi.Input, "https://") {
anyHTTP = true
return false
}
if utils.ProbeURL(mi.Input, client) != "" {
anyHTTP = true
return false
}
return true
})
return !anyHTTP
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

-no-httpx doesn't actually disable the strict-probe reachability prune — gate it consistently.

strictProbeEnabled() / noHTTPServiceReachable() never check r.options.DisableHTTPProbe, so the web-protocol exclusion prune (which relies on its own internal httpx.New() probe) still runs — and can still exclude HTTP/headless/websocket templates and issue HTTP probes — even when the user passes -no-httpx. This contradicts the warning added alongside the per-input gating path, which claims strict-probe "has no effect" in that combination.

  • internal/runner/reachability.go#L55-L92: add a !r.options.DisableHTTPProbe (or equivalent) check to strictProbeEnabled()/noHTTPServiceReachable() so this probe/prune is skipped when httpx probing is disabled.
  • internal/runner/runner.go#L682-L690: once the check above is added, this block will correctly no-op under -no-httpx, matching user intent.
  • internal/runner/runner.go#L783-L791: the existing warning message will then be accurate for both strict-probe code paths, not just the per-input one.
📍 Affects 2 files
  • internal/runner/reachability.go#L55-L92 (this comment)
  • internal/runner/runner.go#L682-L690
  • internal/runner/runner.go#L783-L791
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/runner/reachability.go` around lines 55 - 92, Gate the strict
reachability probe in noHTTPServiceReachable (or strictProbeEnabled) on
!r.options.DisableHTTPProbe, so -no-httpx skips the internal httpx probe and
web-template prune. In internal/runner/runner.go lines 682-690, make no further
change because the root-cause guard will make this path no-op; in lines 783-791,
make no further change because the existing warning will then accurately cover
both strict-probe paths.

Comment on lines +269 to +281
func classifyDial(dial dialFunc, addr string, timeout time.Duration) probeResult {
ctx, cancel := context.WithTimeout(context.Background(), timeout)
defer cancel()
conn, err := dial(ctx, "tcp", addr)
if err == nil {
_ = conn.Close()
return portOpen
}
if ne, ok := err.(net.Error); ok && ne.Timeout() {
return portUnknown
}
return portClosed
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

DNS-resolution failures are classified as portClosed, not portUnknown.

classifyDial only treats net.Error.Timeout() as portUnknown; a DNS lookup failure (e.g. temporary resolution issue) surfaces as a non-timeout error and falls through to portClosed. This is mostly cushioned by the multi-host "reachable if open/unknown on ANY host" logic, but for single-host scans (or all hosts failing DNS transiently) it could cause an incorrect "definitively closed" prune, violating the losslessness guarantee.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@internal/runner/reachability.go` around lines 269 - 281, Update classifyDial
to classify transient DNS-resolution failures as portUnknown rather than
portClosed, while preserving portOpen and existing timeout handling. Detect the
relevant DNS/network error condition from the dial result before the final
portClosed fallback, ensuring single-host scans do not treat temporary
resolution failures as definitive closure.

Comment thread pkg/types/types.go
PerHostRateLimit: options.PerHostRateLimit,
LeaveDefaultPorts: options.LeaveDefaultPorts,
AutomaticScan: options.AutomaticScan,
StrictProbe: options.StrictProbe,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Misaligned struct field breaks gofmt formatting.

StrictProbe: doesn't line up with the other aligned fields in this literal (e.g. AutomaticScan:, Silent:). Run go fmt ./... to fix the column alignment.

As per coding guidelines, "**/*.go: Format Go code using go fmt ./...".

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/types/types.go` at line 587, Run go fmt ./... to reformat the struct
literal containing StrictProbe, ensuring its field alignment matches
AutomaticScan, Silent, and the other entries.

Source: Coding guidelines

Comment thread README_CN.md
-ss, -scan-strategy value strategy to use while scanning(auto/host-spray/template-spray) (default auto)
-irt, -input-read-timeout value timeout on input read (default 3m0s)
-nh, -no-httpx disable httpx probing for non-url input
-stp, -strict-probe skip templates whose target service is unreachable (HTTP on non-web ports, network on closed ports; lossless)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Localize the strict-probe description in every translated README.

The new flag is documented with the same English text across all localized files, so users reading those translations receive untranslated help text.

  • README_CN.md#L309-L309: translate the description into Chinese.
  • README_ES.md#L309-L309: translate the description into Spanish.
  • README_ID.md#L309-L309: translate the description into Indonesian.
  • README_JP.md#L309-L309: translate the description into Japanese.
  • README_KR.md#L309-L309: translate the description into Korean.
  • README_PT-BR.md#L309-L309: translate the description into Brazilian Portuguese.
  • README_TR.md#L309-L309: translate the description into Turkish.
📍 Affects 7 files
  • README_CN.md#L309-L309 (this comment)
  • README_ES.md#L309-L309
  • README_ID.md#L309-L309
  • README_JP.md#L309-L309
  • README_KR.md#L309-L309
  • README_PT-BR.md#L309-L309
  • README_TR.md#L309-L309
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@README_CN.md` at line 309, Translate the strict-probe description while
preserving the flag name, behavior, and lossless qualifier: update README_CN.md
lines 309-309 in Chinese, README_ES.md lines 309-309 in Spanish, README_ID.md
lines 309-309 in Indonesian, README_JP.md lines 309-309 in Japanese,
README_KR.md lines 309-309 in Korean, README_PT-BR.md lines 309-309 in Brazilian
Portuguese, and README_TR.md lines 309-309 in Turkish.

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.

[Feature Request] Add a flag (e.g., -strict-probe) to stop scanning if internal httpx probe fails (No Fallback)

1 participant