Skip to content

fix: prevent nmap service info from being copied to unrelated ports#1613

Merged
Mzack9999 merged 3 commits intoprojectdiscovery:devfrom
yutianqaq:fix/nmap-service-leak
Jan 7, 2026
Merged

fix: prevent nmap service info from being copied to unrelated ports#1613
Mzack9999 merged 3 commits intoprojectdiscovery:devfrom
yutianqaq:fix/nmap-service-leak

Conversation

@yutianqaq
Copy link
Copy Markdown
Contributor

@yutianqaq yutianqaq commented Jan 2, 2026

Problem

When using naabu with nmap integration (-nmap-cli), service information was being incorrectly copied to ports that don't have service data from nmap.

Example:

  • Port 22 (SSH) has service info from nmap
  • Ports 2080, 5173 have NO service info
  • But output showed all ports with SSH service details

Before fix:

{"ip":"192.168.1.117","port":22,"extra_info":"protocol 2.0","name":"ssh","product":"OpenSSH","version":"10.0p2 Debian 5"}
{"ip":"192.168.1.117","port":2080,"extra_info":"protocol 2.0","name":"ssh","product":"OpenSSH","version":"10.0p2 Debian 5"}  // Wrong!
{"ip":"192.168.1.117","port":5173,"extra_info":"protocol 2.0","name":"ssh","product":"OpenSSH","version":"10.0p2 Debian 5"}  // Wrong!

After fix:

{"ip":"192.168.1.117","port":22,"extra_info":"protocol 2.0","name":"ssh","product":"OpenSSH","version":"10.0p2 Debian 5"}
{"ip":"192.168.1.117","port":2080}  // Correct - no service info
{"ip":"192.168.1.117","port":5173}  // Correct - no service info

Root Cause

In pkg/runner/output.go, the Result struct was reused across port iterations. When a port had no service info (p.Service == nil), the service fields from the previous port were not cleared, causing data leakage.

Solution

Added else blocks to clear all service-related fields when p.Service == nil in both:

  1. WriteJSONOutputWithMac() - for JSON output
  2. WriteCsvOutputWithMac() - for CSV output

Changes

  • Modified pkg/runner/output.go
  • Fixed service info propagation bug in both JSON and CSV output functions
  • Added field clearing logic for ports without service data

Testing

Tested with port 22 (with SSH service info) followed by ports 2080, 5173 (no service info):

  • JSON output: Only port 22 shows service info
  • CSV output: Only port 22 shows service info

Summary by CodeRabbit

  • Bug Fixes
    • JSON and CSV outputs now consistently clear service-related fields when a port has no associated service, preventing values from carrying over between entries and ensuring outputs accurately reflect absent service data.
    • Internal handling of service field population centralized and simplified to improve consistency.

✏️ Tip: You can customize this high-level summary in your review settings.

@auto-assign auto-assign bot requested a review from dogancanbakir January 2, 2026 20:54
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 2, 2026

Walkthrough

WriteJSONOutputWithMac and WriteCsvOutputWithMac now delegate service-field copying/clearing to a new helper, copyServiceFields, which copies all service-related fields when p.Service is present or clears them by using an empty port.Service when p.Service is nil.

Changes

Cohort / File(s) Summary
Service field handling
pkg/runner/output.go
Added copyServiceFields(result *Result, service *port.Service) and replaced manual per-field copies in WriteJSONOutputWithMac and WriteCsvOutputWithMac with calls to this helper; helper copies all service fields when non-nil or clears them via an empty port.Service when nil.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

Poem

🐰 I hop through lines with careful cheer,

A helper now keeps fields all clear,
No stray values hide,
Outputs tidy and wide,
Hooray — rows hop forward, sincere! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% 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 title accurately describes the main fix: preventing nmap service info from leaking to unrelated ports, which directly addresses the root cause identified in the PR.
✨ Finishing touches
  • 📝 Generate docstrings

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
Copy Markdown

@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/runner/output.go (1)

226-259: Consider extracting service field logic to reduce duplication.

The service field copying and clearing logic (lines 226-259) is duplicated in WriteCsvOutputWithMac (lines 303-336). Consider extracting this into a helper method to improve maintainability.

🔎 Example refactor with helper methods

Add these helper methods to the file:

// copyServiceToResult copies service fields from port.Service to Result
func copyServiceToResult(result *Result, service *port.Service) {
	result.DeviceType = service.DeviceType
	result.ExtraInfo = service.ExtraInfo
	result.HighVersion = service.HighVersion
	result.Hostname = service.Hostname
	result.LowVersion = service.LowVersion
	result.Method = service.Method
	result.Name = service.Name
	result.OSType = service.OSType
	result.Product = service.Product
	result.Proto = service.Proto
	result.RPCNum = service.RPCNum
	result.ServiceFP = service.ServiceFP
	result.Tunnel = service.Tunnel
	result.Version = service.Version
	result.Confidence = service.Confidence
}

// clearServiceFields clears all service-related fields in Result
func clearServiceFields(result *Result) {
	result.DeviceType = ""
	result.ExtraInfo = ""
	result.HighVersion = ""
	result.Hostname = ""
	result.LowVersion = ""
	result.Method = ""
	result.Name = ""
	result.OSType = ""
	result.Product = ""
	result.Proto = ""
	result.RPCNum = ""
	result.ServiceFP = ""
	result.Tunnel = ""
	result.Version = ""
	result.Confidence = 0
}

Then replace the duplicated logic:

 		// copy the service fields
 		if p.Service != nil {
-			result.DeviceType = p.Service.DeviceType
-			result.ExtraInfo = p.Service.ExtraInfo
-			result.HighVersion = p.Service.HighVersion
-			result.Hostname = p.Service.Hostname
-			result.LowVersion = p.Service.LowVersion
-			result.Method = p.Service.Method
-			result.Name = p.Service.Name
-			result.OSType = p.Service.OSType
-			result.Product = p.Service.Product
-			result.Proto = p.Service.Proto
-			result.RPCNum = p.Service.RPCNum
-			result.ServiceFP = p.Service.ServiceFP
-			result.Tunnel = p.Service.Tunnel
-			result.Version = p.Service.Version
-			result.Confidence = p.Service.Confidence
+			copyServiceToResult(result, p.Service)
 		} else {
-			// Clear service fields if no service information exists
-			result.DeviceType = ""
-			result.ExtraInfo = ""
-			result.HighVersion = ""
-			result.Hostname = ""
-			result.LowVersion = ""
-			result.Method = ""
-			result.Name = ""
-			result.OSType = ""
-			result.Product = ""
-			result.Proto = ""
-			result.RPCNum = ""
-			result.ServiceFP = ""
-			result.Tunnel = ""
-			result.Version = ""
-			result.Confidence = 0
+			clearServiceFields(result)
 		}

Apply the same pattern to WriteCsvOutputWithMac.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9976c32 and 326a0f1.

📒 Files selected for processing (1)
  • pkg/runner/output.go
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/runner/output.go (1)
pkg/port/port.go (1)
  • Service (35-51)
🔇 Additional comments (2)
pkg/runner/output.go (2)

242-259: Fix correctly prevents service information leakage.

The else block properly clears all 15 service-related fields when p.Service == nil, preventing carryover from previous iterations. This directly addresses the bug described in the PR objectives.


301-337: CSV output correctly mirrors JSON fix.

The service field handling in WriteCsvOutputWithMac is consistent with WriteJSONOutputWithMac, ensuring both output formats prevent service information leakage. The added blank lines improve code readability.

@yutianqaq
Copy link
Copy Markdown
Contributor Author

Additional Notes

This issue can be observed when running:

sudo ./naabu -host 192.168.1.117 -port 22,2080,5173,5432 -nmap-cli 'nmap -sV' -json -o test

Terminal output correctly shows only port 22 with service info.
JSON file incorrectly copies the SSH service info to other ports without service data.

@dogancanbakir
Copy link
Copy Markdown
Member

I couldn't repro this

@yutianqaq
Copy link
Copy Markdown
Contributor Author

yutianqaq commented Jan 3, 2026

https://asciinema.org/a/7XShK6J7x4dpLLGuN6ZSjmE2G
Reduce the possibility of incorrect data leakage
without relying on -sT.

git clone https://github.com/projectdiscovery/naabu naabu-test

cd naabu-test

ls naabu

go build ./cmd/naabu

ls naabu

naabu -host 192.168.1.117 -port 22,2080,5173,5432 -nmap-cli 'nmap -sV' -json -o test

Copy link
Copy Markdown

@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/runner/output.go (1)

304-329: LGTM! Helper function correctly addresses the service leak bug.

The implementation correctly handles both cases:

  • When service is not nil, all 15 service fields are copied to the result
  • When service is nil, copying from an empty port.Service{} clears all fields using zero values

This centralized approach eliminates the code duplication flagged in the previous review and ensures consistent behavior across JSON and CSV output paths.

💡 Optional: Minor simplification

The local variable s could be eliminated by directly handling both branches, though the current approach is perfectly clear:

func copyServiceFields(result *Result, service *port.Service) {
	if service == nil {
		service = &port.Service{}
	}
	
	result.DeviceType = service.DeviceType
	result.ExtraInfo = service.ExtraInfo
	// ... rest of fields
}

However, modifying the parameter might be considered less idiomatic, so the current implementation is fine.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 326a0f1 and 6a4affd.

📒 Files selected for processing (1)
  • pkg/runner/output.go
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/runner/output.go (2)
pkg/port/port.go (1)
  • Service (35-51)
pkg/result/results.go (1)
  • Result (39-44)
🔇 Additional comments (2)
pkg/runner/output.go (2)

225-226: LGTM! Service field leak fixed for JSON output.

The centralized helper correctly copies service fields when present or clears them when p.Service is nil, preventing service data from leaking to subsequent ports.


268-270: LGTM! Service field leak fixed for CSV output.

Consistent with the JSON output path, this correctly delegates service field handling to the centralized helper.

Copy link
Copy Markdown

@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/runner/output.go (1)

302-326: LGTM! The helper correctly handles both copying and clearing service fields.

The function properly addresses the service info leak by ensuring all service fields are set to zero values when service is nil. All 15 fields from port.Service are accounted for, and the implementation is straightforward.

💡 Optional: Minor simplification

The intermediate variable s could be eliminated for slightly more concise code:

 func copyServiceFields(result *Result, service *port.Service) {
-	var s *port.Service
-	if service != nil {
-		s = service
-	} else {
-		s = &port.Service{}
+	if service == nil {
+		service = &port.Service{}
 	}
 
-	result.DeviceType = s.DeviceType
-	result.ExtraInfo = s.ExtraInfo
+	result.DeviceType = service.DeviceType
+	result.ExtraInfo = service.ExtraInfo
 	// ... (apply to all remaining fields)

The current implementation is perfectly correct, so this is purely a style preference.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6a4affd and 9666e3e.

📒 Files selected for processing (1)
  • pkg/runner/output.go
🧰 Additional context used
🧬 Code graph analysis (1)
pkg/runner/output.go (1)
pkg/port/port.go (1)
  • Service (35-51)
🔇 Additional comments (2)
pkg/runner/output.go (2)

225-225: LGTM! Correctly delegates service field handling to the helper.

This change ensures that when p.Service is nil, all service fields are cleared rather than retaining values from the previous iteration, which was the root cause of the service info leak bug.


267-268: LGTM! Consistent fix applied to CSV output path.

The same helper is used for both JSON and CSV outputs, ensuring the service info leak bug is fixed consistently across all output formats.

@knakul853
Copy link
Copy Markdown
Contributor

@yutianqaq Appreciate the contribution. Nice work on this change.

@Mzack9999
Copy link
Copy Markdown
Member

@coderabbitai create an issue for this PR

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 7, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for PR #1613

gh issue create \
  --title "Fix nmap service information leaking to unrelated ports" \
  --body "## Description

A bug was identified where nmap-derived service information is incorrectly propagated to ports that have no service data. This occurs when service fields from a previous port are not cleared and leak into subsequent port outputs in JSON and CSV formats.

## Impact

When running naabu with nmap service detection (e.g., \`-nmap-cli 'nmap -sV'\`), ports without service information incorrectly inherit service data from previously scanned ports in the output files, while terminal output displays correctly.

## Related Pull Request

This issue is being tracked for PR #1613: https://github.com/projectdiscovery/naabu/pull/1613

Requested by: @Mzack9999
Comment: https://github.com/projectdiscovery/naabu/pull/1613#issuecomment" \
  --assignee Mzack9999

Length of output: 53


@Mzack9999: I've created issue #1624 for this PR: #1624

The issue describes the bug where nmap service information was leaking to unrelated ports and includes a backlink to this PR.

@Mzack9999 Mzack9999 linked an issue Jan 7, 2026 that may be closed by this pull request
@Mzack9999 Mzack9999 merged commit a551031 into projectdiscovery:dev Jan 7, 2026
10 checks passed
@Mzack9999 Mzack9999 added the Type: Bug Inconsistencies or issues which will cause an issue or problem for users or implementors. label Jan 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Type: Bug Inconsistencies or issues which will cause an issue or problem for users or implementors.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix nmap service information leaking to unrelated ports

4 participants