Merge branch 'dev' of https://github.com/projectdiscovery/nuclei into feat-6702-http-client

This commit is contained in:
Ice3man
2026-01-01 16:10:24 +05:30
57 changed files with 1421 additions and 292 deletions

View File

@@ -36,7 +36,10 @@ body:
description: |
Steps to reproduce the behavior, for example, commands to run Nuclei.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
💡 Prefer copying text output over using screenshots for easier troubleshooting.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
placeholder: |
@@ -49,7 +52,10 @@ body:
description: |
Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
💡 Prefer copying text output over using screenshots for easier troubleshooting.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
render: shell
@@ -58,9 +64,9 @@ body:
label: Environment
description: |
Examples:
- **OS**: Ubuntu 20.04
- **Nuclei** (`nuclei -version`): v3.3.1
- **Go** (`go version`): go1.22.0 _(only if you've installed it via the `go install` command)_
- **OS**: Ubuntu 24.04
- **Nuclei** (`nuclei -version`): v3.6.0
- **Go** (`go version`): go1.24.0 _(only if you've installed it via the `go install` command)_
value: |
- OS:
- Nuclei:
@@ -72,7 +78,7 @@ body:
attributes:
label: Anything else?
description: |
Links? References? Templates? Anything that will give us more context about the issue you are encountering!
Links? References? Templates? CPU, memory, and trace profiles? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:

View File

@@ -2,6 +2,9 @@
<!-- Describe the overall picture of your modifications to help maintainers understand the pull request. PRs are required to be associated to their related issue tickets or feature request. -->
### Proof
<!-- How has this been tested? Please describe the tests that you ran to verify your changes. -->
## Checklist

View File

@@ -1,28 +1,24 @@
name: 🤖 Auto Merge
name: 🔀 Auto merge PR
on:
pull_request_review:
types: [submitted]
# pull_request:
# types: [opened, synchronize, reopened, ready_for_review]
# pull_request_review:
# types: [submitted]
workflow_run:
workflows: ["♾️ Compatibility Check"]
types:
- completed
workflows: ["♾️ Compatibility Checks"]
types: [completed]
permissions:
contents: write
pull-requests: write
issues: write
repository-projects: write
jobs:
auto-merge:
auto-merge-dependabot:
runs-on: ubuntu-latest
if: github.actor == 'dependabot[bot]'
if: ${{ github.event.workflow_run.conclusion == 'success' }}
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/pr/approve@v1
- uses: projectdiscovery/actions/pr/merge@v1
with:
token: ${{ secrets.DEPENDABOT_PAT }}
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
github-token: ${{ secrets.DEPENDABOT_PAT }}
target: all
auto: "true"

View File

@@ -7,8 +7,8 @@ on:
- dev
jobs:
check:
if: github.actor == 'dependabot[bot]'
compat-checks:
if: ${{ github.actor == 'dependabot[bot]' }}
runs-on: ubuntu-latest
permissions:
contents: write
@@ -16,4 +16,5 @@ jobs:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/go/compat-checks@v1
with:
go-version: "stable"
release-test: true

61
.github/workflows/flamegraph.yaml vendored Normal file
View File

@@ -0,0 +1,61 @@
name: 📊 Flamegraph
on:
workflow_call: {}
jobs:
flamegraph:
name: "Flamegraph"
env:
PROFILE_MEM: "/tmp/nuclei-profile"
TARGET_URL: "http://honey.scanme.sh/-/?foo=bar"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- run: make build
- name: "Setup environment (push)"
if: ${{ github.event_name == 'push' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
- name: "Setup environment (pull_request)"
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
- run: ./bin/nuclei -update-templates
- run: |
./bin/nuclei -silent -target="${TARGET_URL}" \
-disable-update-check \
-no-mhe -no-httpx \
-code -dast -file -headless \
-enable-self-contained -enable-global-matchers \
-rate-limit=0 \
-concurrency=250 \
-headless-concurrency=100 \
-payload-concurrency=250 \
-bulk-size=250 \
-headless-bulk-size=100 \
-profile-mem="${PROFILE_MEM}"
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"

View File

@@ -4,11 +4,11 @@ on:
push:
branches:
- dev
workflow_dispatch:
workflow_dispatch: {}
jobs:
publish-docs:
if: "${{ !endsWith(github.actor, '[bot]') }}"
if: ${{ !endsWith(github.actor, '[bot]') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

View File

@@ -1,56 +1,50 @@
name: 👤 Generate PGO
on:
push:
branches: ["dev"]
paths:
- '**.go'
- '**.mod'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# TODO(dwisiswant0): https://go.dev/doc/pgo#merging-profiles
workflow_dispatch: {}
workflow_call: {}
jobs:
pgo:
strategy:
matrix:
targets: [150]
runs-on: ubuntu-latest
if: github.repository == 'projectdiscovery/nuclei'
permissions:
contents: write
env:
PGO_FILE: "cmd/nuclei/default.pgo"
LIST_FILE: "/tmp/targets-${{ matrix.targets }}.txt"
PROFILE_MEM: "/tmp/nuclei-profile-${{ matrix.targets }}-targets"
TARGET: "https://honey.scanme.sh"
TARGET_COUNT: "100"
TARGET_LIST: "/tmp/targets.txt"
PROFILE_MEM: "/tmp/nuclei-profile"
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/git@v1
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- name: Generate list
run: for i in {1..${{ matrix.targets }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
# NOTE(dwisiswant0): use `-no-mhe` flag to get better samples.
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}" -no-mhe
working-directory: cmd/nuclei/
- run: mv "${PROFILE_MEM}.cpu" ${PGO_FILE}
# NOTE(dwisiswant0): shall we prune $PGO_FILE git history?
# if we prune it, this won't be linear since it requires a force-push.
# if we don't, the git objects will just keep growing bigger.
#
# Ref:
# - https://go.dev/blog/pgo#:~:text=We%20recommend%20committing%20default.pgo%20files%20to%20your%20repository
# - https://gist.github.com/nottrobin/5758221
- uses: projectdiscovery/actions/commit@v1
with:
files: "${PGO_FILE}"
message: "build: update PGO profile :robot:"
- run: git push origin $GITHUB_REF
- run: |
for i in {1..${{ env.TARGET_COUNT }}}; do
echo "${{ env.TARGET }}/-/?_=${i}" >> "${{ env.TARGET_LIST }}";
done
- run: make build
- run: ./bin/nuclei -update-templates
- run: |
./bin/nuclei -silent -list="${TARGET_LIST}" \
-disable-update-check \
-no-mhe -no-httpx \
-code -dast -file -headless \
-enable-self-contained -enable-global-matchers \
-rate-limit=0 \
-concurrency=250 \
-headless-concurrency=100 \
-payload-concurrency=250 \
-bulk-size=250 \
-headless-bulk-size=100 \
-profile-mem="${PROFILE_MEM}" \
env:
DISABLE_STDOUT: "1"
- run: mv "${PROFILE_MEM}.cpu" "${PGO_FILE}"
- uses: actions/upload-artifact@v6
with:
name: "pgo"
path: "${{ env.PGO_FILE }}"
overwrite: true

View File

@@ -3,7 +3,7 @@ name: 🐛 govulncheck
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
workflow_dispatch: {}
jobs:
govulncheck:

View File

@@ -1,8 +1,8 @@
name: 🔨 Performance Regression
on:
workflow_call:
workflow_dispatch:
workflow_call: {}
workflow_dispatch: {}
jobs:
perf-regression:

View File

@@ -1,43 +0,0 @@
name: 🔨 Performance Test
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
jobs:
perf-test:
strategy:
matrix:
count: [50, 100, 150]
runs-on: ubuntu-latest
if: github.repository == 'projectdiscovery/nuclei'
env:
LIST_FILE: "/tmp/targets-${{ matrix.count }}.txt"
PROFILE_MEM: "/tmp/nuclei-perf-test-${{ matrix.count }}"
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/go@v1
- run: make verify
- name: Generate list
run: for i in {1..${{ matrix.count }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}"
env:
NUCLEI_ARGS: host-error-stats
working-directory: cmd/nuclei/
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"

View File

@@ -4,16 +4,28 @@ on:
push:
tags:
- '*'
workflow_dispatch:
workflow_dispatch: {}
jobs:
release:
jobs:
pgo:
name: "Generate PGO"
uses: ./.github/workflows/generate-pgo.yaml
release:
name: "Release"
needs: ["pgo"]
runs-on: ubuntu-latest-16-cores
steps:
- uses: actions/checkout@v6
with:
fetch-depth: 0
- uses: actions/download-artifact@v7
with:
name: "pgo"
path: "cmd/nuclei/"
- uses: projectdiscovery/actions/setup/go@v1
with:
go-version: "stable"
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}

View File

@@ -19,7 +19,7 @@ concurrency:
jobs:
lint:
name: "Lint"
if: "${{ !endsWith(github.actor, '[bot]') }}"
if: ${{ !endsWith(github.actor, '[bot]') }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
@@ -152,47 +152,16 @@ jobs:
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/go@v1
with:
go-version: "stable"
- uses: projectdiscovery/actions/goreleaser@v1
flamegraph:
name: "Flamegraph"
needs: ["tests"]
env:
PROFILE_MEM: "/tmp/nuclei"
TARGET_URL: "http://scanme.sh/a/?b=c"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- run: make build
- name: "Setup environment (push)"
if: ${{ github.event_name == 'push' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
- name: "Setup environment (pull_request)"
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
- run: ./bin/nuclei -silent -update-templates
- run: ./bin/nuclei -silent -u "${TARGET_URL}" -profile-mem="${PROFILE_MEM}"
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
uses: ./.github/workflows/flamegraph.yaml
perf-regression:
name: "Performance regression"
needs: ["tests"]
uses: ./.github/workflows/perf-regression.yaml

View File

@@ -11,8 +11,9 @@ builds:
id: nuclei-cli
env:
- CGO_ENABLED=0
goos: [windows,linux,darwin]
goarch: [amd64,'386',arm,arm64]
- GOEXPERIMENT=greenteagc
goos: [windows, linux, darwin]
goarch: [amd64, '386', arm, arm64]
ignore:
- goos: darwin
goarch: '386'

View File

@@ -26,6 +26,7 @@ var codeTestCases = []TestCaseInfo{
{Path: "protocols/code/pre-condition.yaml", TestCase: &codePreCondition{}, DisableOn: isCodeDisabled},
{Path: "protocols/code/sh-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() }},
{Path: "protocols/code/py-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() }},
{Path: "protocols/code/pwsh-echo.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return isCodeDisabled() }},
}
const (

View File

@@ -15,19 +15,25 @@ 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() }},
{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/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/mysql-connect.yaml", TestCase: &javascriptMySQLConnect{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
{Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}},
}
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
postgresResource *dockertest.Resource
mysqlResource *dockertest.Resource
rsyncResource *dockertest.Resource
pool *dockertest.Pool
defaultRetry = 3
)
type javascriptNetHttps struct{}
@@ -120,7 +126,7 @@ func (j *javascriptOracleAuthTest) Execute(filePath string) error {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let ssh server start
// let oracle server start
time.Sleep(3 * time.Second)
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
return nil
@@ -169,6 +175,70 @@ func (j *javascriptVncPassBrute) Execute(filePath string) error {
return multierr.Combine(errs...)
}
type javascriptPostgresPassBrute struct{}
func (j *javascriptPostgresPassBrute) Execute(filePath string) error {
if postgresResource == nil || pool == nil {
// skip test as postgres is not running
return nil
}
tempPort := postgresResource.GetPort("5432/tcp")
finalURL := "localhost:" + tempPort
defer purge(postgresResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let postgres 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...)
}
type javascriptMySQLConnect struct{}
func (j *javascriptMySQLConnect) Execute(filePath string) error {
if mysqlResource == nil || pool == nil {
// skip test as mysql is not running
return nil
}
tempPort := mysqlResource.GetPort("3306/tcp")
finalURL := "localhost:" + tempPort
defer purge(mysqlResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let mysql server start
time.Sleep(5 * 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...)
}
type javascriptMultiPortsSSH struct{}
func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
@@ -190,6 +260,38 @@ func (j *javascriptNoPortArgs) Execute(filePath string) error {
return expectResultsCount(results, 1)
}
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 {
@@ -292,4 +394,57 @@ func init() {
if err := vncResource.Expire(30); err != nil {
log.Printf("Could not expire resource: %s", err)
}
// setup a temporary postgres instance
postgresResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "postgres",
Tag: "latest",
Env: []string{
"POSTGRES_PASSWORD=postgres",
"POSTGRES_USER=postgres",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start postgres resource: %s", err)
return
}
// by default expire after 30 sec
if err := postgresResource.Expire(30); err != nil {
log.Printf("Could not expire postgres resource: %s", err)
}
// setup a temporary mysql instance
mysqlResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "mysql",
Tag: "latest",
Env: []string{
"MYSQL_ROOT_PASSWORD=secret",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start mysql resource: %s", err)
return
}
// by default expire after 30 sec
if err := mysqlResource.Expire(30); err != nil {
log.Printf("Could not expire mysql 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)
}
}

View File

@@ -100,6 +100,9 @@
# update-existing: false
# # URL is the jira application url
# url: https://localhost/jira
# # site-url is the browsable URL for the Jira instance (optional)
# # If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com
# site-url: https://your-company.atlassian.net
# # account-id is the account-id of the Jira user or username in case of on-prem Jira
# account-id: test-account-id
# # email is the email of the user for Jira instance

44
go.mod
View File

@@ -22,12 +22,12 @@ require (
github.com/olekukonko/tablewriter v1.0.8
github.com/pkg/errors v0.9.1
github.com/projectdiscovery/clistats v0.1.1
github.com/projectdiscovery/fastdialer v0.4.20
github.com/projectdiscovery/hmap v0.0.98
github.com/projectdiscovery/fastdialer v0.5.1
github.com/projectdiscovery/hmap v0.0.99
github.com/projectdiscovery/interactsh v1.2.4
github.com/projectdiscovery/rawhttp v0.1.90
github.com/projectdiscovery/retryabledns v1.0.111
github.com/projectdiscovery/retryablehttp-go v1.1.1
github.com/projectdiscovery/retryabledns v1.0.112
github.com/projectdiscovery/retryablehttp-go v1.3.1
github.com/projectdiscovery/yamldoc-go v1.0.6
github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/xid v1.6.0
@@ -39,9 +39,9 @@ require (
github.com/valyala/fasttemplate v1.2.2
github.com/weppos/publicsuffix-go v0.50.1
go.uber.org/multierr v1.11.0
golang.org/x/net v0.47.0
golang.org/x/net v0.48.0
golang.org/x/oauth2 v0.30.0
golang.org/x/text v0.31.0
golang.org/x/text v0.32.0
gopkg.in/yaml.v2 v2.4.0
)
@@ -53,6 +53,7 @@ require (
github.com/DataDog/gostackparse v0.7.0
github.com/Masterminds/semver/v3 v3.2.1
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/alexsnet/go-vnc v0.1.0
@@ -93,26 +94,26 @@ require (
github.com/microsoft/go-mssqldb v1.9.2
github.com/ory/dockertest/v3 v3.12.0
github.com/praetorian-inc/fingerprintx v1.1.15
github.com/projectdiscovery/dsl v0.8.8
github.com/projectdiscovery/dsl v0.8.10
github.com/projectdiscovery/fasttemplate v0.0.2
github.com/projectdiscovery/gcache v0.0.0-20241015120333-12546c6e3f4c
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
github.com/projectdiscovery/goflags v0.1.74
github.com/projectdiscovery/gologger v1.1.64
github.com/projectdiscovery/gologger v1.1.66
github.com/projectdiscovery/gostruct v0.0.2
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81
github.com/projectdiscovery/httpx v1.7.4
github.com/projectdiscovery/mapcidr v1.1.97
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
github.com/projectdiscovery/networkpolicy v0.1.32
github.com/projectdiscovery/networkpolicy v0.1.33
github.com/projectdiscovery/ratelimit v0.0.82
github.com/projectdiscovery/rdap v0.9.0
github.com/projectdiscovery/sarif v0.0.1
github.com/projectdiscovery/tlsx v1.2.2
github.com/projectdiscovery/uncover v1.2.0
github.com/projectdiscovery/useragent v0.0.105
github.com/projectdiscovery/utils v0.7.3
github.com/projectdiscovery/wappalyzergo v0.2.59
github.com/projectdiscovery/useragent v0.0.106
github.com/projectdiscovery/utils v0.8.0
github.com/projectdiscovery/wappalyzergo v0.2.61
github.com/redis/go-redis/v9 v9.11.0
github.com/seh-msft/burpxml v1.0.1
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
@@ -125,7 +126,7 @@ require (
github.com/zmap/zgrab2 v0.1.8
gitlab.com/gitlab-org/api/client-go v0.130.1
go.mongodb.org/mongo-driver v1.17.4
golang.org/x/term v0.37.0
golang.org/x/term v0.38.0
gopkg.in/yaml.v3 v3.0.1
moul.io/http2curl v1.0.0
)
@@ -207,6 +208,7 @@ require (
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/djherbis/times v1.6.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/docker/cli v27.4.1+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
@@ -268,6 +270,7 @@ require (
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // 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.2 // indirect
@@ -325,10 +328,10 @@ require (
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/projectdiscovery/asnmap v1.1.1 // indirect
github.com/projectdiscovery/blackrock v0.0.1 // indirect
github.com/projectdiscovery/cdncheck v1.2.14 // indirect
github.com/projectdiscovery/cdncheck v1.2.16 // indirect
github.com/projectdiscovery/freeport v0.0.7 // indirect
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 // indirect
github.com/refraction-networking/utls v1.7.1 // indirect
github.com/sashabaranov/go-openai v1.37.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
@@ -383,8 +386,7 @@ require (
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/sync v0.18.0 // indirect
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
golang.org/x/sync v0.19.0 // indirect
mellium.im/sasl v0.3.2 // indirect
)
@@ -407,12 +409,12 @@ require (
go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/zap v1.27.0 // indirect
goftp.io/server/v2 v2.0.1 // indirect
golang.org/x/crypto v0.45.0 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621
golang.org/x/mod v0.29.0 // indirect
golang.org/x/sys v0.38.0 // indirect
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.38.0
golang.org/x/tools v0.39.0
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect

89
go.sum
View File

@@ -87,6 +87,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=
@@ -314,6 +316,8 @@ github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5Qvfr
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
@@ -632,6 +636,8 @@ github.com/k14s/difflib v0.0.0-20201117154628-0c031775bf57 h1:CwBRArr+BWBopnUJhD
github.com/k14s/difflib v0.0.0-20201117154628-0c031775bf57/go.mod h1:B0xN2MiNBGWOWi9CcfAo9LBI8IU4J1utlbOIJCsmKr4=
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 h1:4bcRTTSx+LKSxMWibIwzHnDNmaN1x52oEpvnjCy+8vk=
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368/go.mod h1:lKGj1op99m4GtQISxoD2t+K+WO/q2NzEPKvfXFQfbCA=
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/kataras/jwt v0.1.10 h1:GBXOF9RVInDPhCFBiDumRG9Tt27l7ugLeLo8HL5SeKQ=
github.com/kataras/jwt v0.1.10/go.mod h1:xkimAtDhU/aGlQqjwvgtg+VyuPwMiyZHaY8LJRh0mYo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@@ -825,14 +831,14 @@ github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kI
github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60=
github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ=
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
github.com/projectdiscovery/cdncheck v1.2.14 h1:mHfNMdFwpQziPxIZnE4i0UERU/8vOcxSTcjYwp1PA2Y=
github.com/projectdiscovery/cdncheck v1.2.14/go.mod h1:wPckYZjRFQydtbosCS0beGjuAP5h4faX7eUqB8L3/cA=
github.com/projectdiscovery/cdncheck v1.2.16 h1:m6sQh5VAWN2sVp3o8LSFtAfnv3V0Dpoqqw83ZwnrA2c=
github.com/projectdiscovery/cdncheck v1.2.16/go.mod h1:2OsjLn4x7VsM+ZNccGU185pd43U+0h5gDDStU0GtNl8=
github.com/projectdiscovery/clistats v0.1.1 h1:8mwbdbwTU4aT88TJvwIzTpiNeow3XnAB72JIg66c8wE=
github.com/projectdiscovery/clistats v0.1.1/go.mod h1:4LtTC9Oy//RiuT1+76MfTg8Hqs7FQp1JIGBM3nHK6a0=
github.com/projectdiscovery/dsl v0.8.8 h1:UlcCf/2hfqqfDlhHWun3xT0u310iLWMiRoVw1t3TTLg=
github.com/projectdiscovery/dsl v0.8.8/go.mod h1:gXrMGOfBVu4UvgabOfXSMqeC5RnR93aeZ94+V32OlHI=
github.com/projectdiscovery/fastdialer v0.4.20 h1:LBw2pB9Y/0Du+d/asyB4u5vF+QhvkPIo+SLcUaf2pG8=
github.com/projectdiscovery/fastdialer v0.4.20/go.mod h1:GXzyco8SVX6E335FKpIiX0WSRbK2MsNwvqXCr195QtM=
github.com/projectdiscovery/dsl v0.8.10 h1:7t8KMukLlNGiOPdESGCkpOxu9RAT3RLRBY0Z5CoZIgs=
github.com/projectdiscovery/dsl v0.8.10/go.mod h1:66WXaiVEOA2LlZuH81/izzybA3s++zX/nrKwgQV/2S0=
github.com/projectdiscovery/fastdialer v0.5.1 h1:/4PX1u80QfZ8+DdF9jdtz1in5eajJhw89/xouVc9/+c=
github.com/projectdiscovery/fastdialer v0.5.1/go.mod h1:34SS9VxrrmUhO67LYioxjrAWOQ5/kwgJNj9CiFgRJso=
github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA=
github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw=
github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk=
@@ -843,38 +849,38 @@ github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb h1:rutG90
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb/go.mod h1:FLjF1DmZ+POoGEiIQdWuYVwS++C/GwpX8YaCsTSm1RY=
github.com/projectdiscovery/goflags v0.1.74 h1:n85uTRj5qMosm0PFBfsvOL24I7TdWRcWq/1GynhXS7c=
github.com/projectdiscovery/goflags v0.1.74/go.mod h1:UMc9/7dFz2oln+10tv6cy+7WZKTHf9UGhaNkF95emh4=
github.com/projectdiscovery/gologger v1.1.64 h1:GQ1DplEo5YnVozPImYWQqkeU5rwkPBxjMzeZmirVMdA=
github.com/projectdiscovery/gologger v1.1.64/go.mod h1:daez34xaA7LTazBb4t+Ccm9/9Kjvlsu4EY033lQdATA=
github.com/projectdiscovery/gologger v1.1.66 h1:dYXfCWWNNiC7AgIDBkIF+3sTGyQbrpncsQEFQwyKCOg=
github.com/projectdiscovery/gologger v1.1.66/go.mod h1:lm3vFKt52lXgo799dBtySe/1zRN1lMGUQuNb5YtwkYQ=
github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M=
github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81 h1:yHh46pJovYbyiaHCV7oIDinFmy+Fyq36H1BowJgb0M0=
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81/go.mod h1:9lmGPBDGZVANzCGjQg+V32n8Y3Cgjo/4kT0E88lsVTI=
github.com/projectdiscovery/hmap v0.0.98 h1:XxYIi7yJCNiDAKCJXvuY9IBM5O6OgDgx4XHgKxkR4eg=
github.com/projectdiscovery/hmap v0.0.98/go.mod h1:bgN5fuZPJMj2YnAGEEnCypoifCnALJixHEVQszktQIU=
github.com/projectdiscovery/hmap v0.0.99 h1:XPfLnD3CUrMqVCIdpK9ozD7Xmp3simx3T+2j4WWhHnU=
github.com/projectdiscovery/hmap v0.0.99/go.mod h1:koyUJi83K5G3w35ZLFXOYZIyYJsO+6hQrgDDN1RBrVE=
github.com/projectdiscovery/httpx v1.7.4 h1:GUEklAZ71VKM0krmgck2Km5odJi5dwfo0euc2r88tj0=
github.com/projectdiscovery/httpx v1.7.4/go.mod h1:sPnFYIXh0RAdjb02vpUGOsP5j28VDZs5khjMbXwEUGQ=
github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/IyenyRBhkI5jiqI=
github.com/projectdiscovery/interactsh v1.2.4/go.mod h1:E/IVNZ80/WKz8zTwGJWQygxIbhlRmuzZFsZwcGSZTdc=
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb/go.mod h1:vmgC0DTFCfoCLp0RAfsfYTZZan0QMVs+cmTbH6blfjk=
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE=
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 h1:eR+0HE//Ciyfwy3HC7fjRyKShSJHYoX2Pv7pPshjK/Q=
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8QuWs2puwy+2w=
github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA=
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
github.com/projectdiscovery/networkpolicy v0.1.32 h1:MD561zl0/fSzcNTlDlf/7pcQbYzulYoQssFBFjb2pcg=
github.com/projectdiscovery/networkpolicy v0.1.32/go.mod h1:5x4rGh4XhnoYl9wACnZyrjDGKIB/bQqxw2KrIM5V+XU=
github.com/projectdiscovery/networkpolicy v0.1.33 h1:bVgp+XpLEsQ7ZEJt3UaUqIwhI01MMdt7F2dfIKFQg/w=
github.com/projectdiscovery/networkpolicy v0.1.33/go.mod h1:YAPddAXUc/lhoU85AFdvgOQKx8Qh8r0vzSjexRWk6Yk=
github.com/projectdiscovery/ratelimit v0.0.82 h1:rtO5SQf5uQFu5zTahTaTcO06OxmG8EIF1qhdFPIyTak=
github.com/projectdiscovery/ratelimit v0.0.82/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
github.com/projectdiscovery/rawhttp v0.1.90 h1:LOSZ6PUH08tnKmWsIwvwv1Z/4zkiYKYOSZ6n+8RFKtw=
github.com/projectdiscovery/rawhttp v0.1.90/go.mod h1:VZYAM25UI/wVB3URZ95ZaftgOnsbphxyAw/XnQRRz4Y=
github.com/projectdiscovery/rdap v0.9.0 h1:wPhHx5pQ2QI+WGhyNb2PjhTl0NtB39Nk7YFZ9cp8ZGA=
github.com/projectdiscovery/rdap v0.9.0/go.mod h1:zk4yrJFQ2Hy36Aqk+DvotYQxYAeALaCJ5ORySkff36Q=
github.com/projectdiscovery/retryabledns v1.0.111 h1:iyMdCDgNmaSRJYcGqB+SLlvlw9WijlbJ6Q9OEpRAWsQ=
github.com/projectdiscovery/retryabledns v1.0.111/go.mod h1:6TOPJ3QAE4reBu6bvsGsTcyEb+OypcKYFQH7yVsjyIM=
github.com/projectdiscovery/retryablehttp-go v1.1.1 h1:R1r5DgxXIW+AH98ifKxFpeFbZAXP/eP9bNOMpaO32As=
github.com/projectdiscovery/retryablehttp-go v1.1.1/go.mod h1:D+jVQkvQ3ZNDYX30SfxJQ4ylh//NMPq1Yf11lEo+oyA=
github.com/projectdiscovery/retryabledns v1.0.112 h1:4iCiuo6jMnw/pdOZRzBQrbUOUu5tOeuvGupxVV8RDLw=
github.com/projectdiscovery/retryabledns v1.0.112/go.mod h1:xsJTKbo+KGqd7+88z1naEUFJybLH2yjB/zUyOweA7k0=
github.com/projectdiscovery/retryablehttp-go v1.3.1 h1:ds0dcKa3565pYdIJrLcwcbrb9cH6MojY2uHwGPoXubg=
github.com/projectdiscovery/retryablehttp-go v1.3.1/go.mod h1:ISMHtd5DrSSDenQ23Vz9cIyIj3XlccZzZhuWHUJTa60=
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
@@ -883,12 +889,12 @@ github.com/projectdiscovery/tlsx v1.2.2 h1:Y96QBqeD2anpzEtBl4kqNbwzXh2TrzJuXfgiB
github.com/projectdiscovery/tlsx v1.2.2/go.mod h1:ZJl9F1sSl0sdwE+lR0yuNHVX4Zx6tCSTqnNxnHCFZB4=
github.com/projectdiscovery/uncover v1.2.0 h1:31tjYa0v8FB8Ch8hJTxb+2t63vsljdOo0OSFylJcX4M=
github.com/projectdiscovery/uncover v1.2.0/go.mod h1:ozqKb++p39Kmh1SmwIpbQ9p0aVGPXuwsb4/X2Kvx6ms=
github.com/projectdiscovery/useragent v0.0.105 h1:yFGFTfWZ/RZP5XbGRJtvKcbRNnlGI6xfPRXHb8DWdhg=
github.com/projectdiscovery/useragent v0.0.105/go.mod h1:jWG1BD2yu8EC3olt6Amke7BiyLkXzpErI7Jtzr/tWZM=
github.com/projectdiscovery/utils v0.7.3 h1:kX+77AA58yK6EZgkTRJEnK9V/7AZYzlXdcu/o/kJhFs=
github.com/projectdiscovery/utils v0.7.3/go.mod h1:uDdQ3/VWomai98l+a3Ye/srDXdJ4xUIar/mSXlQ9gBM=
github.com/projectdiscovery/wappalyzergo v0.2.59 h1:oztLKyEf3yC2ncUIfSXguVA6jwb7iaV5tuK1BF3K9CY=
github.com/projectdiscovery/wappalyzergo v0.2.59/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ=
github.com/projectdiscovery/useragent v0.0.106 h1:9fS08MRUUJvfBskTxcXY9TA4X1TwpH6iJ3P3YNaXNlo=
github.com/projectdiscovery/useragent v0.0.106/go.mod h1:9oVMjgd7CchIsyeweyigIPtW83gpiGf2NtR6UM5XK+o=
github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ=
github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg=
github.com/projectdiscovery/wappalyzergo v0.2.61 h1:TxiYJvXqReiscuWKtGKhFx3VxbVVjHOgECNX709AEX4=
github.com/projectdiscovery/wappalyzergo v0.2.61/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU=
github.com/projectdiscovery/yamldoc-go v1.0.6 h1:GCEdIRlQjDux28xTXKszM7n3jlMf152d5nqVpVoetas=
github.com/projectdiscovery/yamldoc-go v1.0.6/go.mod h1:R5lWrNzP+7Oyn77NDVPnBsxx2/FyQZBBkIAaSaCQFxw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -1214,8 +1220,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1253,8 +1259,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1305,8 +1311,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1335,8 +1341,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1389,6 +1395,7 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1401,8 +1408,8 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1416,8 +1423,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1434,8 +1441,8 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -1489,8 +1496,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1593,8 +1600,6 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 h1:K4u1NprbDNvKPczKfHLbwdOWHTZ0zfv2ow71H1nRnFU=
gopkg.in/corvus-ch/zbase32.v1 v1.0.0/go.mod h1:T3oKkPOm4AV/bNXCNFUxRmlE9RUyBz/DSo0nK9U+c0Y=
gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o=
gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=

View File

@@ -0,0 +1,27 @@
id: pw-echo
info:
name: PowerShell Echo Test
author: pdteam
severity: info
description: Tests PowerShell execution with an echo-like operation.
tags: test,powershell,echo
self-contained: true
code:
- engine:
- pwsh
- powershell
- powershell.exe
args:
- -ExecutionPolicy
- Bypass
pattern: "*.ps1"
source: |
Write-Output "test-output-success"
matchers:
- type: word
words:
- "test-output-success"

View File

@@ -0,0 +1,23 @@
id: mysql-connect
info:
name: MySQL Connect Test
author: pdteam
severity: high
javascript:
- pre-condition: |
isPortOpen(Host, Port)
code: |
const mysql = require('nuclei/mysql');
const client = new mysql.MySQLClient;
success = client.Connect(Host, Port, User, Pass);
args:
Host: "{{Host}}"
Port: "3306"
User: "root"
Pass: "secret"
matchers:
- type: dsl
dsl:
- "success == true"

View File

@@ -0,0 +1,47 @@
id: postgres-pass-brute
info:
name: PostgreSQL Password Bruteforce
author: pdteam
severity: high
description: |
This template bruteforces passwords for protected PostgreSQL instances.
If PostgreSQL is not protected with password, it is also matched.
metadata:
shodan-query: product:"PostgreSQL"
tags: js,network,postgresql,authentication
javascript:
- pre-condition: |
isPortOpen(Host,Port)
code: |
const postgres = require('nuclei/postgres');
const client = new postgres.PGClient;
success = client.Connect(Host, Port, User, Pass);
args:
Host: "{{Host}}"
Port: "5432"
User: "{{usernames}}"
Pass: "{{passwords}}"
attack: clusterbomb
payloads:
usernames:
- postgres
- admin
- root
passwords:
- ""
- postgres
- password
- admin
- root
stop-at-first-match: true
matchers:
- type: dsl
dsl:
- "success == true"

View File

@@ -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"

View File

@@ -19,6 +19,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
@@ -238,6 +239,9 @@ func (e *NucleiEngine) closeInternal() {
if e.tmpDir != "" {
_ = os.RemoveAll(e.tmpDir)
}
if e.opts != nil {
generators.ClearOptionsPayloadMap(e.opts)
}
}
// Close all resources used by nuclei engine

View File

@@ -31,7 +31,7 @@ const (
CLIConfigFileName = "config.yaml"
ReportingConfigFilename = "reporting-config.yaml"
// Version is the current version of nuclei
Version = `v3.6.1`
Version = `v3.6.2`
// Directory Names of custom templates
CustomS3TemplatesDirName = "s3"
CustomGitHubTemplatesDirName = "github"

View File

@@ -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()
}

View File

@@ -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.

View File

@@ -11,7 +11,7 @@ import (
)
func memoizedconnect(executionId string, host string, port int, username string, password string, dbName string) (bool, error) {
hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName)
hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return connect(executionId, host, port, username, password, dbName)
@@ -27,7 +27,7 @@ func memoizedconnect(executionId string, host string, port int, username string,
}
func memoizedisMssql(executionId string, host string, port int) (bool, error) {
hash := "isMssql" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isMssql" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isMssql(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisMySQL(executionId string, host string, port int) (bool, error) {
hash := "isMySQL" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isMySQL" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isMySQL(executionId, host, port)
@@ -25,7 +25,7 @@ func memoizedisMySQL(executionId string, host string, port int) (bool, error) {
}
func memoizedfingerprintMySQL(executionId string, host string, port int) (MySQLInfo, error) {
hash := "fingerprintMySQL" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "fingerprintMySQL" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return fingerprintMySQL(executionId, host, port)

View File

@@ -8,11 +8,11 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)
func memoizedconnectWithDSN(dsn string) (bool, error) {
hash := "connectWithDSN" + ":" + fmt.Sprint(dsn)
func memoizedconnectWithDSN(executionId string, dsn string) (bool, error) {
hash := "connectWithDSN" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(dsn)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return connectWithDSN(dsn)
return connectWithDSN(executionId, dsn)
})
if err != nil {
return false, err

View File

@@ -108,7 +108,7 @@ func (c *MySQLClient) Connect(ctx context.Context, host string, port int, userna
if err != nil {
return false, err
}
return connectWithDSN(dsn)
return connectWithDSN(executionId, dsn)
}
type (
@@ -190,8 +190,9 @@ func fingerprintMySQL(executionId string, host string, port int) (MySQLInfo, err
// const client = new mysql.MySQLClient;
// const connected = client.ConnectWithDSN('username:password@tcp(acme.com:3306)/');
// ```
func (c *MySQLClient) ConnectWithDSN(dsn string) (bool, error) {
return memoizedconnectWithDSN(dsn)
func (c *MySQLClient) ConnectWithDSN(ctx context.Context, dsn string) (bool, error) {
executionId := ctx.Value("executionId").(string)
return memoizedconnectWithDSN(executionId, dsn)
}
// ExecuteQueryWithOpts connects to Mysql database using given credentials

View File

@@ -1,6 +1,7 @@
package mysql
import (
"context"
"database/sql"
"fmt"
"net"
@@ -72,7 +73,7 @@ func BuildDSN(opts MySQLOptions) (string, error) {
}
// @memo
func connectWithDSN(dsn string) (bool, error) {
func connectWithDSN(executionId string, dsn string) (bool, error) {
db, err := sql.Open("mysql", dsn)
if err != nil {
return false, err
@@ -83,7 +84,8 @@ func connectWithDSN(dsn string) (bool, error) {
db.SetMaxOpenConns(1)
db.SetMaxIdleConns(0)
_, err = db.Exec("select 1")
ctx := context.WithValue(context.Background(), "executionId", executionId) // nolint: staticcheck
err = db.PingContext(ctx)
if err != nil {
return false, err
}

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisOracle(executionId string, host string, port int) (IsOracleResponse, error) {
hash := "isOracle" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isOracle" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isOracle(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisPoP3(executionId string, host string, port int) (IsPOP3Response, error) {
hash := "isPoP3" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isPoP3" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isPoP3(executionId, host, port)

View File

@@ -5,15 +5,15 @@ import (
"errors"
"fmt"
_ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap"
utils "github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
_ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)
func memoizedisPostgres(executionId string, host string, port int) (bool, error) {
hash := "isPostgres" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isPostgres" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isPostgres(executionId, host, port)
@@ -29,7 +29,7 @@ func memoizedisPostgres(executionId string, host string, port int) (bool, error)
}
func memoizedexecuteQuery(executionId string, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) {
hash := "executeQuery" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + ":" + fmt.Sprint(query)
hash := "executeQuery" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName) + ":" + fmt.Sprint(query)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return executeQuery(executionId, host, port, username, password, dbName, query)
@@ -45,7 +45,7 @@ func memoizedexecuteQuery(executionId string, host string, port int, username st
}
func memoizedconnect(executionId string, host string, port int, username string, password string, dbName string) (bool, error) {
hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName)
hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(username) + ":" + fmt.Sprint(password) + ":" + fmt.Sprint(dbName)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return connect(executionId, host, port, username, password, dbName)

View File

@@ -82,7 +82,7 @@ func isPostgres(executionId string, host string, port int) (bool, error) {
// const client = new postgres.PGClient;
// const connected = client.Connect('acme.com', 5432, 'username', 'password');
// ```
func (c *PGClient) Connect(ctx context.Context, host string, port int, username, password string) (bool, error) {
func (c *PGClient) Connect(ctx context.Context, host string, port int, username string, password string) (bool, error) {
ok, err := c.IsPostgres(ctx, host, port)
if err != nil {
return false, err
@@ -104,7 +104,7 @@ func (c *PGClient) Connect(ctx context.Context, host string, port int, username,
// const result = client.ExecuteQuery('acme.com', 5432, 'username', 'password', 'dbname', 'select * from users');
// log(to_json(result));
// ```
func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username, password, dbName, query string) (*utils.SQLResult, error) {
func (c *PGClient) ExecuteQuery(ctx context.Context, host string, port int, username string, password string, dbName string, query string) (*utils.SQLResult, error) {
ok, err := c.IsPostgres(ctx, host, port)
if err != nil {
return nil, err
@@ -157,7 +157,7 @@ func executeQuery(executionId string, host string, port int, username string, pa
// const client = new postgres.PGClient;
// const connected = client.ConnectWithDB('acme.com', 5432, 'username', 'password', 'dbname');
// ```
func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username, password, dbName string) (bool, error) {
func (c *PGClient) ConnectWithDB(ctx context.Context, host string, port int, username string, password string, dbName string) (bool, error) {
ok, err := c.IsPostgres(ctx, host, port)
if err != nil {
return false, err
@@ -207,7 +207,7 @@ func connect(executionId string, host string, port int, username string, passwor
_ = db.Close()
}()
_, err := db.Exec(ctx, "select 1")
_, err := db.ExecContext(ctx, "select 1")
if err != nil {
switch true {
case strings.Contains(err.Error(), "connect: connection refused"):

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisRDP(executionId string, host string, port int) (IsRDPResponse, error) {
hash := "isRDP" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isRDP" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isRDP(executionId, host, port)
@@ -25,7 +25,7 @@ func memoizedisRDP(executionId string, host string, port int) (IsRDPResponse, er
}
func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAuthResponse, error) {
hash := "checkRDPAuth" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "checkRDPAuth" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return checkRDPAuth(executionId, host, port)
@@ -41,7 +41,7 @@ func memoizedcheckRDPAuth(executionId string, host string, port int) (CheckRDPAu
}
func memoizedcheckRDPEncryption(executionId string, host string, port int) (RDPEncryptionResponse, error) {
hash := "checkRDPEncryption" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "checkRDPEncryption" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return checkRDPEncryption(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedgetServerInfo(executionId string, host string, port int) (string, error) {
hash := "getServerInfo" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "getServerInfo" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return getServerInfo(executionId, host, port)
@@ -25,7 +25,7 @@ func memoizedgetServerInfo(executionId string, host string, port int) (string, e
}
func memoizedconnect(executionId string, host string, port int, password string) (bool, error) {
hash := "connect" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password)
hash := "connect" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return connect(executionId, host, port, password)
@@ -41,7 +41,7 @@ func memoizedconnect(executionId string, host string, port int, password string)
}
func memoizedgetServerInfoAuth(executionId string, host string, port int, password string) (string, error) {
hash := "getServerInfoAuth" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password)
hash := "getServerInfoAuth" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(password)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return getServerInfoAuth(executionId, host, port, password)
@@ -57,7 +57,7 @@ func memoizedgetServerInfoAuth(executionId string, host string, port int, passwo
}
func memoizedisAuthenticated(executionId string, host string, port int) (bool, error) {
hash := "isAuthenticated" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isAuthenticated" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isAuthenticated(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisRsync(executionId string, host string, port int) (IsRsyncResponse, error) {
hash := "isRsync" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isRsync" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isRsync(executionId, host, port)

View File

@@ -1,18 +1,31 @@
package rsync
import (
"bytes"
"context"
"fmt"
"log/slog"
"net"
"strconv"
"time"
rsynclib "github.com/Mzack9999/go-rsync/rsync"
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
"github.com/praetorian-inc/fingerprintx/pkg/plugins/services/rsync"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)
type (
// 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();
// ```
RsyncClient struct{}
// IsRsyncResponse is the response from the IsRsync function.
// this is returned by IsRsync function.
// @example
@@ -25,8 +38,30 @@ type (
IsRsync bool
Banner string
}
// ListSharesResponse is the response from the ListShares function.
// this is returned by ListShares function.
// @example
// ```javascript
// const rsync = require('nuclei/rsync');
// const client = new rsync.RsyncClient();
// const listShares = client.ListShares('acme.com', 873);
// log(toJSON(listShares));
RsyncListResponse struct {
Modules []string
Files []string
Output string
}
)
func connectWithFastDialer(executionId string, host string, port int) (net.Conn, error) {
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
}
return dialer.Fastdialer.Dial(context.Background(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
}
// IsRsync checks if a host is running a Rsync server.
// @example
// ```javascript
@@ -44,11 +79,7 @@ func isRsync(executionId string, host string, port int) (IsRsyncResponse, error)
resp := IsRsyncResponse{}
timeout := 5 * time.Second
dialer := protocolstate.GetDialersWithId(executionId)
if dialer == nil {
return IsRsyncResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
conn, err := dialer.Fastdialer.Dial(context.TODO(), "tcp", net.JoinHostPort(host, strconv.Itoa(port)))
conn, err := connectWithFastDialer(executionId, host, port)
if err != nil {
return resp, err
}
@@ -59,7 +90,7 @@ func isRsync(executionId string, host string, port int) (IsRsyncResponse, error)
rsyncPlugin := rsync.RSYNCPlugin{}
service, err := rsyncPlugin.Run(conn, timeout, plugins.Target{Host: host})
if err != nil {
return resp, err
return resp, nil
}
if service == nil {
return resp, nil
@@ -68,3 +99,115 @@ func isRsync(executionId string, host string, port int) (IsRsyncResponse, error)
resp.IsRsync = true
return resp, nil
}
// ListModules lists the modules of a Rsync server.
// @example
// ```javascript
// const rsync = require('nuclei/rsync');
// const client = new rsync.RsyncClient();
// const listModules = client.ListModules('acme.com', 873, 'username', 'password');
// log(toJSON(listModules));
// ```
func (c *RsyncClient) ListModules(ctx context.Context, host string, port int, username string, password string) (RsyncListResponse, error) {
executionId := ctx.Value("executionId").(string)
return listModules(executionId, host, port, username, password)
}
// ListShares lists the shares of a Rsync server.
// @example
// ```javascript
// const rsync = require('nuclei/rsync');
// const client = new rsync.RsyncClient();
// const listShares = client.ListFilesInModule('acme.com', 873, 'username', 'password', '/');
// log(toJSON(listShares));
// ```
func (c *RsyncClient) ListFilesInModule(ctx context.Context, host string, port int, username string, password string, module string) (RsyncListResponse, error) {
executionId := ctx.Value("executionId").(string)
return listFilesInModule(executionId, host, port, username, password, module)
}
func listModules(executionId string, host string, port int, username string, password string) (RsyncListResponse, error) {
fastDialer := protocolstate.GetDialersWithId(executionId)
if fastDialer == nil {
return RsyncListResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
address := net.JoinHostPort(host, strconv.Itoa(port))
// Create a bytes buffer for logging
var logBuffer bytes.Buffer
// Create a custom slog handler that writes to the buffer
logHandler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
Level: slog.LevelDebug,
})
// Create a logger that writes to our buffer
logger := slog.New(logHandler)
sr, err := rsynclib.ListModules(address,
rsynclib.WithClientAuth(username, password),
rsynclib.WithLogger(logger),
rsynclib.WithFastDialer(fastDialer.Fastdialer),
)
if err != nil {
return RsyncListResponse{}, fmt.Errorf("connect failed: %v", err)
}
result := RsyncListResponse{
Modules: make([]string, len(sr)),
Output: logBuffer.String(),
}
for i, item := range sr {
result.Modules[i] = string(item.Name)
}
return result, nil
}
func listFilesInModule(executionId string, host string, port int, username string, password string, module string) (RsyncListResponse, error) {
fastDialer := protocolstate.GetDialersWithId(executionId)
if fastDialer == nil {
return RsyncListResponse{}, fmt.Errorf("dialers not initialized for %s", executionId)
}
address := net.JoinHostPort(host, strconv.Itoa(port))
// Create a bytes buffer for logging
var logBuffer bytes.Buffer
// Create a custom slog handler that writes to the buffer
logHandler := slog.NewTextHandler(&logBuffer, &slog.HandlerOptions{
Level: slog.LevelDebug,
})
// Create a logger that writes to our buffer
logger := slog.New(logHandler)
sr, err := rsynclib.SocketClient(nil, address, module, ".",
rsynclib.WithClientAuth(username, password),
rsynclib.WithLogger(logger),
rsynclib.WithFastDialer(fastDialer.Fastdialer),
)
if err != nil {
return RsyncListResponse{}, fmt.Errorf("connect failed: %v", err)
}
// Try to list files to test authentication
list, err := sr.List()
if err != nil {
return RsyncListResponse{}, fmt.Errorf("authentication failed: %v", err)
}
result := RsyncListResponse{
Files: make([]string, len(list)),
Output: logBuffer.String(),
}
for i, item := range list {
result.Files[i] = string(item.Path)
}
return result, nil
}

View File

@@ -11,7 +11,7 @@ import (
)
func memoizedconnectSMBInfoMode(executionId string, host string, port int) (*smb.SMBLog, error) {
hash := "connectSMBInfoMode" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "connectSMBInfoMode" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return connectSMBInfoMode(executionId, host, port)
@@ -27,7 +27,7 @@ func memoizedconnectSMBInfoMode(executionId string, host string, port int) (*smb
}
func memoizedlistShares(executionId string, host string, port int, user string, password string) ([]string, error) {
hash := "listShares" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(user) + ":" + fmt.Sprint(password)
hash := "listShares" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(user) + ":" + fmt.Sprint(password)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return listShares(executionId, host, port, user, password)

View File

@@ -13,7 +13,7 @@ import (
)
func memoizedcollectSMBv2Metadata(executionId string, host string, port int, timeout time.Duration) (*plugins.ServiceSMB, error) {
hash := "collectSMBv2Metadata" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(timeout)
hash := "collectSMBv2Metadata" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port) + ":" + fmt.Sprint(timeout)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return collectSMBv2Metadata(executionId, host, port, timeout)

View File

@@ -3,13 +3,14 @@ package smb
import (
"errors"
"fmt"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
)
func memoizeddetectSMBGhost(executionId string, host string, port int) (bool, error) {
hash := "detectSMBGhost" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "detectSMBGhost" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return detectSMBGhost(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisTelnet(executionId string, host string, port int) (IsTelnetResponse, error) {
hash := "isTelnet" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isTelnet" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isTelnet(executionId, host, port)

View File

@@ -9,7 +9,7 @@ import (
)
func memoizedisVNC(executionId string, host string, port int) (IsVNCResponse, error) {
hash := "isVNC" + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
hash := "isVNC" + ":" + fmt.Sprint(executionId) + ":" + fmt.Sprint(host) + ":" + fmt.Sprint(port)
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
return isVNC(executionId, host, port)

View File

@@ -44,20 +44,51 @@ func MergeMapsMany(maps ...interface{}) map[string][]string {
return m
}
// MergeMaps merges two maps into a new map
// MergeMaps merges multiple maps into a new map.
//
// Use [CopyMap] if you need to copy a single map.
// Use [MergeMapsInto] to merge into an existing map.
func MergeMaps(maps ...map[string]interface{}) map[string]interface{} {
merged := make(map[string]interface{})
mapsLen := 0
for _, m := range maps {
mapsLen += len(m)
}
merged := make(map[string]interface{}, mapsLen)
for _, m := range maps {
maps0.Copy(merged, m)
}
return merged
}
// CopyMap creates a shallow copy of a single map.
func CopyMap(m map[string]interface{}) map[string]interface{} {
if m == nil {
return nil
}
result := make(map[string]interface{}, len(m))
maps0.Copy(result, m)
return result
}
// MergeMapsInto copies all entries from src maps into dst (mutating dst).
//
// Use when dst is a fresh map the caller owns and wants to avoid allocation.
func MergeMapsInto(dst map[string]interface{}, srcs ...map[string]interface{}) {
for _, src := range srcs {
maps0.Copy(dst, src)
}
}
// ExpandMapValues converts values from flat string to string slice
func ExpandMapValues(m map[string]string) map[string][]string {
m1 := make(map[string][]string, len(m))
for k, v := range m {
m1[k] = []string{v}
}
return m1
}

View File

@@ -0,0 +1,109 @@
package generators
import (
"fmt"
"testing"
)
func BenchmarkMergeMaps(b *testing.B) {
map1 := map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
}
map2 := map[string]interface{}{
"key6": "value6",
"key7": "value7",
"key8": "value8",
"key9": "value9",
"key10": "value10",
}
map3 := map[string]interface{}{
"key11": "value11",
"key12": "value12",
"key13": "value13",
}
for i := 1; i <= 3; i++ {
b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
switch i {
case 1:
_ = MergeMaps(map1)
case 2:
_ = MergeMaps(map1, map2)
case 3:
_ = MergeMaps(map1, map2, map3)
}
}
})
}
}
func BenchmarkCopyMap(b *testing.B) {
map1 := map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
}
for i := 1; i <= 1; i++ {
b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
switch i {
case 1:
_ = CopyMap(map1)
}
}
})
}
}
func BenchmarkMergeMapsInto(b *testing.B) {
map1 := map[string]interface{}{
"key1": "value1",
"key2": "value2",
"key3": "value3",
"key4": "value4",
"key5": "value5",
}
map2 := map[string]interface{}{
"key6": "value6",
"key7": "value7",
"key8": "value8",
"key9": "value9",
"key10": "value10",
}
map3 := map[string]interface{}{
"key11": "value11",
"key12": "value12",
"key13": "value13",
}
map4 := map[string]interface{}{
"key14": "value14",
"key15": "value15",
"key16": "value16",
}
for i := 1; i <= 3; i++ {
b.Run(fmt.Sprintf("%d-maps", i), func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
switch i {
case 1:
MergeMapsInto(map1, map2)
case 2:
MergeMapsInto(map1, map2, map3)
case 3:
MergeMapsInto(map1, map2, map3, map4)
}
}
})
}
}

View File

@@ -1,12 +1,32 @@
package generators
import (
"sync"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
)
// BuildPayloadFromOptions returns a map with the payloads provided via CLI
// optionsPayloadMap caches the result of BuildPayloadFromOptions per options
// pointer. This supports multiple SDK instances with different options running
// concurrently.
var optionsPayloadMap sync.Map // map[*types.Options]map[string]interface{}
// BuildPayloadFromOptions returns a map with the payloads provided via CLI.
//
// The result is cached per options pointer since options don't change during a run.
// Returns a copy of the cached map to prevent concurrent modification issues.
// Safe for concurrent use with multiple SDK instances.
func BuildPayloadFromOptions(options *types.Options) map[string]interface{} {
if options == nil {
return make(map[string]interface{})
}
if cached, ok := optionsPayloadMap.Load(options); ok {
return CopyMap(cached.(map[string]interface{}))
}
m := make(map[string]interface{})
// merge with vars
if !options.Vars.IsEmpty() {
m = MergeMaps(m, options.Vars.AsMap())
@@ -16,5 +36,18 @@ func BuildPayloadFromOptions(options *types.Options) map[string]interface{} {
if options.EnvironmentVariables {
m = MergeMaps(EnvVars(), m)
}
return m
actual, _ := optionsPayloadMap.LoadOrStore(options, m)
// Return a copy to prevent concurrent writes to the cached map
return CopyMap(actual.(map[string]interface{}))
}
// ClearOptionsPayloadMap clears the cached options payload.
// SDK users should call this when disposing of a NucleiEngine instance
// to prevent memory leaks if creating many short-lived instances.
func ClearOptionsPayloadMap(options *types.Options) {
if options != nil {
optionsPayloadMap.Delete(options)
}
}

View File

@@ -0,0 +1,45 @@
package generators
import (
"testing"
"github.com/projectdiscovery/goflags"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
)
func BenchmarkBuildPayloadFromOptions(b *testing.B) {
// Setup options with vars and env vars
vars := goflags.RuntimeMap{}
_ = vars.Set("key1=value1")
_ = vars.Set("key2=value2")
_ = vars.Set("key3=value3")
_ = vars.Set("key4=value4")
_ = vars.Set("key5=value5")
opts := &types.Options{
Vars: vars,
EnvironmentVariables: true, // This adds more entries
}
b.Run("Sequential", func(b *testing.B) {
ClearOptionsPayloadMap(opts)
b.ReportAllocs()
for b.Loop() {
_ = BuildPayloadFromOptions(opts)
}
})
b.Run("Parallel", func(b *testing.B) {
ClearOptionsPayloadMap(opts)
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
m := BuildPayloadFromOptions(opts)
// Simulate typical usage - read a value
_ = m["key1"]
}
})
})
}

View File

@@ -0,0 +1,92 @@
package generators
import (
"sync"
"testing"
"github.com/projectdiscovery/goflags"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/stretchr/testify/require"
)
func TestBuildPayloadFromOptionsConcurrency(t *testing.T) {
// Test that BuildPayloadFromOptions is safe for concurrent use
// and returns independent copies that can be modified without races
vars := goflags.RuntimeMap{}
_ = vars.Set("key=value")
opts := &types.Options{
Vars: vars,
}
const numGoroutines = 100
var wg sync.WaitGroup
wg.Add(numGoroutines)
// Each goroutine gets a map and modifies it
for i := 0; i < numGoroutines; i++ {
go func(id int) {
defer wg.Done()
// Get the map (should be a copy of cached data)
m := BuildPayloadFromOptions(opts)
// Modify it - this should not cause races
m["goroutine_id"] = id
m["test_key"] = "test_value"
// Verify original cached value is present
require.Equal(t, "value", m["key"])
}(i)
}
wg.Wait()
}
func TestBuildPayloadFromOptionsCaching(t *testing.T) {
// Test that caching actually works
vars := goflags.RuntimeMap{}
_ = vars.Set("cached=yes")
opts := &types.Options{
Vars: vars,
EnvironmentVariables: false,
}
// First call - builds and caches
m1 := BuildPayloadFromOptions(opts)
require.Equal(t, "yes", m1["cached"])
// Second call - should return copy of cached result
m2 := BuildPayloadFromOptions(opts)
require.Equal(t, "yes", m2["cached"])
// Modify m1 - should not affect m2 since they're copies
m1["modified"] = "in_m1"
require.NotContains(t, m2, "modified")
// Modify m2 - should not affect future calls
m2["modified"] = "in_m2"
m3 := BuildPayloadFromOptions(opts)
require.NotContains(t, m3, "modified")
}
func TestClearOptionsPayloadMap(t *testing.T) {
vars := goflags.RuntimeMap{}
_ = vars.Set("temp=data")
opts := &types.Options{
Vars: vars,
}
// Build and cache
m1 := BuildPayloadFromOptions(opts)
require.Equal(t, "data", m1["temp"])
// Clear the cache
ClearOptionsPayloadMap(opts)
// Verify it still works (rebuilds)
m2 := BuildPayloadFromOptions(opts)
require.Equal(t, "data", m2["temp"])
}

View File

@@ -200,8 +200,14 @@ func initDialers(options *types.Options) error {
addr += ":3306"
}
executionId := ctx.Value("executionId").(string)
var executionId string
if val := ctx.Value("executionId"); val != nil {
executionId = val.(string)
}
dialer := GetDialersWithId(executionId)
if dialer == nil {
return nil, fmt.Errorf("dialers not initialized for %s", executionId)
}
return dialer.Fastdialer.Dial(ctx, "tcp", addr)
})

View File

@@ -70,18 +70,23 @@ func (variables *Variable) UnmarshalJSON(data []byte) error {
// Evaluate returns a finished map of variables based on set values
func (variables *Variable) Evaluate(values map[string]interface{}) map[string]interface{} {
result := make(map[string]interface{}, variables.Len())
combined := make(map[string]interface{}, len(values)+variables.Len())
generators.MergeMapsInto(combined, values)
variables.ForEach(func(key string, value interface{}) {
if sliceValue, ok := value.([]interface{}); ok {
// slices cannot be evaluated
result[key] = sliceValue
combined[key] = sliceValue
return
}
valueString := types.ToString(value)
combined := generators.MergeMaps(values, result)
if value, ok := combined[key]; ok {
valueString = types.ToString(value)
if existingValue, ok := combined[key]; ok {
valueString = types.ToString(existingValue)
}
result[key] = evaluateVariableValue(valueString, combined, result)
evaluated := evaluateVariableValueWithMap(valueString, combined)
result[key] = evaluated
combined[key] = evaluated
})
return result
}
@@ -98,29 +103,36 @@ func (variables *Variable) GetAll() map[string]interface{} {
// EvaluateWithInteractsh returns evaluation results of variables with interactsh
func (variables *Variable) EvaluateWithInteractsh(values map[string]interface{}, interact *interactsh.Client) (map[string]interface{}, []string) {
result := make(map[string]interface{}, variables.Len())
combined := make(map[string]interface{}, len(values)+variables.Len())
generators.MergeMapsInto(combined, values)
var interactURLs []string
variables.ForEach(func(key string, value interface{}) {
if sliceValue, ok := value.([]interface{}); ok {
// slices cannot be evaluated
result[key] = sliceValue
combined[key] = sliceValue
return
}
valueString := types.ToString(value)
if existingValue, ok := combined[key]; ok {
valueString = types.ToString(existingValue)
}
if strings.Contains(valueString, "interactsh-url") {
valueString, interactURLs = interact.Replace(valueString, interactURLs)
}
combined := generators.MergeMaps(values, result)
if value, ok := combined[key]; ok {
valueString = types.ToString(value)
}
result[key] = evaluateVariableValue(valueString, combined, result)
evaluated := evaluateVariableValueWithMap(valueString, combined)
result[key] = evaluated
combined[key] = evaluated
})
return result, interactURLs
}
// evaluateVariableValue expression and returns final value
func evaluateVariableValue(expression string, values, processing map[string]interface{}) string {
// evaluateVariableValue expression and returns final value.
//
// Deprecated: use evaluateVariableValueWithMap instead to avoid repeated map
// merging overhead.
func evaluateVariableValue(expression string, values, processing map[string]interface{}) string { // nolint
finalMap := generators.MergeMaps(values, processing)
result, err := expressions.Evaluate(expression, finalMap)
if err != nil {
@@ -130,6 +142,16 @@ func evaluateVariableValue(expression string, values, processing map[string]inte
return result
}
// evaluateVariableValueWithMap evaluates an expression with a pre-merged map.
func evaluateVariableValueWithMap(expression string, combinedMap map[string]interface{}) string {
result, err := expressions.Evaluate(expression, combinedMap)
if err != nil {
return expression
}
return result
}
// checkForLazyEval checks if the variables have any lazy evaluation i.e any dsl function
// and sets the flag accordingly.
func (variables *Variable) checkForLazyEval() bool {

View File

@@ -0,0 +1,80 @@
package variables
import (
"fmt"
"testing"
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
)
func BenchmarkVariableEvaluate(b *testing.B) {
// Setup variables with chained references and DSL functions
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(5),
}
variables.Set("base", "testvalue")
variables.Set("derived1", "{{base}}_suffix")
variables.Set("derived2", "{{md5(derived1)}}")
variables.Set("derived3", "prefix_{{derived2}}")
variables.Set("final", "{{derived3}}_end")
inputValues := map[string]interface{}{
"BaseURL": "http://example.com",
"Host": "example.com",
"Path": "/api/v1",
}
b.Run("Evaluate", func(b *testing.B) {
b.Run("5Variables", func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
_ = variables.Evaluate(inputValues)
}
})
b.Run("Parallel", func(b *testing.B) {
b.ReportAllocs()
b.RunParallel(func(pb *testing.PB) {
for pb.Next() {
_ = variables.Evaluate(inputValues)
}
})
})
})
}
func BenchmarkVariableEvaluateScaling(b *testing.B) {
// Test how the optimization scales with different variable counts
inputValues := map[string]interface{}{
"BaseURL": "http://example.com",
"Host": "example.com",
}
benchmarkSizes := []int{1, 5, 10, 20}
for _, size := range benchmarkSizes {
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(size),
}
// Create chain of variables
for i := range size {
varName := fmt.Sprintf("var%d", i)
if i == 0 {
variables.Set(varName, "initial")
} else {
prevVarName := fmt.Sprintf("var%d", i-1)
variables.Set(varName, fmt.Sprintf("{{%s}}_step", prevVarName))
}
}
b.Run(fmt.Sprintf("Variables-%d", size), func(b *testing.B) {
b.ReportAllocs()
for b.Loop() {
_ = variables.Evaluate(inputValues)
}
})
}
}

View File

@@ -4,6 +4,7 @@ import (
"testing"
"time"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/stretchr/testify/require"
@@ -147,3 +148,176 @@ func TestCheckForLazyEval(t *testing.T) {
require.True(t, variables.LazyEval, "LazyEval flag should be true")
})
}
func TestVariablesEvaluateChained(t *testing.T) {
t.Run("chained-variable-references", func(t *testing.T) {
// Test that variables can reference previously defined variables
// and that input values (like BaseURL) are available for evaluation
// but not included in the result
variables := &Variable{
LazyEval: true, // skip auto-evaluation in UnmarshalYAML
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(3),
}
variables.Set("a", "hello")
variables.Set("b", "{{a}} world")
variables.Set("c", "{{b}}!")
inputValues := map[string]interface{}{
"BaseURL": "http://example.com",
"Host": "example.com",
}
result := variables.Evaluate(inputValues)
// Result should contain only the defined variables, not input values
require.Len(t, result, 3, "result should contain exactly 3 variables")
require.NotContains(t, result, "BaseURL", "result should not contain input values")
require.NotContains(t, result, "Host", "result should not contain input values")
// Chained evaluation should work correctly
require.Equal(t, "hello", result["a"])
require.Equal(t, "hello world", result["b"])
require.Equal(t, "hello world!", result["c"])
})
t.Run("variables-using-input-values", func(t *testing.T) {
// Test that variables can use input values in expressions
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(2),
}
variables.Set("api_url", "{{BaseURL}}/api/v1")
variables.Set("full_path", "{{api_url}}/users")
inputValues := map[string]interface{}{
"BaseURL": "http://example.com",
}
result := variables.Evaluate(inputValues)
require.Len(t, result, 2)
require.Equal(t, "http://example.com/api/v1", result["api_url"])
require.Equal(t, "http://example.com/api/v1/users", result["full_path"])
require.NotContains(t, result, "BaseURL")
})
t.Run("mixed-expressions-and-chaining", func(t *testing.T) {
// Test combining DSL functions with chained variables
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(3),
}
variables.Set("token", "secret123")
variables.Set("hashed", "{{md5(token)}}")
variables.Set("header", "X-Auth: {{hashed}}")
result := variables.Evaluate(map[string]interface{}{})
require.Equal(t, "secret123", result["token"])
require.Equal(t, "5d7845ac6ee7cfffafc5fe5f35cf666d", result["hashed"]) // md5("secret123")
require.Equal(t, "X-Auth: 5d7845ac6ee7cfffafc5fe5f35cf666d", result["header"])
})
t.Run("evaluation-order-preserved", func(t *testing.T) {
// Test that evaluation follows insertion order
// (important for variables that depend on previously defined ones)
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(4),
}
variables.Set("step1", "A")
variables.Set("step2", "{{step1}}B")
variables.Set("step3", "{{step2}}C")
variables.Set("step4", "{{step3}}D")
result := variables.Evaluate(map[string]interface{}{})
require.Equal(t, "A", result["step1"])
require.Equal(t, "AB", result["step2"])
require.Equal(t, "ABC", result["step3"])
require.Equal(t, "ABCD", result["step4"])
})
}
func TestEvaluateWithInteractshOverrideOrder(t *testing.T) {
// This test demonstrates a bug where interactsh URL replacement is wasted
// when an input value exists for the same variable key.
//
// Bug scenario:
// 1. Variable "callback" is defined with "{{interactsh-url}}"
// 2. Input values contain "callback" with some other value
// 3. The interactsh-url is replaced first (wasting an interactsh URL)
// 4. Then immediately overwritten by the input value
//
// Expected behavior: Input override should be checked FIRST, then interactsh
// replacement should happen on the final valueString.
t.Run("interactsh-replacement-with-input-override", func(t *testing.T) {
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1),
}
variables.Set("callback", "{{interactsh-url}}")
// Input provides an override that also contains interactsh-url
inputValues := map[string]interface{}{
"callback": "https://custom.{{interactsh-url}}/path",
}
// Create a real interactsh client for testing
client, err := interactsh.New(&interactsh.Options{
ServerURL: "oast.fun",
CacheSize: 100,
Eviction: 60 * time.Second,
CooldownPeriod: 5 * time.Second,
PollDuration: 5 * time.Second,
DisableHttpFallback: true,
})
require.NoError(t, err, "could not create interactsh client")
defer client.Close()
result, urls := variables.EvaluateWithInteractsh(inputValues, client)
// The input override contains interactsh-url, so it should be replaced
// and we should have exactly 1 URL from the input override
require.Len(t, urls, 1, "should have 1 interactsh URL from input override")
// The result should use the input override (with interactsh replaced)
require.Contains(t, result["callback"], "https://custom.", "should use input override pattern")
require.Contains(t, result["callback"], "/path", "should use input override pattern")
require.NotContains(t, result["callback"], "{{interactsh-url}}", "interactsh should be replaced")
})
t.Run("interactsh-replacement-without-input-override", func(t *testing.T) {
variables := &Variable{
LazyEval: true,
InsertionOrderedStringMap: *utils.NewEmptyInsertionOrderedStringMap(1),
}
variables.Set("callback", "{{interactsh-url}}")
// No input override for "callback"
inputValues := map[string]interface{}{
"other_key": "other_value",
}
client, err := interactsh.New(&interactsh.Options{
ServerURL: "oast.fun",
CacheSize: 100,
Eviction: 60 * time.Second,
CooldownPeriod: 5 * time.Second,
PollDuration: 5 * time.Second,
DisableHttpFallback: true,
})
require.NoError(t, err, "could not create interactsh client")
defer client.Close()
result, urls := variables.EvaluateWithInteractsh(inputValues, client)
// Should have 1 URL from the variable definition
require.Len(t, urls, 1, "should have 1 interactsh URL")
// The result should be the replaced interactsh URL
require.NotContains(t, result["callback"], "{{interactsh-url}}", "interactsh should be replaced")
require.NotEmpty(t, result["callback"], "callback should have a value")
})
}

View File

@@ -84,10 +84,10 @@ func (r *requestGenerator) nextValue() (value string, payloads map[string]interf
r.applyMark(request, Once)
}
if hasPayloadIterator {
return request, generators.MergeMaps(r.currentPayloads), r.okCurrentPayload
return request, generators.CopyMap(r.currentPayloads), r.okCurrentPayload
}
// next should return a copy of payloads and not pointer to payload to avoid data race
return request, generators.MergeMaps(r.currentPayloads), true
return request, generators.CopyMap(r.currentPayloads), true
} else {
return "", nil, false
}

View File

@@ -184,6 +184,9 @@ type Options struct {
UpdateExisting bool `yaml:"update-existing" json:"update_existing"`
// URL is the URL of the jira server
URL string `yaml:"url" json:"url" validate:"required"`
// SiteURL is the browsable URL for the Jira instance (optional)
// If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com
SiteURL string `yaml:"site-url" json:"site_url"`
// AccountID is the accountID of the jira user.
AccountID string `yaml:"account-id" json:"account_id" validate:"required"`
// Email is the email of the user for jira instance
@@ -346,16 +349,26 @@ func (i *Integration) CreateNewIssue(event *output.ResultEvent) (*filters.Create
}
return nil, fmt.Errorf("%w => %s", err, data)
}
return getIssueResponseFromJira(createdIssue)
return i.getIssueResponseFromJira(createdIssue)
}
func getIssueResponseFromJira(issue *jira.Issue) (*filters.CreateIssueResponse, error) {
parsed, err := url.Parse(issue.Self)
if err != nil {
return nil, err
func (i *Integration) getIssueResponseFromJira(issue *jira.Issue) (*filters.CreateIssueResponse, error) {
var issueURL string
// Use SiteURL if provided, otherwise fall back to original issue.Self logic
if i.options.SiteURL != "" {
// Use the configured site URL for browsable links (useful for OAuth)
baseURL := strings.TrimRight(i.options.SiteURL, "/")
issueURL = fmt.Sprintf("%s/browse/%s", baseURL, issue.Key)
} else {
// Fall back to original logic using issue.Self
parsed, err := url.Parse(issue.Self)
if err != nil {
return nil, err
}
parsed.Path = fmt.Sprintf("/browse/%s", issue.Key)
issueURL = parsed.String()
}
parsed.Path = fmt.Sprintf("/browse/%s", issue.Key)
issueURL := parsed.String()
return &filters.CreateIssueResponse{
IssueID: issue.ID,
@@ -376,7 +389,7 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) (*filters.CreateIss
if err != nil {
return nil, errors.Wrap(err, "could not add comment to existing issue")
}
return getIssueResponseFromJira(&issue)
return i.getIssueResponseFromJira(&issue)
}
}
resp, err := i.CreateNewIssue(event)

View File

@@ -5,7 +5,7 @@ import "github.com/projectdiscovery/nuclei/v3/pkg/operators"
// Checks if template has matchers
func hasMatchers(all []*operators.Operators) bool {
for _, operator := range all {
if len(operator.Matchers) > 0 {
if operator != nil && len(operator.Matchers) > 0 {
return true
}
}

View File

@@ -0,0 +1,34 @@
package flow
import (
"testing"
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
"github.com/stretchr/testify/require"
)
func TestHasMatchersPanicRegression(t *testing.T) {
// This test ensures that hasMatchers does not panic when passed a slice containing nil.
// This was the source of a reported panic when a request had no local operators.
require.NotPanics(t, func() {
all := []*operators.Operators{nil}
result := hasMatchers(all)
require.False(t, result)
}, "hasMatchers should not panic with nil element in slice")
require.NotPanics(t, func() {
all := []*operators.Operators{nil, {}}
result := hasMatchers(all)
require.False(t, result)
}, "hasMatchers should not panic with mix of nil and empty operators")
}
func TestHasOperatorsPanicRegression(t *testing.T) {
// Also ensure hasOperators is safe
require.NotPanics(t, func() {
all := []*operators.Operators{nil}
result := hasOperators(all)
require.False(t, result)
}, "hasOperators should not panic with nil element in slice")
}