Skip to content
33 changes: 33 additions & 0 deletions cmd/integration-test/javascript.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

var jsTestcases = []TestCaseInfo{
{Path: "protocols/javascript/redis-pass-brute.yaml", TestCase: &javascriptRedisPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/redis-lua-script.yaml", TestCase: &javascriptRedisLuaScript{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/ssh-server-fingerprint.yaml", TestCase: &javascriptSSHServerFingerprint{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/net-multi-step.yaml", TestCase: &networkMultiStep{}},
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
Expand Down Expand Up @@ -70,6 +71,38 @@ func (j *javascriptRedisPassBrute) Execute(filePath string) error {
return multierr.Combine(errs...)
}

type javascriptRedisLuaScript struct{}

func (j *javascriptRedisLuaScript) Execute(filePath string) error {
if redisResource == nil || pool == nil {
// skip test as redis is not running
return nil
}
tempPort := redisResource.GetPort("6379/tcp")
finalURL := "localhost:" + tempPort
defer purge(redisResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let ssh server start
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
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...)
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

type javascriptSSHServerFingerprint struct{}

func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
Expand Down
27 changes: 27 additions & 0 deletions integration_tests/protocols/javascript/redis-lua-script.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
id: redis-lua-script

info:
name: Redis RunLuaScript - Detect
author: DhiyaneshDK
severity: info

javascript:
- code: |
const redis = require('nuclei/redis');
// First call: Set a key-value pair using ARGV
const setResult = redis.RunLuaScript(Host, Port, Password, 'return redis.call("set", ARGV[1], ARGV[2])', [], ['testkey', 'testvalue']);
log(to_json(setResult));

// Second call: Retrieve the value we just set
const getResult = redis.RunLuaScript(Host, Port, Password, 'return redis.call("get", ARGV[1])', [], ['testkey']);
log(to_json(getResult));

args:
Host: "{{Host}}"
Port: "6379"
Password: ""

matchers:
- type: dsl
dsl:
- "success == true"
7 changes: 5 additions & 2 deletions pkg/js/generated/ts/redis.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,10 +61,13 @@ export function IsAuthenticated(host: string, port: number): boolean | null {
* @example
* ```javascript
* const redis = require('nuclei/redis');
* const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("get", KEYS[1])');
* // Old signature (backwards compatible) - keys and args are optional
* const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("ping")');
* // New signature with keys and args
* const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("get", KEYS[1])', ['mykey'], []);
* ```
*/
export function RunLuaScript(host: string, port: number, password: string, script: string): any | null {
export function RunLuaScript(host: string, port: number, password: string, script: string, keys?: string[] | any, args?: string[] | any): any | null {
return null;
}

51 changes: 47 additions & 4 deletions pkg/js/libs/redis/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -175,9 +175,12 @@ func isAuthenticated(executionId string, host string, port int) (bool, error) {
// @example
// ```javascript
// const redis = require('nuclei/redis');
// const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("get", KEYS[1])');
// // Old signature (backwards compatible) - keys and args are optional
// const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("ping")');
// // New signature with keys and args
// const result = redis.RunLuaScript('acme.com', 6379, 'password', 'return redis.call("get", KEYS[1])', ['mykey'], []);
// ```
func RunLuaScript(ctx context.Context, host string, port int, password string, script string) (interface{}, error) {
func RunLuaScript(ctx context.Context, host string, port int, password string, script string, keys interface{}, args interface{}) (interface{}, error) {
executionId := ctx.Value("executionId").(string)
if !protocolstate.IsHostAllowed(executionId, host) {
// host is not valid according to network policy
Expand All @@ -199,8 +202,48 @@ func RunLuaScript(ctx context.Context, host string, port int, password string, s
return "", err
}

// Get Redis server info
infoCmd := client.Eval(context.Background(), script, []string{})
// Convert interface{} to []string for keys (handle backwards compatibility)
keysSlice := []string{}
if keys != nil {
switch v := keys.(type) {
case []string:
keysSlice = v
case []interface{}:
// Convert []interface{} to []string (from JavaScript arrays)
keysSlice = make([]string, 0, len(v))
for _, item := range v {
if s, ok := item.(string); ok {
keysSlice = append(keysSlice, s)
}
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.

// Convert interface{} to []string for args (handle backwards compatibility)
argsSlice := []string{}
if args != nil {
switch v := args.(type) {
case []string:
argsSlice = v
case []interface{}:
// Convert []interface{} to []string (from JavaScript arrays)
argsSlice = make([]string, 0, len(v))
for _, item := range v {
if s, ok := item.(string); ok {
argsSlice = append(argsSlice, s)
}
}
}
}
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated

// Convert []string args to []interface{} for Eval
argsInterface := make([]interface{}, len(argsSlice))
for i, arg := range argsSlice {
argsInterface[i] = arg
}

// Execute the Lua script with keys and args
infoCmd := client.Eval(context.Background(), script, keysSlice, argsInterface...)

if infoCmd.Err() != nil {
return "", infoCmd.Err()
Expand Down
Loading