Skip to content

Conversation

@YohanBeattie
Copy link

@YohanBeattie YohanBeattie commented Nov 10, 2025

Proposed changes

For UDP, an open port is not always easy to detect. Especially, using Dial on UDP doesn’t really establish a connection, and an error won't necessary pop up if the remote port is closed. The dial may succeed even if the port isn’t open, because UDP is connectionless. I proposed in my piece of code to check if the udp port is open by sending a small packet and wait for a reply or an ICMP “port unreachable” response.

I am not quite familiar with the nuclei unit test process. I only tested the change I made on udp open/closed port to check if it was more effective than the previous version of code.

Checklist

  • [ x] 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

  • Bug Fixes
    • Enhanced UDP port connectivity verification to confirm bidirectional communication and responsiveness with improved timeout handling, ensuring more reliable and accurate diagnostics.
    • Updated error messages for UDP port checks to provide clearer diagnostic feedback.

@auto-assign auto-assign bot requested a review from Mzack9999 November 10, 2025 14:36
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 10, 2025

Walkthrough

The isUDPPortOpen function in scripts.go was enhanced to perform actual UDP communication verification. It now sends a probe message, sets a read deadline, and reads the response to confirm port accessibility. An error message was also corrected to match the function name.

Changes

Cohort / File(s) Change Summary
UDP Port Verification Enhancement
pkg/js/global/scripts.go
Updated error message to reflect function name. Enhanced isUDPPortOpen to send a "ping" probe, defer connection cleanup, set read deadline (from context or 5s default), and validate response receipt instead of only closing the connection.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

  • Verify the UDP probe logic correctly validates port accessibility
  • Confirm deadline handling (context deadline fallback to 5 seconds) is appropriate
  • Check that error handling on read failures returns expected values

Poem

🐰 A UDP port now truly speaks,
No phantom checks for several weeks!
Our probe hops forth with "ping" so bright,
Awaits a bouncing response in sight—
Is-Open now means what it means!

Pre-merge checks and finishing touches

✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main change: modifying UDP open port detection logic to be more reliable by sending probe packets and reading responses instead of relying on unreliable Dial behavior.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
✨ 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: 1

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between cc0f1e9 and 52d7d39.

📒 Files selected for processing (1)
  • pkg/js/global/scripts.go (2 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.go

📄 CodeRabbit inference engine (CLAUDE.md)

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

Files:

  • pkg/js/global/scripts.go
🔇 Additional comments (1)
pkg/js/global/scripts.go (1)

153-153: Good consistency fix!

The error message now correctly references isUDPPortOpen instead of isPortOpen, improving debugging clarity.

Comment on lines +166 to 184
defer conn.Close()

probe := []byte("ping")
if _, err := conn.Write(probe); err != nil {
return false, err
}

if dl, ok := ctx.Deadline(); ok {
_ = conn.SetReadDeadline(dl)
} else {
_ = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
}

buf := make([]byte, 1024)
_, err = conn.Read(buf)
if err != nil {
return false, nil
}
return true, nil
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

The hardcoded probe will produce false negatives for most UDP services.

The implementation has several significant issues:

  1. Protocol-agnostic probe: The hardcoded "ping" probe won't work for protocol-specific UDP services (DNS, NTP, SNMP, TFTP, etc.). Most UDP services ignore or reject arbitrary data, leading to false negatives where open ports appear closed.

  2. Inconsistent error handling: Write errors return (false, err) (line 170), but read errors return (false, nil) (line 182). A read timeout doesn't definitively prove the port is closed—it could mean the service doesn't respond to arbitrary data or has network issues.

  3. Missing ICMP error differentiation: While the PR description mentions detecting ICMP "port unreachable" responses, the code treats all read errors identically. Go's UDP Read will error on ICMP unreachable, but you should distinguish this from timeouts.

  4. Limited reliability: UDP port detection is inherently challenging without protocol-specific probes. Consider documenting this limitation or accepting a service-type parameter to send appropriate protocol messages.

Consider these improvements:

-			probe := []byte("ping")
-			if _, err := conn.Write(probe); err != nil {
-				return false, err
-			}
-		
-			if dl, ok := ctx.Deadline(); ok {
-				_ = conn.SetReadDeadline(dl)
-			} else {
-				_ = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
-			}
-
-			buf := make([]byte, 1024)
-			_, err = conn.Read(buf)
-			if err != nil {
-				return false, nil
-			}
-			return true, nil
+			// Send a minimal probe packet
+			probe := []byte("ping")
+			if _, err := conn.Write(probe); err != nil {
+				return false, nil  // Write errors typically indicate network issues, not closed ports
+			}
+		
+			if dl, ok := ctx.Deadline(); ok {
+				_ = conn.SetReadDeadline(dl)
+			} else {
+				_ = conn.SetReadDeadline(time.Now().Add(5 * time.Second))
+			}
+
+			buf := make([]byte, 1024)
+			_, err = conn.Read(buf)
+			if err != nil {
+				// Check if error is ICMP port unreachable (definitive "closed")
+				if opErr, ok := err.(*net.OpError); ok && opErr.Err.Error() == "connection refused" {
+					return false, nil  // Port definitively closed
+				}
+				// Timeout or other error: inconclusive (may be open but unresponsive to arbitrary data)
+				return false, nil
+			}
+			return true, nil

Note: Even with these changes, UDP port detection remains unreliable for services that require specific protocol handshakes. Document this limitation in the function description or consider adding a protocol-specific probe parameter.


🏁 Script executed:

#!/bin/bash
# 1. Display the file and specific lines in context
echo "=== Code at lines 166-184 with surrounding context ==="
sed -n '150,200p' pkg/js/global/scripts.go

echo ""
echo "=== Run go fmt on the file to check formatting ==="
go fmt pkg/js/global/scripts.go
echo "Exit code: $?"

echo ""
echo "=== Run go vet on the file ==="
go vet pkg/js/global/scripts.go

echo ""
echo "=== Search for the function containing these lines ==="
sed -n '1,184p' pkg/js/global/scripts.go | tail -100

Length of output: 628


🏁 Script executed:

#!/bin/bash
# Display the file and specific lines in context
echo "=== Code at lines 150-200 with surrounding context ==="
sed -n '150,200p' pkg/js/global/scripts.go

echo ""
echo "=== Run go fmt to check formatting ==="
cd pkg/js/global && go fmt scripts.go 2>&1 || true
echo "---"

echo ""
echo "=== Run go vet on the file ==="
cd - > /dev/null && go vet ./pkg/js/global/scripts.go 2>&1 || true

echo ""
echo "=== Extract full function containing lines 166-184 ==="
# Search backwards from line 166 for 'func' to find function start
awk 'NR >= 140 && NR <= 190' pkg/js/global/scripts.go

echo ""
echo "=== Search for error handling patterns with net.OpError ==="
grep -n "OpError\|ICMP\|connection refused" pkg/js/global/scripts.go || echo "No matches found"

Length of output: 11816


UDP port detection logic has multiple functional issues requiring fixes.

The code at lines 166-184 has three confirmed issues:

  1. Hardcoded probe won't work for protocol-specific UDP services: The "ping" probe (line 168) won't elicit responses from DNS, NTP, SNMP, TFTP, and other UDP services that require specific protocol handshakes, causing false negatives.

  2. Inconsistent error handling: Write errors return (false, err) (line 170), but read errors return (false, nil) (line 182). This inconsistency masks write failures and incorrectly treats all read timeouts as open ports.

  3. Missing ICMP error differentiation: No code distinguishes ICMP "port unreachable" from read timeouts. Go's UDP Read will error on ICMP unreachable—this should be caught and handled separately, not treated as a timeout.

These issues conflict with the PR description's intent to detect ICMP unreachable responses. The proposed diff additions (ICMP detection and consistent error handling) are necessary improvements, though UDP port detection will remain inherently unreliable for arbitrary services without protocol-specific probes.

🤖 Prompt for AI Agents
In pkg/js/global/scripts.go around lines 166 to 184, fix UDP probe logic: stop
using a hardcoded "ping" payload — send an empty/zero-length probe or accept a
configurable/protocol-specific payload so services that require handshakes
aren't assumed closed; make error handling consistent by returning (false, err)
for any actual write failures and for non-timeout read errors (only treat read
timeouts as a nil error indicating no response); and detect ICMP "port
unreachable" separately by inspecting the read error (e.g., unwrap
net.OpError/syscall errors or use errors.Is) and treat ICMP unreachable as a
definitive closed port (return false with a specific error), while timeouts
should be treated as no response (return false, nil) as intended. Ensure the
read deadline logic remains and document that UDP checks are unreliable without
protocol-specific probes.

@YohanBeattie
Copy link
Author

For context, I started having multiple false positive on a "snmpv3 fingerprint" template a few weeks ago (when it was created) and I believe it comes from the way UDP open port check is made.

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.

1 participant