From 6a6de384fca77752a940c7d07c4c118a92e46b11 Mon Sep 17 00:00:00 2001 From: pussycat0x <65701233+pussycat0x@users.noreply.github.com> Date: Sat, 27 Sep 2025 21:00:13 +0530 Subject: [PATCH 1/9] Multi Port Support Added - JS --- cmd/nuclei/ssh.yaml | 31 +++++++++++++++ pkg/protocols/javascript/js.go | 70 +++++++++++++++++++++++++++++++++- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 cmd/nuclei/ssh.yaml diff --git a/cmd/nuclei/ssh.yaml b/cmd/nuclei/ssh.yaml new file mode 100644 index 0000000000..0c5719cb13 --- /dev/null +++ b/cmd/nuclei/ssh.yaml @@ -0,0 +1,31 @@ +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,2222,22" + + extractors: + - type: json + json: + - '.UserAuth' \ No newline at end of file diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index 8b872d84a9..ce97583d3f 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -282,11 +282,60 @@ 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 all ports to test + ports := request.getPorts() + + // Check if target already has a specific port (from URL like host:port) + targetURL, err := urlutil.Parse(target.MetaInput.Input) + var urlPort string + if err == nil { + urlPort = targetURL.Port() + } + + // Create a map to track unique ports and avoid duplicates + uniquePorts := make(map[string]bool) + + // Add URL port if it exists + if urlPort != "" { + uniquePorts[urlPort] = true + } + + // Add template ports + for _, port := range ports { + uniquePorts[port] = true + } + + // If no ports found, fallback to single port behavior + if len(uniquePorts) == 0 { + return request.executeWithSinglePort(target, dynamicValues, previous, callback) + } + + // Execute for each unique port + for port := range uniquePorts { + 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(port, request.getExcludePorts()); err != nil { + gologger.Debug().Msgf("Could not network port from constants: %s\n", err) + } + + if err := request.executeWithSinglePort(input, dynamicValues, previous, callback); err != nil { + gologger.Debug().Msgf("Error executing request for port %s: %s\n", port, err) + } + } + + return nil +} + +// executeWithSinglePort executes the request for a single port +func (request *Request) executeWithSinglePort(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 { + // Note: target already has the correct port set from the multi-port loop or from URL + if err := input.UseNetworkPort("", request.getExcludePorts()); err != nil { gologger.Debug().Msgf("Could not network port from constants: %s\n", err) } @@ -764,6 +813,25 @@ func (request *Request) getPort() string { return "" } +// getPorts returns a slice of ports from the Port argument +func (request *Request) getPorts() []string { + portStr := request.getPort() + if portStr == "" { + return []string{} + } + + // Split by comma and clean up whitespace + ports := strings.Split(portStr, ",") + var cleanedPorts []string + for _, port := range ports { + cleaned := strings.TrimSpace(port) + if cleaned != "" { + cleanedPorts = append(cleanedPorts, cleaned) + } + } + return cleanedPorts +} + func (request *Request) getExcludePorts() string { for k, v := range request.Args { if strings.EqualFold(k, "exclude-ports") { From 7e041813910f900d703a085074a6d8e14fdd4c85 Mon Sep 17 00:00:00 2001 From: pussycat0x <65701233+pussycat0x@users.noreply.github.com> Date: Sat, 27 Sep 2025 21:14:20 +0530 Subject: [PATCH 2/9] minor -changes --- cmd/nuclei/ssh.yaml | 2 +- pkg/protocols/javascript/js.go | 59 +++++++++++++++++++++++----------- 2 files changed, 42 insertions(+), 19 deletions(-) diff --git a/cmd/nuclei/ssh.yaml b/cmd/nuclei/ssh.yaml index 0c5719cb13..afc5115d8b 100644 --- a/cmd/nuclei/ssh.yaml +++ b/cmd/nuclei/ssh.yaml @@ -23,7 +23,7 @@ javascript: Export(response); args: Host: "{{Host}}" - Port: "222,2222,22" + Port: "222,22" extractors: - type: json diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index ce97583d3f..35c6c2f335 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -7,6 +7,7 @@ import ( "maps" "net" "strings" + "sync" "sync/atomic" "time" @@ -310,34 +311,56 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV return request.executeWithSinglePort(target, dynamicValues, previous, callback) } - // Execute for each unique port + // Execute for each unique port in parallel + var wg sync.WaitGroup + portList := make([]string, 0, len(uniquePorts)) for port := range uniquePorts { - 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(port, request.getExcludePorts()); err != nil { - gologger.Debug().Msgf("Could not network port from constants: %s\n", err) - } + portList = append(portList, port) + } - if err := request.executeWithSinglePort(input, dynamicValues, previous, callback); err != nil { - gologger.Debug().Msgf("Error executing request for port %s: %s\n", port, err) - } + // Use a semaphore to limit concurrent goroutines (max 10 concurrent port tests) + semaphore := make(chan struct{}, 10) + + for _, port := range portList { + wg.Add(1) + go func(port string) { + defer wg.Done() + + // Acquire semaphore + semaphore <- struct{}{} + defer func() { <-semaphore }() + + gologger.Debug().Msgf("Testing port: %s\n", port) + input := target.Clone() + + // Parse the original input to get hostname + originalURL, err := urlutil.Parse(target.MetaInput.Input) + if err != nil { + gologger.Debug().Msgf("Could not parse original input: %s\n", err) + return + } + + // Create new input with the specific port + newInput := fmt.Sprintf("%s:%s", originalURL.Hostname(), port) + input.MetaInput.Input = newInput + + if err := request.executeWithSinglePort(input, dynamicValues, previous, callback); err != nil { + gologger.Debug().Msgf("Error executing request for port %s: %s\n", port, err) + } + }(port) } + // Wait for all goroutines to complete + wg.Wait() + return nil } // executeWithSinglePort executes the request for a single port func (request *Request) executeWithSinglePort(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 - // Note: target already has the correct port set from the multi-port loop or from URL - if err := input.UseNetworkPort("", request.getExcludePorts()); err != nil { - gologger.Debug().Msgf("Could not network port from constants: %s\n", err) - } + // Note: target already has the correct port set from the multi-port loop + // No need to call UseNetworkPort again as the port is already set in the target hostPort, err := getAddress(input.MetaInput.Input) if err != nil { From 07590268c1207f96e78efbc50fae6dc2d0bf02dc Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 6 Oct 2025 16:10:58 +0200 Subject: [PATCH 3/9] restoring basic sequential multiport support --- pkg/protocols/javascript/js.go | 118 +++++++-------------------------- 1 file changed, 23 insertions(+), 95 deletions(-) diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index 35c6c2f335..b75e26eada 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -7,7 +7,6 @@ import ( "maps" "net" "strings" - "sync" "sync/atomic" "time" @@ -37,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" ) @@ -134,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 != "" { @@ -282,85 +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 all ports to test + // Get default port(s) if specified in template ports := request.getPorts() - // Check if target already has a specific port (from URL like host:port) - targetURL, err := urlutil.Parse(target.MetaInput.Input) - var urlPort string - if err == nil { - urlPort = targetURL.Port() - } - - // Create a map to track unique ports and avoid duplicates - uniquePorts := make(map[string]bool) - - // Add URL port if it exists - if urlPort != "" { - uniquePorts[urlPort] = true - } - - // Add template ports for _, port := range ports { - uniquePorts[port] = true - } - - // If no ports found, fallback to single port behavior - if len(uniquePorts) == 0 { - return request.executeWithSinglePort(target, dynamicValues, previous, callback) - } - - // Execute for each unique port in parallel - var wg sync.WaitGroup - portList := make([]string, 0, len(uniquePorts)) - for port := range uniquePorts { - portList = append(portList, port) - } - - // Use a semaphore to limit concurrent goroutines (max 10 concurrent port tests) - semaphore := make(chan struct{}, 10) - - for _, port := range portList { - wg.Add(1) - go func(port string) { - defer wg.Done() - - // Acquire semaphore - semaphore <- struct{}{} - defer func() { <-semaphore }() - - gologger.Debug().Msgf("Testing port: %s\n", port) - input := target.Clone() - - // Parse the original input to get hostname - originalURL, err := urlutil.Parse(target.MetaInput.Input) - if err != nil { - gologger.Debug().Msgf("Could not parse original input: %s\n", err) - return - } - - // Create new input with the specific port - newInput := fmt.Sprintf("%s:%s", originalURL.Hostname(), port) - input.MetaInput.Input = newInput - - if err := request.executeWithSinglePort(input, dynamicValues, previous, callback); err != nil { - gologger.Debug().Msgf("Error executing request for port %s: %s\n", port, err) - } - }(port) + err := request.executeWithResults(port, target, dynamicValues, previous, callback) + if err != nil { + return err + } } - // Wait for all goroutines to complete - wg.Wait() - return nil } -// executeWithSinglePort executes the request for a single port -func (request *Request) executeWithSinglePort(target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error { +// executeWithResults executes the request +func (request *Request) executeWithResults(port string, target *contextargs.Context, dynamicValues, previous output.InternalEvent, callback protocols.OutputEventCallback) error { input := target.Clone() - // Note: target already has the correct port set from the multi-port loop - // No need to call UseNetworkPort again as the port is already set in the target + // 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 { + gologger.Debug().Msgf("Could not network port from constants: %s\n", err) + } hostPort, err := getAddress(input.MetaInput.Input) if err != nil { @@ -827,32 +773,14 @@ 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) - } - } - return "" -} - -// getPorts returns a slice of ports from the Port argument -func (request *Request) getPorts() []string { - portStr := request.getPort() - if portStr == "" { - return []string{} - } - - // Split by comma and clean up whitespace - ports := strings.Split(portStr, ",") - var cleanedPorts []string - for _, port := range ports { - cleaned := strings.TrimSpace(port) - if cleaned != "" { - cleanedPorts = append(cleanedPorts, cleaned) + ports := types.ToStringSlice(strings.Split(types.ToString(v), ",")) + return sliceutil.Dedupe(ports) } } - return cleanedPorts + return []string{} } func (request *Request) getExcludePorts() string { From 8c560523e3fefb9bd70e47f59a691f40aca63a38 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Mon, 6 Oct 2025 21:29:56 +0200 Subject: [PATCH 4/9] better error handling --- pkg/protocols/javascript/js.go | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index b75e26eada..5bfd660c91 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -288,14 +288,16 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV // 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 { - return err + errs = append(errs, err) } } - return nil + return errkit.Join(errs...) } // executeWithResults executes the request From 9b37f1b4417e50b354080eeb2811b513dd248bbb Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 18 Nov 2025 21:43:28 +0400 Subject: [PATCH 5/9] adding test case --- cmd/integration-test/javascript.go | 44 ++++++++++++++++++++++++++---- cmd/nuclei/ssh.yaml | 31 --------------------- pkg/protocols/javascript/js.go | 9 +++++- 3 files changed, 46 insertions(+), 38 deletions(-) delete mode 100644 cmd/nuclei/ssh.yaml diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index 6e99b7f844..3fb7e1baa9 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -17,15 +17,17 @@ 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 ( - redisResource *dockertest.Resource - sshResource *dockertest.Resource - oracleResource *dockertest.Resource - vncResource *dockertest.Resource - pool *dockertest.Pool - defaultRetry = 3 + redisResource *dockertest.Resource + sshResource *dockertest.Resource + oracleResource *dockertest.Resource + vncResource *dockertest.Resource + multiPortsSShResource *dockertest.Resource + pool *dockertest.Pool + defaultRetry = 3 ) type javascriptNetHttps struct{} @@ -167,6 +169,36 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error { return multierr.Combine(errs...) } +type javascriptMultiPortsSSH struct{} + +func (j *javascriptMultiPortsSSH) Execute(filePath string) error { + if sshResource == nil || pool == nil { + // skip test as redis is not running + return nil + } + finalURL := "scanme.sh" + errs := []error{} + for i := 0; i < defaultRetry; i++ { + results := []string{} + var err error + _ = pool.Retry(func() error { + //let ssh server start + time.Sleep(3 * time.Second) + results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug) + return nil + }) + if err != nil { + return err + } + if err := expectResultsCount(results, 1); err == nil { + return nil + } else { + errs = append(errs, err) + } + } + return multierr.Combine(errs...) +} + // purge any given resource if it is not nil func purge(resource *dockertest.Resource) { if resource != nil && pool != nil { diff --git a/cmd/nuclei/ssh.yaml b/cmd/nuclei/ssh.yaml deleted file mode 100644 index afc5115d8b..0000000000 --- a/cmd/nuclei/ssh.yaml +++ /dev/null @@ -1,31 +0,0 @@ -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" - - extractors: - - type: json - json: - - '.UserAuth' \ No newline at end of file diff --git a/pkg/protocols/javascript/js.go b/pkg/protocols/javascript/js.go index 5bfd660c91..2d927555fc 100644 --- a/pkg/protocols/javascript/js.go +++ b/pkg/protocols/javascript/js.go @@ -778,7 +778,14 @@ func (request *Request) Type() templateTypes.ProtocolType { func (request *Request) getPorts() []string { for k, v := range request.Args { if strings.EqualFold(k, "Port") { - ports := types.ToStringSlice(strings.Split(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) } } From c746a8fdae6e2616d3701bc9e2b8032888f209bb Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 18 Nov 2025 22:20:17 +0400 Subject: [PATCH 6/9] lint --- cmd/integration-test/javascript.go | 13 ++++++------- lib/example_test.go | 1 - pkg/operators/common/dsl/dsl.go | 2 +- pkg/scan/events/scan_noop.go | 1 - pkg/scan/events/stats_build.go | 1 - pkg/utils/json/json.go | 2 -- pkg/utils/json/json_fallback.go | 1 - 7 files changed, 7 insertions(+), 14 deletions(-) diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index 3fb7e1baa9..b7a8ad6b85 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -21,13 +21,12 @@ var jsTestcases = []TestCaseInfo{ } var ( - redisResource *dockertest.Resource - sshResource *dockertest.Resource - oracleResource *dockertest.Resource - vncResource *dockertest.Resource - multiPortsSShResource *dockertest.Resource - pool *dockertest.Pool - defaultRetry = 3 + redisResource *dockertest.Resource + sshResource *dockertest.Resource + oracleResource *dockertest.Resource + vncResource *dockertest.Resource + pool *dockertest.Pool + defaultRetry = 3 ) type javascriptNetHttps struct{} diff --git a/lib/example_test.go b/lib/example_test.go index 1d82073e1f..81c7fc106e 100644 --- a/lib/example_test.go +++ b/lib/example_test.go @@ -1,5 +1,4 @@ //go:build !race -// +build !race package nuclei_test diff --git a/pkg/operators/common/dsl/dsl.go b/pkg/operators/common/dsl/dsl.go index a424790d55..10c1782ec7 100644 --- a/pkg/operators/common/dsl/dsl.go +++ b/pkg/operators/common/dsl/dsl.go @@ -115,7 +115,7 @@ func init() { })) dsl.PrintDebugCallback = func(args ...interface{}) error { - gologger.Debug().Msgf("print_debug value: %s", fmt.Sprint(args)) + gologger.Debug().Msgf("print_debug value: %s", fmt.Sprint(args)) //nolint return nil } diff --git a/pkg/scan/events/scan_noop.go b/pkg/scan/events/scan_noop.go index 055baed4ee..2815bf183e 100644 --- a/pkg/scan/events/scan_noop.go +++ b/pkg/scan/events/scan_noop.go @@ -1,5 +1,4 @@ //go:build !stats -// +build !stats package events diff --git a/pkg/scan/events/stats_build.go b/pkg/scan/events/stats_build.go index 7d03e42e52..43e67865ee 100644 --- a/pkg/scan/events/stats_build.go +++ b/pkg/scan/events/stats_build.go @@ -1,5 +1,4 @@ //go:build stats -// +build stats package events diff --git a/pkg/utils/json/json.go b/pkg/utils/json/json.go index 0546406959..515ca3e9ed 100644 --- a/pkg/utils/json/json.go +++ b/pkg/utils/json/json.go @@ -1,6 +1,4 @@ //go:build (linux || darwin || windows) && (amd64 || arm64) -// +build linux darwin windows -// +build amd64 arm64 package json diff --git a/pkg/utils/json/json_fallback.go b/pkg/utils/json/json_fallback.go index 3c87dd9220..2e8bf98e01 100644 --- a/pkg/utils/json/json_fallback.go +++ b/pkg/utils/json/json_fallback.go @@ -1,5 +1,4 @@ //go:build !(linux || darwin || windows) || !(amd64 || arm64) -// +build !linux,!darwin,!windows !amd64,!arm64 package json From 232de93297b11bf4490b6a70c3e4e95e47104f70 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 18 Nov 2025 22:21:13 +0400 Subject: [PATCH 7/9] removing unused check --- cmd/integration-test/javascript.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index b7a8ad6b85..4b96bd8d9c 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -171,10 +171,6 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error { type javascriptMultiPortsSSH struct{} func (j *javascriptMultiPortsSSH) Execute(filePath string) error { - if sshResource == nil || pool == nil { - // skip test as redis is not running - return nil - } finalURL := "scanme.sh" errs := []error{} for i := 0; i < defaultRetry; i++ { From 761c7c0c296c1b61ea78764ba696a7ca6abfb0fc Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Tue, 18 Nov 2025 22:23:35 +0400 Subject: [PATCH 8/9] adding multiport template --- .../protocols/javascript/multi-ports.yaml | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 integration_tests/protocols/javascript/multi-ports.yaml diff --git a/integration_tests/protocols/javascript/multi-ports.yaml b/integration_tests/protocols/javascript/multi-ports.yaml new file mode 100644 index 0000000000..aa1c5fe2d9 --- /dev/null +++ b/integration_tests/protocols/javascript/multi-ports.yaml @@ -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' \ No newline at end of file From 3ea8dc29c38ddaa2fed732089f122aea27040204 Mon Sep 17 00:00:00 2001 From: Mzack9999 Date: Wed, 19 Nov 2025 13:28:32 +0400 Subject: [PATCH 9/9] refactor test --- cmd/integration-test/javascript.go | 25 +++++-------------------- 1 file changed, 5 insertions(+), 20 deletions(-) diff --git a/cmd/integration-test/javascript.go b/cmd/integration-test/javascript.go index 4b96bd8d9c..c0b8c19900 100644 --- a/cmd/integration-test/javascript.go +++ b/cmd/integration-test/javascript.go @@ -171,27 +171,12 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error { type javascriptMultiPortsSSH struct{} func (j *javascriptMultiPortsSSH) Execute(filePath string) error { - finalURL := "scanme.sh" - errs := []error{} - for i := 0; i < defaultRetry; i++ { - results := []string{} - var err error - _ = pool.Retry(func() error { - //let ssh server start - time.Sleep(3 * time.Second) - results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug) - return nil - }) - if err != nil { - return err - } - if err := expectResultsCount(results, 1); err == nil { - return nil - } else { - errs = append(errs, err) - } + // 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 multierr.Combine(errs...) + return expectResultsCount(results, 1) } // purge any given resource if it is not nil