Skip to content

Conversation

@pussycat0x
Copy link
Contributor

@pussycat0x pussycat0x commented Sep 27, 2025

Proposed changes

Added support for multiple ports in the Nuclei engine for JavaScript templates

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)

./nuclei -u 127.0.0.1 -t ssh.yaml

Sample Template


id: ssh-auth-methods

info:
  name: SSH Auth Methods - Detection
  author: Ice3man543
  severity: info
  description: |
    SSH (Secure Shell) authentication modes are methods used to verify the identity of users and ensure secure access to remote systems. Common SSH authentication modes include password-based authentication, which relies on a secret passphrase, and public key authentication, which uses cryptographic keys for a more secure and convenient login process. Additionally, multi-factor authentication (MFA) can be employed to enhance security by requiring users to provide multiple forms of authentication, such as a password and a one-time code.
  reference:
    - https://nmap.org/nsedoc/scripts/ssh-auth-methods.html
  metadata:
    max-request: 1
    shodan-query: product:"OpenSSH"
  tags: js,detect,ssh,enum,network

javascript:
  - pre-condition: |
      isPortOpen(Host,Port);
    code: |
      var m = require("nuclei/ssh");
      var c = m.SSHClient();
      var response = c.ConnectSSHInfoMode(Host, Port);
      Export(response);
    args:
      Host: "{{Host}}"
      Port: "222,22",2244

    extractors:
      - type: json
        json:
          - '.UserAuth'


Summary by CodeRabbit

  • New Features

    • Support testing multiple ports in a single request via a comma-separated list; the target URL’s port is also considered automatically.
    • Each unique port is tested independently with per-port progress, outcomes, and error reporting.
    • Per-port inputs and payloads are applied so each port is validated in context.
  • Behavior

    • Per-port tests run sequentially; duplicate ports are ignored.
    • Backward compatible: if no ports are provided, behavior remains unchanged.

@auto-assign auto-assign bot requested a review from dwisiswant0 September 27, 2025 15:49
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 27, 2025

Walkthrough

Adds multi-port support to pkg/protocols/javascript/js.go: ports are parsed from request and target URL, deduplicated, and tests run sequentially per port via a new executeWithResults(port, ...), threading port into input/payload and aggregating per-port results and errors.

Changes

Cohort / File(s) Summary
JavaScript protocol: multi-port sequential execution
pkg/protocols/javascript/js.go
Replace single-port handling with multi-port capability: add getPorts() to parse ports from Port header and target URL; deduplicate ports; iterate ports and invoke executeWithResults(port, ...) per port; thread port into input preparation and UseNetworkPort; validate DSL per port; aggregate per-port errors/results while preserving preconditions, payload handling, logging, and progress events.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Client
  participant JSHandler as js.go
  participant PortParser as getPorts()
  participant PerPortExec as executeWithResults(port)

  Client->>JSHandler: Request (may include Port header / target URL)
  JSHandler->>PortParser: Parse & dedupe ports
  PortParser-->>JSHandler: Unique ports[]

  alt ports found
    loop for each port
      JSHandler->>PerPortExec: executeWithResults(port, ...)
      PerPortExec-->>JSHandler: per-port result / error
    end
    JSHandler-->>Client: aggregated per-port results/errors
  else no ports (fallback)
    JSHandler->>PerPortExec: executeWithResults(single-port flow)
    PerPortExec-->>JSHandler: result / error
    JSHandler-->>Client: result/error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

I hop through ports, one by one,
Mapping tunnels in the sun.
Each hole inspected, neat and smart,
I thread the port into each part.
Tests stitched tight — a rabbit's run. 🐇

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 concisely and accurately reflects the primary change by indicating the introduction of multi-port support for JavaScript templates, aligning directly with the pull request’s objective.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch multiport-js

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0759026 and 8c56052.

📒 Files selected for processing (1)
  • pkg/protocols/javascript/js.go (4 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/javascript/js.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/javascript/js.go
🧬 Code graph analysis (1)
pkg/protocols/javascript/js.go (4)
pkg/protocols/common/contextargs/contextargs.go (1)
  • Context (22-33)
pkg/protocols/network/network.go (1)
  • Request (20-104)
pkg/protocols/protocols.go (2)
  • Request (308-333)
  • OutputEventCallback (336-336)
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). (3)
  • GitHub Check: Tests (ubuntu-latest)
  • GitHub Check: Tests (macOS-latest)
  • GitHub Check: Tests (windows-latest)
🔇 Additional comments (2)
pkg/protocols/javascript/js.go (2)

137-142: LGTM! Port validation correctly handles multiple ports.

The loop properly validates that each port doesn't contain DSL expressions, preventing runtime issues.


304-454: LGTM! Per-port execution correctly threads port through the request flow.

The refactored executeWithResults properly uses the port parameter in UseNetworkPort and maintains the original execution logic. Port values are correctly propagated to payload values and through the entire request lifecycle.


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 f8f89bb and 7e04181.

⛔ Files ignored due to path filters (1)
  • cmd/nuclei/ssh.yaml is excluded by !**/*.yaml
📒 Files selected for processing (1)
  • pkg/protocols/javascript/js.go (3 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/javascript/js.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/javascript/js.go
🧠 Learnings (1)
📚 Learning: 2025-09-10T18:57:51.327Z
Learnt from: Mzack9999
PR: projectdiscovery/nuclei#6465
File: pkg/catalog/loader/loader.go:504-505
Timestamp: 2025-09-10T18:57:51.327Z
Learning: The mapsutil.SyncLockMap from projectdiscovery/utils provides thread-safe operations including Has() and Set() methods that can be used safely in concurrent goroutines without additional synchronization. The separate Has() and Set() calls are internally synchronized by the SyncLockMap implementation.

Applied to files:

  • pkg/protocols/javascript/js.go
🧬 Code graph analysis (1)
pkg/protocols/javascript/js.go (3)
pkg/protocols/common/contextargs/metainput.go (1)
  • MetaInput (18-30)
pkg/protocols/protocols.go (2)
  • Request (308-333)
  • OutputEventCallback (336-336)
pkg/protocols/common/contextargs/contextargs.go (1)
  • Context (22-33)
🪛 GitHub Actions: 🔨 Tests
pkg/protocols/javascript/js.go

[error] 858-858: getExcludePorts is unused (unused)

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: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7e04181 and 0759026.

📒 Files selected for processing (1)
  • pkg/protocols/javascript/js.go (4 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/javascript/js.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/javascript/js.go
🧬 Code graph analysis (1)
pkg/protocols/javascript/js.go (2)
pkg/protocols/network/network.go (1)
  • Request (20-104)
pkg/protocols/protocols.go (2)
  • Request (308-333)
  • OutputEventCallback (336-336)
🔇 Additional comments (2)
pkg/protocols/javascript/js.go (2)

39-39: LGTM!

The sliceutil import is appropriately added to support port deduplication at line 780.


137-142: LGTM!

The validation correctly iterates over all ports to ensure none contain DSL expressions, maintaining the security constraint.

Comment on lines 288 to 299
// Get default port(s) if specified in template
ports := request.getPorts()

for _, port := range ports {
err := request.executeWithResults(port, target, dynamicValues, previous, callback)
if err != nil {
return err
}
}

return 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

Critical: Templates without Port won't execute.

When getPorts() returns an empty slice (no Port specified in template args), the loop doesn't execute and the function returns immediately. This breaks existing JavaScript templates that don't specify a Port argument.

Apply this diff to execute once with an empty port when no ports are specified:

 func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
 	// Get default port(s) if specified in template
 	ports := request.getPorts()
+	if len(ports) == 0 {
+		ports = []string{""}
+	}
 
 	for _, port := range ports {
 		err := request.executeWithResults(port, target, dynamicValues, previous, callback)
 		if err != nil {
 			return err
 		}
 	}
 
 	return nil
 }
📝 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
// Get default port(s) if specified in template
ports := request.getPorts()
for _, port := range ports {
err := request.executeWithResults(port, target, dynamicValues, previous, callback)
if err != nil {
return err
}
}
return nil
}
func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
// Get default port(s) if specified in template
ports := request.getPorts()
if len(ports) == 0 {
ports = []string{""}
}
for _, port := range ports {
err := request.executeWithResults(port, target, dynamicValues, previous, callback)
if err != nil {
return err
}
}
return nil
}
🤖 Prompt for AI Agents
In pkg/protocols/javascript/js.go around lines 288 to 299, the current logic
returns immediately when request.getPorts() yields an empty slice, which
prevents templates without a Port arg from running; change the flow so that if
ports is empty you call request.executeWithResults once with an empty-string (or
zero-value) port (preserving the same target, dynamicValues, previous, callback)
and return any error; otherwise keep iterating over ports and return any error
from each call. Ensure you do not alter error handling semantics and that a
single execution occurs when no ports are specified.

Comment on lines +776 to 784
func (request *Request) getPorts() []string {
for k, v := range request.Args {
if strings.EqualFold(k, "Port") {
return types.ToString(v)
ports := types.ToStringSlice(strings.Split(types.ToString(v), ","))
return sliceutil.Dedupe(ports)
}
}
return ""
return []string{}
}
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 | 🟠 Major

Simplify port parsing and add whitespace trimming.

Two issues with the current implementation:

  1. types.ToStringSlice is redundant since strings.Split already returns []string
  2. Port values aren't trimmed, so "22, 80, 443" would parse as ["22", " 80", " 443"] with leading spaces causing invalid ports

Apply this diff to fix both issues:

 func (request *Request) getPorts() []string {
 	for k, v := range request.Args {
 		if strings.EqualFold(k, "Port") {
-			ports := types.ToStringSlice(strings.Split(types.ToString(v), ","))
-			return sliceutil.Dedupe(ports)
+			portStr := types.ToString(v)
+			ports := []string{}
+			for _, p := range strings.Split(portStr, ",") {
+				trimmed := strings.TrimSpace(p)
+				if trimmed != "" {
+					ports = append(ports, trimmed)
+				}
+			}
+			return sliceutil.Dedupe(ports)
 		}
 	}
 	return []string{}
 }
📝 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 (request *Request) getPorts() []string {
for k, v := range request.Args {
if strings.EqualFold(k, "Port") {
return types.ToString(v)
ports := types.ToStringSlice(strings.Split(types.ToString(v), ","))
return sliceutil.Dedupe(ports)
}
}
return ""
return []string{}
}
func (request *Request) getPorts() []string {
for k, v := range request.Args {
if strings.EqualFold(k, "Port") {
portStr := types.ToString(v)
ports := []string{}
for _, p := range strings.Split(portStr, ",") {
trimmed := strings.TrimSpace(p)
if trimmed != "" {
ports = append(ports, trimmed)
}
}
return sliceutil.Dedupe(ports)
}
}
return []string{}
}
🤖 Prompt for AI Agents
In pkg/protocols/javascript/js.go around lines 776 to 784, simplify port parsing
by removing the redundant types.ToStringSlice call and trim whitespace for each
split port; when you find the "Port" arg, call strings.Split(types.ToString(v),
",") to get []string, iterate over the slice trimming each element with
strings.TrimSpace, skip empty entries, collect cleaned ports into a []string,
then return sliceutil.Dedupe(cleanedPorts); keep the existing strings.EqualFold
key check and return an empty slice if no Port arg is found.

@Mzack9999
Copy link
Member

@CodeRabbit create an issue for this PR. It should be about adding the capability to support multi default ports in javascript templates

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 6, 2025

Tip

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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Create a GitHub issue for multi default ports support in JavaScript templates

gh issue create \
  --title "Add support for multiple default ports in JavaScript templates" \
  --body "This issue tracks the capability to support multiple default ports in JavaScript templates within the Nuclei engine.

**Context:**
This enhancement is being implemented in PR #6501 by @pussycat0x.

**Related Links:**
- Pull Request: https://github.com/projectdiscovery/nuclei/pull/6501
- Requested by: @Mzack9999 in https://github.com/projectdiscovery/nuclei/pull/6501#discussion_r1938741028

**Description:**
The capability allows JavaScript templates to specify and test multiple ports, enabling more flexible network protocol testing scenarios. This is particularly useful for protocols that may run on various default ports (e.g., SSH on ports 22, 222, 2244).

**Example Usage:**
Templates can now specify multiple ports in the Port argument, and the engine will test each port accordingly.

cc: @pussycat0x @Mzack9999" \
  --assignee "Mzack9999"

Length of output: 54


@Mzack9999 I've created issue #6513 to track the capability for supporting multiple default ports in JavaScript templates.

Issue: #6513

The issue includes context about this PR, backlinks, and has been assigned to you.

@Mzack9999 Mzack9999 linked an issue Oct 6, 2025 that may be closed by this pull request
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.

Add support for multiple default ports in JavaScript templates

3 participants