Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cmd/nuclei/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -373,6 +373,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.StringVarP(&options.Interface, "interface", "i", "", "network interface to use for network scan"),
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
flagSet.StringSliceVarP(&options.ReservedPorts, "reserved-ports", "rport", nil, "list of reserved ports that network templates should avoid", goflags.CommaSeparatedStringSliceOptions),
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 0, "max response size to read in bytes"),
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", unitutils.Mega, "max response size to read in bytes"),
flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"),
Expand Down
17 changes: 11 additions & 6 deletions pkg/protocols/common/contextargs/contextargs.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ import (
)

var (
// reservedPorts contains list of reserved ports for non-network requests in nuclei
reservedPorts = []string{"80", "443", "8080", "8443", "8081", "53"}
// defaultReservedPorts contains the default list of reserved ports for non-network requests in nuclei
defaultReservedPorts = []string{"80", "443", "8080", "8443", "8081", "53"}
)

// Context implements a shared context struct to share information across multiple templates within a workflow
Expand Down Expand Up @@ -109,12 +109,17 @@ func (ctx *Context) Add(key string, v interface{}) {

// UseNetworkPort updates input with required/default network port for that template
// but is ignored if input/target contains non-http ports like 80,8080,8081 etc
func (ctx *Context) UseNetworkPort(port string, excludePorts string) error {
ignorePorts := reservedPorts
if excludePorts != "" {
// Precedence: cliExcludePorts > templateExcludePorts > default reserved ports
func (ctx *Context) UseNetworkPort(port string, templateExcludePorts string, cliExcludePorts []string) error {
ignorePorts := defaultReservedPorts

if len(cliExcludePorts) > 0 {
ignorePorts = cliExcludePorts
} else if templateExcludePorts != "" {
// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready
ignorePorts = sliceutil.Dedupe(strings.Split(excludePorts, ","))
ignorePorts = sliceutil.Dedupe(strings.Split(templateExcludePorts, ","))
}
Comment on lines +116 to 121
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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "Inspect UseNetworkPort parsing:"
rg -n -C3 'UseNetworkPort|templateExcludePorts|cliExcludePorts|strings.Split|TrimSpace' pkg/protocols/common/contextargs/contextargs.go

echo
echo "Inspect TestUseNetworkPort cases:"
rg -n 'templateExcl|cliReserved|name:' pkg/protocols/common/contextargs/contextargs_test.go

Repository: projectdiscovery/nuclei

Length of output: 1835


🏁 Script executed:

rg -n "normalizePorts|TrimSpace" pkg/protocols/common/contextargs/ --type go

Repository: projectdiscovery/nuclei

Length of output: 49


🏁 Script executed:

# Check if sliceutil or other utility packages have port normalization
rg -n "TrimSpace|Dedupe" pkg/utils/ --type go -A 2 | head -30

Repository: projectdiscovery/nuclei

Length of output: 174


🏁 Script executed:

# Check if there are other port-related parsing that might have similar issues
rg -n "strings\.Split.*port|excludePort" pkg/protocols/ --type go | head -20

Repository: projectdiscovery/nuclei

Length of output: 152


🏁 Script executed:

# Check the javascript/js.go port parsing more thoroughly
rg -n -B5 -A10 'strings\.Split.*portStr' pkg/protocols/javascript/js.go

Repository: projectdiscovery/nuclei

Length of output: 520


🏁 Script executed:

# Find where ignorePorts is actually used for comparison
rg -n -A15 'ignorePorts :=' pkg/protocols/common/contextargs/contextargs.go | head -40

Repository: projectdiscovery/nuclei

Length of output: 636


🏁 Script executed:

# Check sliceutil.Dedupe to understand what it does
fd -t f "sliceutil" --exec cat {} \; 2>/dev/null | head -100

Repository: projectdiscovery/nuclei

Length of output: 49


🏁 Script executed:

# Find sliceutil package/file
fd -t f -i sliceutil

Repository: projectdiscovery/nuclei

Length of output: 49


🏁 Script executed:

# Check how ignorePorts is used after assignment (the actual comparison)
rg -n -A30 'if port ==' pkg/protocols/common/contextargs/contextargs.go | head -50

Repository: projectdiscovery/nuclei

Length of output: 908


🏁 Script executed:

# Find stringsutil.EqualFoldAny implementation to confirm comparison behavior
fd -t f stringsutil || rg -n "func EqualFoldAny" --type go

Repository: projectdiscovery/nuclei

Length of output: 49


🏁 Script executed:

# Get the full context of how ports flow through the system
rg -n "defaultReservedPorts" pkg/protocols/common/contextargs/contextargs.go -B 2 -A 2

Repository: projectdiscovery/nuclei

Length of output: 556


🏁 Script executed:

# Search more broadly for EqualFoldAny and stringsutil
rg -n "EqualFoldAny" --type go | head -20

Repository: projectdiscovery/nuclei

Length of output: 2619


🏁 Script executed:

# Check all imports in the contextargs file to understand what stringsutil is used
head -20 pkg/protocols/common/contextargs/contextargs.go

Repository: projectdiscovery/nuclei

Length of output: 598


🏁 Script executed:

# Find the stringsutil package to check EqualFoldAny implementation
rg -n "package strings|func EqualFoldAny" --type go | grep -A5 "EqualFoldAny"

Repository: projectdiscovery/nuclei

Length of output: 49


🏁 Script executed:

# Check if there are integration tests or other test files that might test with whitespace
find pkg/protocols/common/contextargs -name "*test*" -type f

Repository: projectdiscovery/nuclei

Length of output: 119


🏁 Script executed:

# Examine the full test file to see all test cases
cat pkg/protocols/common/contextargs/contextargs_test.go

Repository: projectdiscovery/nuclei

Length of output: 2137


Normalize excluded port tokens before matching.

templateExcludePorts and CLI-provided cliExcludePorts are not trimmed. Inputs like "80, 443" will create tokens with leading/trailing whitespace that fail to match during port comparison, causing incorrect port-rewrite behavior.

This differs from the port parsing in pkg/protocols/javascript/js.go, which correctly applies strings.TrimSpace() to each token. Test coverage also lacks whitespace-padded port cases.

💡 Proposed fix
 func (ctx *Context) UseNetworkPort(port string, templateExcludePorts string, cliExcludePorts []string) error {
 	ignorePorts := defaultReservedPorts
 
 	if len(cliExcludePorts) > 0 {
-		ignorePorts = cliExcludePorts
+		ignorePorts = normalizePorts(cliExcludePorts)
 	} else if templateExcludePorts != "" {
 		// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready
-		ignorePorts = sliceutil.Dedupe(strings.Split(templateExcludePorts, ","))
+		ignorePorts = normalizePorts(strings.Split(templateExcludePorts, ","))
 	}
@@
 	return nil
 }
+
+func normalizePorts(ports []string) []string {
+	normalized := make([]string, 0, len(ports))
+	for _, p := range ports {
+		p = strings.TrimSpace(p)
+		if p != "" {
+			normalized = append(normalized, p)
+		}
+	}
+	return sliceutil.Dedupe(normalized)
+}
📝 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
if len(cliExcludePorts) > 0 {
ignorePorts = cliExcludePorts
} else if templateExcludePorts != "" {
// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready
ignorePorts = sliceutil.Dedupe(strings.Split(excludePorts, ","))
ignorePorts = sliceutil.Dedupe(strings.Split(templateExcludePorts, ","))
}
if len(cliExcludePorts) > 0 {
ignorePorts = normalizePorts(cliExcludePorts)
} else if templateExcludePorts != "" {
// TODO: add support for service names like http,https,ssh etc once https://github.com/projectdiscovery/netdb is ready
ignorePorts = normalizePorts(strings.Split(templateExcludePorts, ","))
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/protocols/common/contextargs/contextargs.go` around lines 116 - 121,
Normalize excluded-port tokens before using them: when building ignorePorts from
templateExcludePorts or cliExcludePorts ensure each token is trimmed (use
strings.TrimSpace on each Split token) and then deduplicated (sliceutil.Dedupe).
Update the block that sets ignorePorts (variables templateExcludePorts,
cliExcludePorts, and ignorePorts in contextargs.go) to map TrimSpace over tokens
coming from strings.Split(templateExcludePorts, ",") and to trim any entries in
cliExcludePorts as well so comparisons later will match whitespace-padded
inputs.


if port == "" {
// if template does not contain port, do nothing
return nil
Expand Down
86 changes: 86 additions & 0 deletions pkg/protocols/common/contextargs/contextargs_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package contextargs

import (
"context"
"testing"

"github.com/stretchr/testify/require"
)

func TestUseNetworkPort(t *testing.T) {
tests := []struct {
name string
target string
templatePort string
templateExcl string
cliReserved []string
expectedHost string
}{
{
name: "default-reserved-ports-work",
target: "example.com:80",
templatePort: "1234",
templateExcl: "",
cliReserved: nil,
expectedHost: "example.com:1234",
},
{
name: "default-target-works",
target: "example.com:22",
templatePort: "80",
templateExcl: "",
cliReserved: nil,
expectedHost: "example.com:22",
},
{
name: "template-overwrites-defaults-no-exclusions",
target: "example.com:80",
templatePort: "1234",
templateExcl: "0",
cliReserved: nil,
expectedHost: "example.com:80",
},
{
name: "template-overwrites-defaults-some-exclusions",
target: "example.com:5353",
templatePort: "1234",
templateExcl: "5353",
cliReserved: nil,
expectedHost: "example.com:1234",
},
{
name: "cli-overwrites-template",
target: "example.com:80",
templatePort: "1234",
templateExcl: "0",
cliReserved: []string{"80"},
expectedHost: "example.com:1234",
},
{
name: "cli-overwrites-default-no-exclusions",
target: "example.com:80",
templatePort: "1234",
templateExcl: "",
cliReserved: []string{"0"},
expectedHost: "example.com:80",
},
{
name: "cli-overwrites-default-some-exclusions",
target: "example.com:5353",
templatePort: "1234",
templateExcl: "",
cliReserved: []string{"5353"},
expectedHost: "example.com:1234",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ctx := NewWithInput(context.Background(), tt.target)
err := ctx.UseNetworkPort(tt.templatePort, tt.templateExcl, tt.cliReserved)

require.NoError(t, err, "unexpected error")
require.Equal(t, tt.expectedHost, ctx.MetaInput.Input)
})
}
}
2 changes: 1 addition & 1 deletion pkg/protocols/javascript/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ func (request *Request) executeWithResults(port string, target *contextargs.Cont
// use network port updates input with new port requested in template file
// and it is ignored if input port is not standard http(s) ports like 80,8080,8081 etc
// idea is to reduce redundant dials to http ports
if err := input.UseNetworkPort(port, request.getExcludePorts()); err != nil {
if err := input.UseNetworkPort(port, request.getExcludePorts(), request.options.Options.ReservedPorts); err != nil {
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
}

Expand Down
4 changes: 2 additions & 2 deletions pkg/protocols/network/request.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ func (request *Request) getOpenPorts(target *contextargs.Context) ([]string, err
openPorts := make([]string, 0)
for _, port := range request.ports {
cloned := target.Clone()
if err := cloned.UseNetworkPort(port, request.ExcludePorts); err != nil {
if err := cloned.UseNetworkPort(port, request.ExcludePorts, request.options.Options.ReservedPorts); err != nil {
errs = append(errs, err)
continue
}
Expand Down Expand Up @@ -120,7 +120,7 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
// use network port updates input with new port requested in template file
// and it is ignored if input port is not standard http(s) ports like 80,8080,8081 etc
// idea is to reduce redundant dials to http ports
if err := input.UseNetworkPort(port, request.ExcludePorts); err != nil {
if err := input.UseNetworkPort(port, request.ExcludePorts, request.options.Options.ReservedPorts); err != nil {
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
}
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, wrappedCallback); err != nil {
Expand Down
3 changes: 3 additions & 0 deletions pkg/types/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -465,6 +465,8 @@ type Options struct {
InlineTargetsList string `yaml:"targets-inline,omitempty"`
// ListTemplateProfiles lists all available template profiles
ListTemplateProfiles bool
// ReservedPorts is the list of ports that network templates should not use
ReservedPorts goflags.StringSlice
// LoadHelperFileFunction is a function that will be used to execute LoadHelperFile.
// If none is provided, then the default implementation will be used.
LoadHelperFileFunction LoadHelperFileFunction
Expand Down Expand Up @@ -695,6 +697,7 @@ func (options *Options) Copy() *Options {
HttpApiEndpoint: options.HttpApiEndpoint,
InlineTargetsList: options.InlineTargetsList,
ListTemplateProfiles: options.ListTemplateProfiles,
ReservedPorts: options.ReservedPorts,
LoadHelperFileFunction: options.LoadHelperFileFunction,
Logger: options.Logger,
DoNotCacheTemplates: options.DoNotCacheTemplates,
Expand Down
Loading