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
51 changes: 51 additions & 0 deletions cmd/integration-test/javascript.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,13 @@ var jsTestcases = []TestCaseInfo{
{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{}},
{Path: "protocols/javascript/rsync-test.yaml", TestCase: &javascriptRsyncTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Sanity-check that the template matches container credentials and module.

Ensure protocols/javascript/rsync-test.yaml uses:

  • user: rsyncuser
  • pass: MySecret123
  • module: data
  • target: host:port provided here

🏁 Script executed:

#!/bin/bash
set -euo pipefail
rg -nC2 -e 'rsyncuser' -e 'MySecret123' -e '\bdata\b' protocols/javascript/rsync-test.yaml || {
  echo "Note: verify the template uses the expected credentials/module."
}

Length of output: 322


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate any rsync-test.yaml files in the repo
paths=$(fd rsync-test.yaml)

if [ -z "$paths" ]; then
  echo "No rsync-test.yaml file found in the repository."
  exit 0
fi

# For each found file, verify the expected credentials/module
for file in $paths; do
  echo "Checking $file:"
  rg -nC2 -e 'user:\s*rsyncuser' -e 'pass:\s*MySecret123' -e 'module:\s*data' "$file" \
    || echo "⚠️ Note: verify that $file uses user: rsyncuser, pass: MySecret123, module: data."
done

Length of output: 182


Critical: Verify existence of the referenced YAML template

It appears the integration test is pointing at protocols/javascript/rsync-test.yaml, but no such file is present in the repository. This will cause test discovery to fail.

• File: cmd/integration-test/javascript.go
Line: 18

{Path: "protocols/javascript/rsync-test.yaml", TestCase: &javascriptRsyncTest{}, DisableOn: …},

Action required:

  • Confirm whether the YAML lives under a different path and update the Path accordingly, or
  • Add the missing protocols/javascript/rsync-test.yaml template (ensuring it uses user: rsyncuser, pass: MySecret123, module: data, and the correct target).
🤖 Prompt for AI Agents
In cmd/integration-test/javascript.go around line 18, the test references
protocols/javascript/rsync-test.yaml which is missing from the repo; update the
Path to the actual location if the YAML exists elsewhere or add the missing file
at protocols/javascript/rsync-test.yaml containing a valid rsync template that
includes user: rsyncuser, pass: MySecret123, module: data and the correct target
(ensure the target matches the integration test expectations), then run test
discovery to confirm the test picks up the template.

}

var (
redisResource *dockertest.Resource
sshResource *dockertest.Resource
rsyncResource *dockertest.Resource
pool *dockertest.Pool
defaultRetry = 3
)
Expand Down Expand Up @@ -98,6 +100,38 @@ func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
return multierr.Combine(errs...)
}

type javascriptRsyncTest struct{}

func (j *javascriptRsyncTest) Execute(filePath string) error {
if rsyncResource == nil || pool == nil {
// skip test as rsync is not running
return nil
}
tempPort := rsyncResource.GetPort("873/tcp")
finalURL := "localhost:" + tempPort
defer purge(rsyncResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let rsync 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 {
Expand Down Expand Up @@ -163,4 +197,21 @@ func init() {
if err := sshResource.Expire(30); err != nil {
log.Printf("Could not expire resource: %s", err)
}

// setup a temporary rsync server
rsyncResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "alpine",
Tag: "latest",
Cmd: []string{"sh", "-c", "apk add --no-cache rsync shadow && useradd -m rsyncuser && echo 'rsyncuser:mysecret' | chpasswd && echo 'rsyncuser:MySecret123' > /etc/rsyncd.secrets && chmod 600 /etc/rsyncd.secrets && echo -e '[data]\\n path = /data\\n comment = Local Rsync Share\\n read only = false\\n auth users = rsyncuser\\n secrets file = /etc/rsyncd.secrets' > /etc/rsyncd.conf && mkdir -p /data && exec rsync --daemon --no-detach --config=/etc/rsyncd.conf"},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start Rsync resource: %s", err)
return
}
// by default expire after 30 sec
if err := rsyncResource.Expire(30); err != nil {
log.Printf("Could not expire Rsync resource: %s", err)
}

Comment on lines +201 to +216
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

Rsync port 873 isn’t exposed; GetPort("873/tcp") will be empty.

Alpine image doesn’t EXPOSE 873, so no host port is published unless you set it explicitly. Expose and bind 873/tcp.

Apply this diff in the run options:

 rsyncResource, err = pool.RunWithOptions(&dockertest.RunOptions{
   Repository: "alpine",
   Tag:        "latest",
   Cmd:        []string{"sh", "-c", "apk add --no-cache rsync shadow && useradd -m rsyncuser && echo 'rsyncuser:mysecret' | chpasswd && echo 'rsyncuser:MySecret123' > /etc/rsyncd.secrets && chmod 600 /etc/rsyncd.secrets && echo -e '[data]\\n  path = /data\\n  comment = Local Rsync Share\\n  read only = false\\n  auth users = rsyncuser\\n  secrets file = /etc/rsyncd.secrets' > /etc/rsyncd.conf && mkdir -p /data && exec rsync --daemon --no-detach --config=/etc/rsyncd.conf"},
+  ExposedPorts: []string{"873/tcp"},
+  PortBindings: map[docker.Port][]docker.PortBinding{
+    "873/tcp": {{HostIP: "0.0.0.0", HostPort: ""}},
+  },
   Platform:   "linux/amd64",
 })

Also add the import:

// at the top imports
"github.com/ory/dockertest/v3/docker"
🤖 Prompt for AI Agents
In cmd/integration-test/javascript.go around lines 201-216, the rsync container
never exposes or binds port 873 so GetPort("873/tcp") will be empty; update the
RunWithOptions to declare ExposedPorts and HostConfig PortBindings for "873/tcp"
(map container port 873 to a random host port) in the RunOptions by supplying a
docker HostConfig with PortBindings and ExposedPorts, and add the import
"github.com/ory/dockertest/v3/docker" at the top of the file.

}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ require (
github.com/DataDog/gostackparse v0.7.0
github.com/Masterminds/semver/v3 v3.4.0
github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057
github.com/Mzack9999/go-rsync v0.0.0-20250821180103-81ffa574ef4d
github.com/Mzack9999/goja v0.0.0-20250507184235-e46100e9c697
github.com/Mzack9999/goja_nodejs v0.0.0-20250507184139-66bcbf65c883
github.com/alitto/pond v1.9.2
Expand Down Expand Up @@ -255,6 +256,7 @@ require (
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/kaiakz/ubuffer v0.0.0-20200803053910-dd1083087166 // indirect
github.com/kataras/jwt v0.1.10 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,8 @@ github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057 h1:KFac3SiGbId8ub
github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057/go.mod h1:iLB2pivrPICvLOuROKmlqURtFIEsoJZaMidQfCG1+D4=
github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 h1:ZbFL+BDfBqegi+/Ssh7im5+aQfBRx6it+kHnC7jaDU8=
github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809/go.mod h1:upgc3Zs45jBDnBT4tVRgRcgm26ABpaP7MoTSdgysca4=
github.com/Mzack9999/go-rsync v0.0.0-20250821180103-81ffa574ef4d h1:DofPB5AcjTnOU538A/YD86/dfqSNTvQsAXgwagxmpu4=
github.com/Mzack9999/go-rsync v0.0.0-20250821180103-81ffa574ef4d/go.mod h1:uzdh/m6XQJI7qRvufeBPDa+lj5SVCJO8B9eLxTbtI5U=
github.com/Mzack9999/goja v0.0.0-20250507184235-e46100e9c697 h1:54I+OF5vS4a/rxnUrN5J3hi0VEYKcrTlpc8JosDyP+c=
github.com/Mzack9999/goja v0.0.0-20250507184235-e46100e9c697/go.mod h1:yNqYRqxYkSROY1J+LX+A0tOSA/6soXQs5m8hZSqYBac=
github.com/Mzack9999/goja_nodejs v0.0.0-20250507184139-66bcbf65c883 h1:+Is1AS20q3naP+qJophNpxuvx1daFOx9C0kLIuI0GVk=
Expand Down Expand Up @@ -1017,6 +1019,8 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/kaiakz/ubuffer v0.0.0-20200803053910-dd1083087166 h1:IAukUBAVLUWBcexOYgkTD/EjMkfnNos7g7LFpyIdHJI=
github.com/kaiakz/ubuffer v0.0.0-20200803053910-dd1083087166/go.mod h1:T4xUEny5PVedYIbkMAKYEBjMyDsOvvP0qK4s324AKA8=
github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes=
github.com/kataras/jwt v0.1.10 h1:GBXOF9RVInDPhCFBiDumRG9Tt27l7ugLeLo8HL5SeKQ=
Expand Down
21 changes: 21 additions & 0 deletions integration_tests/protocols/javascript/rsync-test.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
id: rsync-test

info:
name: Rsync Test
author: pdteam
severity: info

javascript:
- code: |
const rsync = require('nuclei/rsync');
rsync.IsRsync(Host, Port);

args:
Host: "{{Host}}"
Port: "873"

matchers:
- type: dsl
dsl:
- "success == true"

1 change: 1 addition & 0 deletions pkg/js/generated/go/librsync/rsync.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ func init() {

// Objects / Classes
"IsRsyncResponse": gojs.GetClassConstructor[lib_rsync.IsRsyncResponse](&lib_rsync.IsRsyncResponse{}),
"RsyncClient": gojs.GetClassConstructor[lib_rsync.RsyncClient](&lib_rsync.RsyncClient{}),
},
).Register()
}
Expand Down
56 changes: 55 additions & 1 deletion pkg/js/generated/ts/rsync.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,61 @@ export function IsRsync(host: string, port: number): IsRsyncResponse | null {
return null;
}


/**
* RsyncClient is a client for RSYNC servers.
* Internally client uses https://github.com/gokrazy/rsync driver.
* @example
* ```javascript
* const rsync = require('nuclei/rsync');
* const client = new rsync.RsyncClient();
* ```
*/
export class RsyncClient {

// Constructor of RsyncClient
constructor() {}

/**
* Connect establishes a connection to the rsync server with authentication.
* @example
* ```javascript
* const rsync = require('nuclei/rsync');
* const client = new rsync.RsyncClient();
* const connected = client.Connect('acme.com', 873, 'username', 'password', 'backup');
* ```
*/
public Connect(host: string, port: number, username: string, password: string, module: string): boolean | null {
return null;
}

/**
* ListModules lists available modules on the rsync server.
* @example
* ```javascript
* const rsync = require('nuclei/rsync');
* const client = new rsync.RsyncClient();
* const modules = client.ListModules('acme.com', 873, 'username', 'password');
* log(toJSON(modules));
* ```
*/
public ListModules(host: string, port: number, username: string, password: string): string[] | null {
return null;
}

/**
* ListFilesInModule lists files in a specific module on the rsync server.
* @example
* ```javascript
* const rsync = require('nuclei/rsync');
* const client = new rsync.RsyncClient();
* const files = client.ListFilesInModule('acme.com', 873, 'username', 'password', 'backup');
* log(toJSON(files));
* ```
*/
public ListFilesInModule(host: string, port: number, username: string, password: string, module: string): string[] | null {
return null;
}
}

/**
* IsRsyncResponse is the response from the IsRsync function.
Expand Down
Loading
Loading