Skip to content
Merged
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
12 changes: 12 additions & 0 deletions cmd/integration-test/javascript.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ var jsTestcases = []TestCaseInfo{
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
{Path: "protocols/javascript/oracle-auth-test.yaml", TestCase: &javascriptOracleAuthTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/vnc-pass-brute.yaml", TestCase: &javascriptVncPassBrute{}},
{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
}

var (
Expand Down Expand Up @@ -167,6 +168,17 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error {
return multierr.Combine(errs...)
}

type javascriptMultiPortsSSH struct{}

func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
// use scanme.sh as target to ensure we match on the 2nd default port 22
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
Comment on lines 173 to 180
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

Multiple issues with executor implementation.

The javascriptMultiPortsSSH executor has several problems that deviate from the established pattern:

  1. Line 175: Checks sshResource instead of multiPortsSShResource, which creates inconsistency with the declared variable at line 28.

  2. Line 176: Copy-paste error in comment—says "redis" but should say "SSH" or "multi-port SSH".

  3. Line 179: Uses hardcoded "scanme.sh" instead of extracting a port from a Docker resource like all other executors (see lines 50-51, 82-83, 114-115, 147-148). This breaks the established pattern of testing against local Docker containers.

  4. Missing defer purge(): Unlike all other executors (lines 52, 84, 116, 149), this executor doesn't clean up the Docker resource, potentially causing resource leaks.

Apply this diff to align with the established pattern (assuming you want to use the existing sshResource):

 func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
 	if sshResource == nil || pool == nil {
-		// skip test as redis is not running
+		// skip test as ssh is not running
 		return nil
 	}
-	finalURL := "scanme.sh"
+	tempPort := sshResource.GetPort("2222/tcp")
+	finalURL := "localhost:" + tempPort
+	defer purge(sshResource)
 	errs := []error{}

Alternatively, if the test genuinely needs to target the external scanme.sh service (which seems inconsistent with an integration test suite using Docker), please clarify the intent and consider whether this should be a separate test type.


// purge any given resource if it is not nil
func purge(resource *dockertest.Resource) {
if resource != nil && pool != nil {
Expand Down
28 changes: 28 additions & 0 deletions integration_tests/protocols/javascript/multi-ports.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
id: multi-ports

info:
name: Multi Ports - Detection
author: pdteam
severity: info
description: |
Multi Ports template for testing
metadata:
max-request: 1
tags: js,detect,multi-ports,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: "2222,22" # Port 22 should match

extractors:
- type: json
json:
- '.UserAuth'
40 changes: 34 additions & 6 deletions pkg/protocols/javascript/js.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"github.com/projectdiscovery/utils/errkit"
iputil "github.com/projectdiscovery/utils/ip"
mapsutil "github.com/projectdiscovery/utils/maps"
sliceutil "github.com/projectdiscovery/utils/slice"
syncutil "github.com/projectdiscovery/utils/sync"
urlutil "github.com/projectdiscovery/utils/url"
)
Expand Down Expand Up @@ -133,8 +134,11 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
}

// "Port" is a special variable and it should not contains any dsl expressions
if strings.Contains(request.getPort(), "{{") {
return errkit.New("'Port' variable cannot contain any dsl expressions")
ports := request.getPorts()
for _, port := range ports {
if strings.Contains(port, "{{") {
return errkit.New("'Port' variable cannot contain any dsl expressions")
}
}

if request.Init != "" {
Expand Down Expand Up @@ -281,12 +285,28 @@ func (request *Request) GetID() string {

// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
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()

var errs []error

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

return errkit.Join(errs...)
}

// executeWithResults executes the request
func (request *Request) executeWithResults(port string, target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
input := target.Clone()
// 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(request.getPort(), request.getExcludePorts()); err != nil {
if err := input.UseNetworkPort(port, request.getExcludePorts()); err != nil {
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
}

Expand Down Expand Up @@ -755,13 +775,21 @@ func (request *Request) Type() templateTypes.ProtocolType {
return templateTypes.JavascriptProtocol
}

func (request *Request) getPort() string {
func (request *Request) getPorts() []string {
for k, v := range request.Args {
if strings.EqualFold(k, "Port") {
return types.ToString(v)
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 ""
return []string{}
}

func (request *Request) getExcludePorts() string {
Expand Down
Loading