mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 02:08:33 -10:00
Compare commits
95
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
38f25f5263 | ||
|
|
0fc4a02a05 | ||
|
|
b649829bc3 | ||
|
|
9a3053e9be | ||
|
|
889972f3de | ||
|
|
f7d30f6389 | ||
|
|
36aae9cc55 | ||
|
|
a12bd9fbf8 | ||
|
|
4bb9374207 | ||
|
|
16735f5243 | ||
|
|
557b4fba38 | ||
|
|
1f985459b8 | ||
|
|
3a07fa9c22 | ||
|
|
b2d4efef1e | ||
|
|
36262a4152 | ||
|
|
7fb7b7ad92 | ||
|
|
a0ec8a0546 | ||
|
|
a229666d41 | ||
|
|
e0ea25d856 | ||
|
|
13dfe8802a | ||
|
|
97b8c510a1 | ||
|
|
fa6a7e8486 | ||
|
|
fbe2981e8d | ||
|
|
436c386272 | ||
|
|
28abf56e75 | ||
|
|
a09fcc196b | ||
|
|
2b4b058886 | ||
|
|
63687c2ce0 | ||
|
|
abfd43268a | ||
|
|
b046f7686f | ||
|
|
13af7ccd49 | ||
|
|
40c83ddb18 | ||
|
|
e07c61689e | ||
|
|
91a18c79fc | ||
|
|
c76421d3d8 | ||
|
|
26311c1668 | ||
|
|
e7d628c414 | ||
|
|
54e81c96f1 | ||
|
|
afc24616a4 | ||
|
|
ba6a050d48 | ||
|
|
3e4ec90cea | ||
|
|
9fdb5cd5b6 | ||
|
|
f0b87ac26e | ||
|
|
49f1de351f | ||
|
|
4cd697ed3f | ||
|
|
c096c7a808 | ||
|
|
cc1941b634 | ||
|
|
ba99750956 | ||
|
|
444f824167 | ||
|
|
31016178e2 | ||
|
|
529ac9d749 | ||
|
|
7547f1a5c3 | ||
|
|
6cb670d260 | ||
|
|
afb5a7ce59 | ||
|
|
465800fb21 | ||
|
|
267997ef60 | ||
|
|
4fcecb16d8 | ||
|
|
bc27db2541 | ||
|
|
402c286a8e | ||
|
|
97403c203e | ||
|
|
b57d086afc | ||
|
|
9339b53002 | ||
|
|
a1e5d181ec | ||
|
|
2f1398832a | ||
|
|
4da1554dc4 | ||
|
|
db002068c8 | ||
|
|
3b50a1fcdb | ||
|
|
4d02224880 | ||
|
|
2039b5c43e | ||
|
|
40a08a4db3 | ||
|
|
88b460c2e3 | ||
|
|
8a4be44631 | ||
|
|
49811d24a1 | ||
|
|
cb1bbff0f0 | ||
|
|
ebdabef420 | ||
|
|
0442aa79da | ||
|
|
7a9facfc26 | ||
|
|
abc63f1f8d | ||
|
|
f5f5f5fc21 | ||
|
|
efb27cfd49 | ||
|
|
5f952fc87a | ||
|
|
505ebc752f | ||
|
|
44f398c08d | ||
|
|
d7c8c8af80 | ||
|
|
7e4b4a8c55 | ||
|
|
92213e1335 | ||
|
|
159a8a53cf | ||
|
|
98948d0266 | ||
|
|
2c832f5590 | ||
|
|
53f56e179d | ||
|
|
cc5c5509dc | ||
|
|
aab2cadb64 | ||
|
|
4ef058758c | ||
|
|
ed3e06a701 | ||
|
|
d0463e8ca3 |
No files matched your search
@@ -1,10 +1,12 @@
|
||||
name: 🤖 dep auto merge
|
||||
name: 🤖 Auto Merge
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
workflow_run:
|
||||
workflows: ["♾️ Compatibility Check"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
@@ -12,11 +14,11 @@ permissions:
|
||||
repository-projects: write
|
||||
|
||||
jobs:
|
||||
automerge:
|
||||
auto-merge:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
name: 🔨 Build Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Builds
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
# required for running python code in py-snippet.yaml integration test
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Verify Go modules
|
||||
run: make verify
|
||||
|
||||
- name: Build
|
||||
run: go build .
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
run: go test ./...
|
||||
|
||||
- name: Integration Tests
|
||||
timeout-minutes: 50
|
||||
env:
|
||||
GH_ACTION: true
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
run: |
|
||||
chmod +x run.sh
|
||||
bash run.sh ${{ matrix.os }}
|
||||
working-directory: integration_tests/
|
||||
|
||||
- name: Race Condition Tests
|
||||
if: ${{ matrix.os != 'windows-latest' }} # known issue: https://github.com/golang/go/issues/46099
|
||||
run: go run -race . -l ../functional-test/targets.txt -id tech-detect,tls-version
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
- name: Example SDK Simple
|
||||
run: go run .
|
||||
working-directory: examples/simple/
|
||||
|
||||
# Temporarily disabled very flaky in github actions
|
||||
# - name: Example SDK Advanced
|
||||
# run: go run .
|
||||
# working-directory: examples/advanced/
|
||||
|
||||
- name: Example SDK with speed control
|
||||
run: go run .
|
||||
working-directory: examples/with_speed_control/
|
||||
@@ -1,39 +0,0 @@
|
||||
name: 🚨 CodeQL Analysis
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
@@ -0,0 +1,19 @@
|
||||
name: ♾️ Compatibility Check
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
check:
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: go mod download && go mod verify && go vet ./...
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 🌥 Docker Push
|
||||
name: 🐳 Docker Push
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
@@ -12,7 +12,7 @@ jobs:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- name: Git Checkout
|
||||
uses: actions/checkout@v3
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Get GitHub tag
|
||||
id: meta
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
name: 🧪 Functional Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
functional:
|
||||
name: Functional Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: Functional Tests
|
||||
env:
|
||||
GH_ACTION: true
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
run: |
|
||||
chmod +x run.sh
|
||||
bash run.sh ${{ matrix.os }}
|
||||
working-directory: cmd/functional-test
|
||||
@@ -0,0 +1,27 @@
|
||||
name: ⏰ Generate Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-docs:
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- run: make syntax-docs
|
||||
- run: git status -s | wc -l | xargs -I {} echo CHANGES={} >> $GITHUB_OUTPUT
|
||||
id: status
|
||||
- uses: projectdiscovery/actions/commit@v1
|
||||
if: steps.status.outputs.CHANGES > 0
|
||||
with:
|
||||
files: |
|
||||
SYNTAX-REFERENCE.md
|
||||
nuclei-jsonschema.json
|
||||
message: 'docs: update syntax & JSON schema 🤖'
|
||||
- run: git push origin $GITHUB_REF
|
||||
@@ -1,22 +0,0 @@
|
||||
name: 🙏🏻 Lint Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: projectdiscovery/actions/golangci-lint@v1
|
||||
@@ -0,0 +1,25 @@
|
||||
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"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make verify
|
||||
- name: Generate list
|
||||
run: for i in {1..${{ matrix.count }}}; do echo "https://scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
|
||||
- run: go run -race . -l "${LIST_FILE}"
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
name: 🔨 Performance Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Weekly
|
||||
- cron: '0 0 * * 0'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Performance
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
steps:
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: Verify Go modules
|
||||
run: make verify
|
||||
|
||||
# Max GH exection time 6H => timeout after that
|
||||
- name: Running performance with big list
|
||||
run: go run -race . -l ../functional-test/targets-150.txt
|
||||
working-directory: cmd/nuclei/
|
||||
@@ -1,47 +0,0 @@
|
||||
name: ⏰ Publish Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Set up Go"
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: "Set up Git"
|
||||
uses: projectdiscovery/actions/setup/git@v1
|
||||
|
||||
- name: Generate YAML Syntax Documentation
|
||||
id: generate-docs
|
||||
run: |
|
||||
if ! which dstdocgen > /dev/null; then
|
||||
echo -e "Command dstdocgen not found! Installing\c"
|
||||
go install github.com/projectdiscovery/yamldoc-go/cmd/docgen/dstdocgen@main
|
||||
fi
|
||||
go generate pkg/templates/templates.go
|
||||
go build -o "cmd/docgen/docgen" cmd/docgen/docgen.go
|
||||
./cmd/docgen/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
git status -s | wc -l | xargs -I {} echo CHANGES={} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit files
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
run: |
|
||||
git add SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
git commit -m "Auto Generate Syntax Docs + JSONSchema [$(date)] :robot:" -a
|
||||
|
||||
- name: Push changes
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -1,23 +0,0 @@
|
||||
name: 🔨 Release Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-test:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- name: "Check out code"
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: Release snapshot
|
||||
uses: projectdiscovery/actions/goreleaser@v1
|
||||
@@ -1,4 +1,4 @@
|
||||
name: 🎉 Release Binary
|
||||
name: 🎉 Release
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -13,10 +13,7 @@ jobs:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
with:
|
||||
release: true
|
||||
@@ -24,4 +21,4 @@ jobs:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}"
|
||||
DISCORD_WEBHOOK_ID: "${{ secrets.DISCORD_WEBHOOK_ID }}"
|
||||
DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}"
|
||||
DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}"
|
||||
@@ -1,22 +0,0 @@
|
||||
name: 🛠 Template Validate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
- name: Template Validation
|
||||
run: |
|
||||
go run . -ut
|
||||
go run . -validate
|
||||
go run . -validate -w workflows
|
||||
working-directory: cmd/nuclei/
|
||||
@@ -0,0 +1,135 @@
|
||||
name: 🔨 Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dev"]
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "Lint"
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/golangci-lint@v1
|
||||
|
||||
tests:
|
||||
name: "Tests"
|
||||
needs: ["lint"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make vet
|
||||
- run: make build
|
||||
- run: make test
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
- run: go run -race . -l ../functional-test/targets.txt -id tech-detect,tls-version
|
||||
if: ${{ matrix.os != 'windows-latest' }} # known issue: https://github.com/golang/go/issues/46099
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
sdk:
|
||||
name: "Run example SDK"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- name: "Simple"
|
||||
run: go run .
|
||||
working-directory: examples/simple/
|
||||
# - run: go run . # Temporarily disabled very flaky in github actions
|
||||
# working-directory: examples/advanced/
|
||||
- name: "with Speed Control"
|
||||
run: go run .
|
||||
working-directory: examples/with_speed_control/
|
||||
|
||||
integration:
|
||||
name: "Integration tests"
|
||||
needs: ["tests"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
- run: bash run.sh "${{ matrix.os }}"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
timeout-minutes: 50
|
||||
working-directory: integration_tests/
|
||||
|
||||
functional:
|
||||
name: "Functional tests"
|
||||
needs: ["tests"]
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: bash run.sh "${{ matrix.os }}"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
working-directory: cmd/functional-test/
|
||||
|
||||
validate:
|
||||
name: "Template validate"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make template-validate
|
||||
|
||||
codeql:
|
||||
name: "CodeQL analysis"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: 'go'
|
||||
- uses: github/codeql-action/autobuild@v2
|
||||
- uses: github/codeql-action/analyze@v2
|
||||
|
||||
release:
|
||||
name: "Release test"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
+20
-19
@@ -2,31 +2,32 @@
|
||||
**/*-cache
|
||||
**/*-config
|
||||
**/.cache
|
||||
*.DS_Store
|
||||
*.exe
|
||||
**/*.DS_Store
|
||||
**/*.exe
|
||||
.devcontainer
|
||||
.gitignore
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
# Binaries
|
||||
**/bindgen
|
||||
**/debug-*
|
||||
**/docgen
|
||||
**/functional-test
|
||||
**/fuzzplayground
|
||||
**/integration-test
|
||||
**/jsdocgen
|
||||
**/main
|
||||
**/memogen
|
||||
**/nuclei
|
||||
**/nuclei-stats*
|
||||
**/nuclei_dev
|
||||
**/nuclei_main
|
||||
**/scan-charts
|
||||
**/scrapefunc
|
||||
**/scrapefuncs
|
||||
**/tsgen
|
||||
/bin/*
|
||||
/bindgen
|
||||
/debug-*
|
||||
/docgen
|
||||
/functional-test
|
||||
/fuzzplayground
|
||||
/integration-test
|
||||
/jsdocgen
|
||||
/main
|
||||
/memogen
|
||||
/nuclei
|
||||
/nuclei-stats*
|
||||
/nuclei_dev
|
||||
/nuclei_main
|
||||
/scan-charts
|
||||
/scrapefunc
|
||||
/scrapefuncs
|
||||
/tsgen
|
||||
|
||||
# Templates
|
||||
/*.yaml
|
||||
|
||||
@@ -381,7 +381,7 @@ func (r *Request) Type() templateTypes.ProtocolType {
|
||||
}
|
||||
```
|
||||
|
||||
Almost all of these protocols have boilerplate functions for which default implementations have been provided in the `providers` package. Examples are the implementation of `Match`, `Extract`, `MakeResultEvent`, GetCompiledOperators`, etc. which are almost same throughout Nuclei protocols code. It is enough to copy-paste them unless customization is required.
|
||||
Almost all of these protocols have boilerplate functions for which default implementations have been provided in the `providers` package. Examples are the implementation of `Match`, `Extract`, `MakeResultEvent`, `GetCompiledOperators`, etc. which are almost same throughout Nuclei protocols code. It is enough to copy-paste them unless customization is required.
|
||||
|
||||
`eventcreator` package offers `CreateEventWithAdditionalOptions` function which can be used to create result events after doing request execution.
|
||||
|
||||
|
||||
@@ -15,8 +15,9 @@ ifneq ($(shell go env GOOS),darwin)
|
||||
endif
|
||||
|
||||
.PHONY: all build build-stats clean devtools-all devtools-bindgen devtools-scrapefuncs
|
||||
.PHONY: devtools-tsgen docs dsl-docs functional fuzzplayground go-build integration
|
||||
.PHONY: jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test tidy ts verify
|
||||
.PHONY: devtools-tsgen docs docgen dsl-docs functional fuzzplayground go-build syntax-docs
|
||||
.PHONY: integration jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test
|
||||
.PHONY: tidy ts verify download vet template-validate
|
||||
|
||||
all: build
|
||||
|
||||
@@ -28,42 +29,42 @@ go-build:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '${LDFLAGS}' $(GOBUILD_ADDITIONAL_ARGS) \
|
||||
-o '${GOBUILD_OUTPUT}' $(GOBUILD_PACKAGES)
|
||||
|
||||
build: GOBUILD_OUTPUT = nuclei
|
||||
build: GOBUILD_OUTPUT = ./bin/nuclei
|
||||
build: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
build: go-build
|
||||
|
||||
build-stats: GOBUILD_OUTPUT = nuclei-stats
|
||||
build-stats: GOBUILD_OUTPUT = ./bin/nuclei-stats
|
||||
build-stats: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
build-stats: GOBUILD_ADDITIONAL_ARGS = -tags=stats
|
||||
build-stats: go-build
|
||||
|
||||
scan-charts: GOBUILD_OUTPUT = scan-charts
|
||||
scan-charts: GOBUILD_OUTPUT = ./bin/scan-charts
|
||||
scan-charts: GOBUILD_PACKAGES = cmd/scan-charts/main.go
|
||||
scan-charts: go-build
|
||||
|
||||
docs: GOBUILD_OUTPUT = cmd/docgen/docgen
|
||||
docs: GOBUILD_PACKAGES = cmd/docgen/docgen.go
|
||||
docs: bin = dstdocgen
|
||||
docs:
|
||||
template-signer: GOBUILD_OUTPUT = ./bin/template-signer
|
||||
template-signer: GOBUILD_PACKAGES = cmd/tools/signer/main.go
|
||||
template-signer: go-build
|
||||
|
||||
docgen: GOBUILD_OUTPUT = ./bin/docgen
|
||||
docgen: GOBUILD_PACKAGES = cmd/docgen/docgen.go
|
||||
docgen: bin = dstdocgen
|
||||
docgen:
|
||||
@if ! which $(bin) >/dev/null; then \
|
||||
read -p "${bin} not found. Do you want to install it? (y/n) " answer; \
|
||||
if [ "$$answer" = "y" ]; then \
|
||||
echo "Installing ${bin}..."; \
|
||||
go get -v github.com/projectdiscovery/yamldoc-go/cmd/docgen/$(bin); \
|
||||
go install -v github.com/projectdiscovery/yamldoc-go/cmd/docgen/$(bin); \
|
||||
else \
|
||||
echo "Please install ${bin} manually."; \
|
||||
exit 1; \
|
||||
fi \
|
||||
echo "Command $(bin) not found! Installing..."; \
|
||||
go install -v github.com/projectdiscovery/yamldoc-go/cmd/docgen/$(bin)@latest; \
|
||||
fi
|
||||
|
||||
# TODO: Handle the panic, so that we just need to run `go install $(bin)@latest` (line 51-52)
|
||||
# TODO: FIX THIS PANIC
|
||||
$(GOCMD) generate pkg/templates/templates.go
|
||||
|
||||
$(GOBUILD) -o "${GOBUILD_OUTPUT}" $(GOBUILD_PACKAGES)
|
||||
./$(GOBUILD_OUTPUT) docs.md nuclei-jsonschema.json
|
||||
|
||||
git reset --hard # line 59
|
||||
docs: docgen
|
||||
docs:
|
||||
./bin/docgen docs.md nuclei-jsonschema.json
|
||||
|
||||
syntax-docs: docgen
|
||||
syntax-docs:
|
||||
./bin/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
|
||||
test: GOFLAGS = -race -v
|
||||
test:
|
||||
@@ -78,30 +79,36 @@ functional:
|
||||
tidy:
|
||||
$(GOMOD) tidy
|
||||
|
||||
verify: tidy
|
||||
download:
|
||||
$(GOMOD) download
|
||||
|
||||
verify: download
|
||||
$(GOMOD) verify
|
||||
|
||||
devtools-bindgen: GOBUILD_OUTPUT = bindgen
|
||||
vet: verify
|
||||
$(GOCMD) vet ./...
|
||||
|
||||
devtools-bindgen: GOBUILD_OUTPUT = ./bin/bindgen
|
||||
devtools-bindgen: GOBUILD_PACKAGES = pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
devtools-bindgen: go-build
|
||||
|
||||
devtools-tsgen: GOBUILD_OUTPUT = tsgen
|
||||
devtools-tsgen: GOBUILD_OUTPUT = ./bin/tsgen
|
||||
devtools-tsgen: GOBUILD_PACKAGES = pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
devtools-tsgen: go-build
|
||||
|
||||
devtools-scrapefuncs: GOBUILD_OUTPUT = scrapefuncs
|
||||
devtools-scrapefuncs: GOBUILD_OUTPUT = ./bin/scrapefuncs
|
||||
devtools-scrapefuncs: GOBUILD_PACKAGES = pkg/js/devtools/scrapefuncs/main.go
|
||||
devtools-scrapefuncs: go-build
|
||||
|
||||
devtools-all: devtools-bindgen devtools-tsgen devtools-scrapefuncs
|
||||
|
||||
jsupdate-bindgen: GOBUILD_OUTPUT = bindgen
|
||||
jsupdate-bindgen: GOBUILD_OUTPUT = ./bin/bindgen
|
||||
jsupdate-bindgen: GOBUILD_PACKAGES = pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
jsupdate-bindgen: go-build
|
||||
jsupdate-bindgen:
|
||||
./$(GOBUILD_OUTPUT) -dir pkg/js/libs -out pkg/js/generated
|
||||
|
||||
jsupdate-tsgen: GOBUILD_OUTPUT = tsgen
|
||||
jsupdate-tsgen: GOBUILD_OUTPUT = ./bin/tsgen
|
||||
jsupdate-tsgen: GOBUILD_PACKAGES = pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
jsupdate-tsgen: go-build
|
||||
jsupdate-tsgen:
|
||||
@@ -111,18 +118,24 @@ jsupdate-all: jsupdate-bindgen jsupdate-tsgen
|
||||
|
||||
ts: jsupdate-tsgen
|
||||
|
||||
fuzzplayground: GOBUILD_OUTPUT = fuzzplayground
|
||||
fuzzplayground: GOBUILD_OUTPUT = ./bin/fuzzplayground
|
||||
fuzzplayground: GOBUILD_PACKAGES = cmd/tools/fuzzplayground/main.go
|
||||
fuzzplayground: LDFLAGS = -s -w
|
||||
fuzzplayground: go-build
|
||||
|
||||
memogen: GOBUILD_OUTPUT = memogen
|
||||
memogen: GOBUILD_OUTPUT = ./bin/memogen
|
||||
memogen: GOBUILD_PACKAGES = cmd/memogen/memogen.go
|
||||
memogen: go-build
|
||||
memogen:
|
||||
./$(GOBUILD_OUTPUT) -src pkg/js/libs -tpl cmd/memogen/function.tpl
|
||||
|
||||
dsl-docs: GOBUILD_OUTPUT = scrapefuncs
|
||||
dsl-docs: GOBUILD_OUTPUT = ./bin/scrapefuncs
|
||||
dsl-docs: GOBUILD_PACKAGES = pkg/js/devtools/scrapefuncs/main.go
|
||||
dsl-docs:
|
||||
./$(GOBUILD_OUTPUT) -out dsl.md
|
||||
|
||||
template-validate: build
|
||||
template-validate:
|
||||
./bin/nuclei -ut
|
||||
./bin/nuclei -validate
|
||||
./bin/nuclei -validate -w workflows
|
||||
@@ -1,30 +1,4 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://nuclei.projectdiscovery.io"><img src="static/nuclei-logo.png" width="200px" alt="Nuclei"></a>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Fast and customisable vulnerability scanner based on simple YAML based DSL.</h4>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#how-it-works">How</a> •
|
||||
<a href="#install-nuclei">Install</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">Documentation</a> •
|
||||
<a href="#credits">Credits</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">FAQs</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Join Discord</a>
|
||||
</p>
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
@@ -34,78 +8,103 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_JP.md">日本語</a>
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<img src="https://img.shields.io/badge/go-1.21-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white">
|
||||
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/overview?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<a href="https://github.com/projectdiscovery/nuclei-templates"><img src="https://img.shields.io/badge/Templates Library-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNoaWVsZCI+PHBhdGggZD0iTTIwIDEzYzAgNS0zLjUgNy41LTcuNjYgOC45NWExIDEgMCAwIDEtLjY3LS4wMUM3LjUgMjAuNSA0IDE4IDQgMTNWNmExIDEgMCAwIDEgMS0xYzIgMCA0LjUtMS4yIDYuMjQtMi43MmExLjE3IDEuMTcgMCAwIDEgMS41MiAwQzE0LjUxIDMuODEgMTcgNSAxOSA1YTEgMSAwIDAgMSAxIDF6Ii8+PC9zdmc+&logoColor=white"></a>
|
||||
|
||||
<a href="https://discord.gg/projectdiscovery?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme"><img src="https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white"></a>
|
||||
|
||||
---
|
||||
|
||||
Nuclei is used to send requests across targets based on a template, leading to zero false positives and providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols, including TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless, Code etc. With powerful and flexible templating, Nuclei can be used to model all kinds of security checks.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
We have a [dedicated repository](https://github.com/projectdiscovery/nuclei-templates) that houses various type of vulnerability templates contributed by **more than 300** security researchers and engineers.
|
||||
Nuclei is a modern, high-performance vulnerability scanner that leverages simple YAML-based templates. It empowers you to design custom vulnerability detection scenarios that mimic real-world conditions, leading to zero false positives.
|
||||
|
||||
## How it works
|
||||
- Simple YAML format for creating and customizing vulnerability templates.
|
||||
- Contributed by thousands of security professionals to tackle trending vulnerabilities.
|
||||
- Reduce false positives by simulating real-world steps to verify a vulnerability.
|
||||
- Ultra-fast parallel scan processing and request clustering.
|
||||
- Integrate into CI/CD pipelines for vulnerability detection and regression testing.
|
||||
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS JavaScript, Code and more.
|
||||
- Integrate with Jira, Splunk, GitHub, Elastic, GitLab.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
- [Get Started](#get-started)
|
||||
- [1. Nuclei CLI](#1-nuclei-cli)
|
||||
- [2. Pro and Enterprise Editions](#2-pro-and-enterprise-editions)
|
||||
- [Documentation](#documentation)
|
||||
- [Command Line Flags](#command-line-flags)
|
||||
- [Single target scan](#single-target-scan)
|
||||
- [Scanning multiple targets](#scanning-multiple-targets)
|
||||
- [Network scan](#network-scan)
|
||||
- [Scanning with your custom template](#scanning-with-your-custom-template)
|
||||
- [Connect Nuclei to ProjectDiscovery](#connect-nuclei-to-projectdiscovery)
|
||||
- [Nuclei Templates, Community and Rewards 💎](#nuclei-templates-community-and-rewards-)
|
||||
- [Our Mission](#our-mission)
|
||||
- [Contributors ❤️](#contributors-️)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
|
||||
| :exclamation: **Disclaimer** |
|
||||
|---------------------------------|
|
||||
| **This project is in active development**. Expect breaking changes with releases. Review the release changelog before updating. |
|
||||
| This project was primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
| This project is primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
|
||||
# Install Nuclei
|
||||
## Get Started
|
||||
|
||||
Nuclei requires **go1.21** to install successfully. Run the following command to install the latest version -
|
||||
### **1. Nuclei CLI**
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
Install Nuclei on your machine. Get started by following the installation guide [here](https://docs.projectdiscovery.io/tools/nuclei/install?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme). Additionally, We provide [a free cloud tier](https://cloud.projectdiscovery.io/sign-up) and comes with a generous monthly free limits:
|
||||
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
- Store and visualize your vulnerability findings
|
||||
- Write and manage your nuclei templates
|
||||
- Access latest nuclei templates
|
||||
- Discover and store your targets
|
||||
|
||||
**More installation [methods can be found here](https://docs.projectdiscovery.io/tools/nuclei/install).**
|
||||
### **2. Pro and Enterprise Editions**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
For security teams and enterprises, we provide a cloud-hosted service built on top of Nuclei OSS, fine-tuned to help you continuously run vulnerability scans at scale with your team and existing workflows:
|
||||
|
||||
### Nuclei Templates
|
||||
- 50x faster scans
|
||||
- Large scale scanning with high accuracy
|
||||
- Integrations with cloud services (AWS, GCP, Azure, CloudFlare, Fastly, Terraform, Kubernetes)
|
||||
- Jira, Slack, Linear, APIs and Webhooks
|
||||
- Executive and compliance reporting
|
||||
- Plus: Real-time scanning, SAML SSO, SOC 2 compliant platform (with EU and US hosting options), shared team workspaces, and more
|
||||
- We're constantly [adding new features](https://feedback.projectdiscovery.io/changelog)!
|
||||
- **Ideal for:** Pentesters, security teams, and enterprises
|
||||
|
||||
Nuclei has built-in support for automatic template download/update as default since version [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2). [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) project provides a community-contributed list of ready-to-use templates that is constantly updated.
|
||||
## Documentation
|
||||
|
||||
You may still use the `update-templates` flag to update the nuclei templates at any time; You can write your own checks for your individual workflow and needs following Nuclei's [templating guide](https://docs.projectdiscovery.io/templates/).
|
||||
Browse the full Nuclei [documentation here](https://docs.projectdiscovery.io/tools/nuclei/running). If you’re new to Nuclei, check out our [foundational Youtube series.](https://www.youtube.com/playlist?list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl)
|
||||
|
||||
The YAML DSL reference syntax is available [here](SYNTAX-REFERENCE.md).
|
||||
<p align="center">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="https://www.youtube.com/watch?v=b5qMyQvL1ZA&list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl&utm_source=github&utm_medium=web&utm_campaign=nuclei_readme" target="_blank"><img src="/static/nuclei-getting-started.png" width="350px"></a> <a href="https://www.youtube.com/watch?v=nFXygQdtjyw&utm_source=github&utm_medium=web&utm_campaign=nuclei_readme" target="_blank"><img src="/static/nuclei-write-your-first-template.png" width="350px"></a>
|
||||
|
||||
### Usage
|
||||
</p>
|
||||
|
||||
### Command Line Flags
|
||||
|
||||
To display all the flags for the tool:
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
This will display help for the tool. Here are all the switches it supports.
|
||||
|
||||
<details>
|
||||
<summary>Expand full help flags</summary>
|
||||
|
||||
```console
|
||||
Nuclei is a fast, template based vulnerability scanner focusing
|
||||
@@ -279,23 +278,24 @@ HEADLESS:
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string optional nuclei memory profile dump file
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string generate memory (heap) profile & trace files
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-vdl, -var-dump-limit int limit the number of characters displayed in var dump (default 255)
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
|
||||
UPDATE:
|
||||
-up, -update update nuclei engine to the latest released version
|
||||
@@ -310,11 +310,13 @@ STATISTICS:
|
||||
-mp, -metrics-port int port to expose nuclei metrics on (default 9092)
|
||||
|
||||
CLOUD:
|
||||
-auth configure projectdiscovery cloud (pdcp) api key (default true)
|
||||
-tid, -team-id string upload scan results to given team id (optional) (default "none")
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard
|
||||
-sid, -scan-id string upload scan results to existing scan id (optional)
|
||||
-sname, -scan-name string scan name to set (optional)
|
||||
-auth configure projectdiscovery cloud (pdcp) api key (default true)
|
||||
-tid, -team-id string upload scan results to given team id (optional) (default "none")
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard [DEPRECATED use -dashboard]
|
||||
-sid, -scan-id string upload scan results to existing scan id (optional)
|
||||
-sname, -scan-name string scan name to set (optional)
|
||||
-pd, -dashboard upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard
|
||||
-pdu, -dashboard-upload string upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard
|
||||
|
||||
AUTHENTICATION:
|
||||
-sf, -secret-file string[] path to config file containing secrets for nuclei authenticated scan
|
||||
@@ -323,59 +325,189 @@ AUTHENTICATION:
|
||||
|
||||
EXAMPLES:
|
||||
Run nuclei on single host:
|
||||
$ nuclei -target example.com
|
||||
$ nuclei -target example.com
|
||||
|
||||
Run nuclei with specific template directories:
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
|
||||
Run nuclei against a list of hosts:
|
||||
$ nuclei -list hosts.txt
|
||||
$ nuclei -list hosts.txt
|
||||
|
||||
Run nuclei with a JSON output:
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
|
||||
Run nuclei with sorted Markdown outputs (with environment variables):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Running Nuclei
|
||||
Additional documentation is available at: [https://docs.nuclei.sh/getting-started/running](https://docs.nuclei.sh/getting-started/running?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme)
|
||||
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/running for details on running Nuclei
|
||||
</details>
|
||||
|
||||
### Using Nuclei From Go Code
|
||||
### Single target scan
|
||||
|
||||
Complete guide of using Nuclei as Library/SDK is available at [godoc](https://pkg.go.dev/github.com/projectdiscovery/nuclei/v3/lib#section-readme)
|
||||
To perform a quick scan on web-application:
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com
|
||||
```
|
||||
|
||||
### Scanning multiple targets
|
||||
|
||||
Nuclei can handle bulk scanning by providing a list of targets. You can use a file containing multiple URLs.
|
||||
|
||||
```sh
|
||||
nuclei -targets urls.txt
|
||||
```
|
||||
|
||||
### Network scan
|
||||
|
||||
This will scan the entire subnet for network-related issues, such as open ports or misconfigured services.
|
||||
|
||||
```sh
|
||||
nuclei -target 192.168.1.0/24
|
||||
```
|
||||
|
||||
### Scanning with your custom template
|
||||
|
||||
To write and use your own template, create a `.yaml` file with specific rules, then use it as follows.
|
||||
|
||||
```sh
|
||||
nuclei -u https://example.com -t /path/to/your-template.yaml
|
||||
```
|
||||
|
||||
### Connect Nuclei to ProjectDiscovery
|
||||
|
||||
You can run the scans on your machine and upload the results to the cloud platform for further analysis and remediation.
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com -dashboard
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This feature is absolutely free and does not require any subscription. For a detailed guide, refer to the [documentation](https://docs.projectdiscovery.io/cloud/scanning/nuclei-scan?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme).
|
||||
|
||||
## Nuclei Templates, Community and Rewards 💎
|
||||
[Nuclei templates](https://github.com/projectdiscovery/nuclei-templates) are based on the concepts of YAML based template files that define how the requests will be sent and processed. This allows easy extensibility capabilities to nuclei. The templates are written in YAML which specifies a simple human-readable format to quickly define the execution process.
|
||||
|
||||
Try it online with our free AI powered Nuclei Templates Editor by [clicking here.](https://cloud.projectdiscovery.io/templates)
|
||||
|
||||
Nuclei Templates offer a streamlined way to identify and communicate vulnerabilities, combining essential details like severity ratings and detection methods. This open-source, community-developed tool accelerates threat response and is widely recognized in the cybersecurity world. Nuclei templates are actively contributed by thousands of security researchers globally. We run two programs for our contributors: [Pioneers](https://projectdiscovery.io/pioneers) and [💎 bounties](https://github.com/projectdiscovery/nuclei-templates/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20Bounty%22).
|
||||
|
||||
|
||||
### Resources
|
||||
<p align="left">
|
||||
<a href="/static/nuclei-templates-teamcity.png" target="_blank"><img src="/static/nuclei-templates-teamcity.png" width="1200px" alt="Nuclei template example for detecting TeamCity misconfiguration" /></a>
|
||||
</p>
|
||||
|
||||
You can access the main documentation for Nuclei at https://docs.projectdiscovery.io/tools/nuclei/, and learn more about Nuclei in the cloud with [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io)
|
||||
#### Examples
|
||||
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/resources for more resources and videos about Nuclei!
|
||||
Visit [our documentation](https://docs.projectdiscovery.io/templates/introduction) for use cases and ideas.
|
||||
|
||||
### Credits
|
||||
| Use case | Nuclei template |
|
||||
| :----------------------------------- | :------------------------------------------------- |
|
||||
| Detect known CVEs | **[CVE-2021-44228 (Log4Shell)](https://cloud.projectdiscovery.io/public/CVE-2021-45046)** |
|
||||
| Identify Out-of-Band vulnerabilities | **[Blind SQL Injection via OOB](https://cloud.projectdiscovery.io/public/CVE-2024-22120)** |
|
||||
| SQL Injection detection | **[Generic SQL Injection](https://cloud.projectdiscovery.io/public/CVE-2022-34265)** |
|
||||
| Cross-Site Scripting (XSS) | **[Reflected XSS Detection](https://cloud.projectdiscovery.io/public/CVE-2023-4173)** |
|
||||
| Default or weak passwords | **[Default Credentials Check](https://cloud.projectdiscovery.io/public/airflow-default-login)** |
|
||||
| Secret files or data exposure | **[Sensitive File Disclosure](https://cloud.projectdiscovery.io/public/airflow-configuration-exposure)** |
|
||||
| Identify open redirects | **[Open Redirect Detection](https://cloud.projectdiscovery.io/public/open-redirect)** |
|
||||
| Detect subdomain takeovers | **[Subdomain Takeover Templates](https://cloud.projectdiscovery.io/public/azure-takeover-detection)** |
|
||||
| Security misconfigurations | **[Unprotected Jenkins Console](https://cloud.projectdiscovery.io/public/unauthenticated-jenkins)** |
|
||||
| Weak SSL/TLS configurations | **[SSL Certificate Expiry](https://cloud.projectdiscovery.io/public/expired-ssl)** |
|
||||
| Misconfigured cloud services | **[Open S3 Bucket Detection](https://cloud.projectdiscovery.io/public/s3-public-read-acp)** |
|
||||
| Remote code execution vulnerabilities| **[RCE Detection Templates](https://cloud.projectdiscovery.io/public/CVE-2024-29824)** |
|
||||
| Directory traversal attacks | **[Path Traversal Detection](https://cloud.projectdiscovery.io/public/oracle-fatwire-lfi)** |
|
||||
| File inclusion vulnerabilities | **[Local/Remote File Inclusion](https://cloud.projectdiscovery.io/public/CVE-2023-6977)** |
|
||||
|
||||
|
||||
## Our Mission
|
||||
|
||||
Traditional vulnerability scanners were built decades ago. They are closed-source, incredibly slow, and vendor-driven. Today's attackers are mass exploiting newly released CVEs across the internet within days, unlike the years it used to take. This shift requires a completely different approach to tackling trending exploits on the internet.
|
||||
|
||||
We built Nuclei to solve this challenge. We made the entire scanning engine framework open and customizable—allowing the global security community to collaborate and tackle the trending attack vectors and vulnerabilities on the internet. Nuclei is now used and contributed by Fortune 500 enterprises, government agencies, universities.
|
||||
|
||||
You can participate by contributing to our code, [templates library](https://github.com/projectdiscovery/nuclei-templates), or [joining our team.](https://projectdiscovery.io/)
|
||||
|
||||
## Contributors :heart:
|
||||
|
||||
Thanks to all the amazing [community contributors for sending PRs](https://github.com/projectdiscovery/nuclei/graphs/contributors) and keeping this project updated. :heart:
|
||||
|
||||
If you have an idea or some kind of improvement, you are welcome to contribute and participate in the Project, feel free to send your PR.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=projectdiscovery/nuclei&max=500">
|
||||
</a>
|
||||
<p align="left">
|
||||
<a href="https://github.com/Ice3man543"><img src="https://avatars.githubusercontent.com/u/22318055?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehsandeep"><img src="https://avatars.githubusercontent.com/u/8293321?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Mzack9999"><img src="https://avatars.githubusercontent.com/u/13421144?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/forgedhallpass"><img src="https://avatars.githubusercontent.com/u/13679401?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tarunKoyalwar"><img src="https://avatars.githubusercontent.com/u/45962551?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/manuelbua"><img src="https://avatars.githubusercontent.com/u/819314?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dogancanbakir"><img src="https://avatars.githubusercontent.com/u/65292895?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parrasajad"><img src="https://avatars.githubusercontent.com/u/16835787?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vzamanillo"><img src="https://avatars.githubusercontent.com/u/10209695?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ShubhamRasal"><img src="https://avatars.githubusercontent.com/u/45902122?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/RamanaReddy0M"><img src="https://avatars.githubusercontent.com/u/90540245?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/LuitelSamikshya"><img src="https://avatars.githubusercontent.com/u/85764322?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kchason"><img src="https://avatars.githubusercontent.com/u/1111099?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/pmareke"><img src="https://avatars.githubusercontent.com/u/3502075?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dwisiswant0"><img src="https://avatars.githubusercontent.com/u/25837540?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/xm1k3"><img src="https://avatars.githubusercontent.com/u/73166077?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/5amu"><img src="https://avatars.githubusercontent.com/u/39925709?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehrishirajsharma"><img src="https://avatars.githubusercontent.com/u/35542790?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zerodivisi0n"><img src="https://avatars.githubusercontent.com/u/687694?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/geeknik"><img src="https://avatars.githubusercontent.com/u/466878?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/TerminalFi"><img src="https://avatars.githubusercontent.com/u/32599364?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KaulSe"><img src="https://avatars.githubusercontent.com/u/45340011?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MachadoOtto"><img src="https://avatars.githubusercontent.com/u/93268441?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jonathanwalker"><img src="https://avatars.githubusercontent.com/u/14978093?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/nHurD"><img src="https://avatars.githubusercontent.com/u/233374?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jessekelly881"><img src="https://avatars.githubusercontent.com/u/22938931?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/JaneX8"><img src="https://avatars.githubusercontent.com/u/5116641?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/atomiczsec"><img src="https://avatars.githubusercontent.com/u/75549184?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/M-Faheem-Khan"><img src="https://avatars.githubusercontent.com/u/17150767?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/denandz"><img src="https://avatars.githubusercontent.com/u/5291556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tibbon"><img src="https://avatars.githubusercontent.com/u/82880?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dany74q"><img src="https://avatars.githubusercontent.com/u/2129762?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0x123456789"><img src="https://avatars.githubusercontent.com/u/36066426?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/danigoland"><img src="https://avatars.githubusercontent.com/u/15079567?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ChrisMandich"><img src="https://avatars.githubusercontent.com/u/14286797?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/austintraver"><img src="https://avatars.githubusercontent.com/u/25112463?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/socialsister"><img src="https://avatars.githubusercontent.com/u/155628741?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Anemys"><img src="https://avatars.githubusercontent.com/u/51196227?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/andreangelucci"><img src="https://avatars.githubusercontent.com/u/18552197?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/AlexS778"><img src="https://avatars.githubusercontent.com/u/98418121?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/noraj"><img src="https://avatars.githubusercontent.com/u/16578570?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/akkuman"><img src="https://avatars.githubusercontent.com/u/7813511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zrquan"><img src="https://avatars.githubusercontent.com/u/33086594?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/1efty"><img src="https://avatars.githubusercontent.com/u/18194777?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/rsrdesarrollo"><img src="https://avatars.githubusercontent.com/u/5142014?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/razin99"><img src="https://avatars.githubusercontent.com/u/44442082?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MetzinAround"><img src="https://avatars.githubusercontent.com/u/65838556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vil02"><img src="https://avatars.githubusercontent.com/u/65706193?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mrschyte"><img src="https://avatars.githubusercontent.com/u/8571?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/PeterDaveHello"><img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parthmalhotra"><img src="https://avatars.githubusercontent.com/u/28601533?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/owenrumney"><img src="https://avatars.githubusercontent.com/u/3049157?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Ovi3"><img src="https://avatars.githubusercontent.com/u/29408109?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Bisstocuz"><img src="https://avatars.githubusercontent.com/u/42398278?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/daffainfo"><img src="https://avatars.githubusercontent.com/u/36522826?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mhmdiaa"><img src="https://avatars.githubusercontent.com/u/19687798?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MiryangJung"><img src="https://avatars.githubusercontent.com/u/48237511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0xmin"><img src="https://avatars.githubusercontent.com/u/44919834?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/maikthulhu"><img src="https://avatars.githubusercontent.com/u/680830?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/sttlr"><img src="https://avatars.githubusercontent.com/u/40246850?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/iamRjarpan"><img src="https://avatars.githubusercontent.com/u/45498226?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/leoloobeek"><img src="https://avatars.githubusercontent.com/u/8801754?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KristinnVikar"><img src="https://avatars.githubusercontent.com/u/93918469?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kant01ne"><img src="https://avatars.githubusercontent.com/u/5072452?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KeisukeYamashita"><img src="https://avatars.githubusercontent.com/u/23056537?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
</p>
|
||||
|
||||
## License
|
||||
|
||||
Do also check out the below similar open-source projects that may fit in your workflow:
|
||||
Nuclei is distributed under [MIT License](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md).
|
||||
|
||||
[FFuF](https://github.com/ffuf/ffuf), [Qsfuzz](https://github.com/ameenmaali/qsfuzz), [Inception](https://github.com/proabiral/inception), [Snallygaster](https://github.com/hannob/snallygaster), [Gofingerprint](https://github.com/Static-Flow/gofingerprint), [Sn1per](https://github.com/1N3/Sn1per/tree/master/templates), [Google tsunami](https://github.com/google/tsunami-security-scanner), [Jaeles](https://github.com/jaeles-project/jaeles), [ChopChop](https://github.com/michelin/ChopChop)
|
||||
|
||||
### License
|
||||
|
||||
Nuclei is distributed under [MIT License](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://docs.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Check Nuclei Documentation"></a>
|
||||
</h1>
|
||||
<img src="https://img.shields.io/badge/license-MIT-000000.svg?style=for-the-badge">
|
||||
@@ -1373,6 +1373,19 @@ Fuzzing describes schema to fuzz http requests
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>analyzer</code> <i><a href="#analyzersanalyzertemplate">analyzers.AnalyzerTemplate</a></i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Analyzer is an analyzer to use for matching the response.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>self-contained</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
@@ -1651,6 +1664,19 @@ FuzzPreConditionOperator is the operator between multiple PreConditions for fuzz
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>global-matchers</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
GlobalMatchers marks matchers as static and applies globally to all result events from other templates
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2012,6 +2038,59 @@ Appears in:
|
||||
|
||||
|
||||
|
||||
## analyzers.AnalyzerTemplate
|
||||
AnalyzerTemplate is the template for the analyzer
|
||||
|
||||
Appears in:
|
||||
|
||||
|
||||
- <code><a href="#httprequest">http.Request</a>.analyzer</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>name</code> <i>string</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Name is the name of the analyzer to use
|
||||
|
||||
|
||||
Valid values:
|
||||
|
||||
|
||||
- <code>time_delay</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>parameters</code> <i>map[string]interface{}</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Parameters is the parameters for the analyzer
|
||||
|
||||
Parameters are different for each analyzer. For example, you can customize
|
||||
time_delay analyzer with sleep_duration, time_slope_error_range, etc. Refer
|
||||
to the docs for each analyzer to get an idea about parameters.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## SignatureTypeHolder
|
||||
SignatureTypeHolder is used to hold internal type of the signature
|
||||
|
||||
@@ -3210,6 +3289,19 @@ read-all: false
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>stop-at-first-match</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
@@ -15,9 +16,8 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
githubAction = os.Getenv("GH_ACTION") == "true"
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
|
||||
mainNucleiBinary = flag.String("main", "", "Main Branch Nuclei Binary")
|
||||
devNucleiBinary = flag.String("dev", "", "Dev Branch Nuclei Binary")
|
||||
@@ -45,7 +45,7 @@ func runFunctionalTests(debug bool) (error, bool) {
|
||||
|
||||
errored, failedTestCases := runTestCases(file, debug)
|
||||
|
||||
if githubAction {
|
||||
if ci.IsCI() {
|
||||
fmt.Println("::group::Failed tests with debug")
|
||||
for _, failedTestCase := range failedTestCases {
|
||||
_ = runTestCase(failedTestCase, true)
|
||||
|
||||
@@ -99,7 +99,7 @@ type codePreCondition struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *codePreCondition) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiArgsWithEnvAndGetResults(debug, getEnvValues(), "-t", filePath, "-u", "input", "-code")
|
||||
results, err := testutils.RunNucleiArgsWithEnvAndGetResults(debug, getEnvValues(), "-t", filePath, "-u", "input", "-code", "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ type fileWithOrMatcher struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *fileWithOrMatcher) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -27,7 +27,7 @@ type fileWithAndMatcher struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *fileWithAndMatcher) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -39,7 +39,7 @@ type fileWithExtractor struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *fileWithExtractor) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "protocols/file/data/", debug, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -22,7 +22,7 @@ var flowTestcases = []TestCaseInfo{
|
||||
type conditionalFlow struct{}
|
||||
|
||||
func (t *conditionalFlow) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "blog.projectdiscovery.io", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "cloud.projectdiscovery.io", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -155,7 +155,7 @@ func (h *httpInteractshRequest) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
return expectResultsCount(results, 1, 2)
|
||||
}
|
||||
|
||||
type httpDefaultMatcherCondition struct{}
|
||||
@@ -952,7 +952,7 @@ func (h *httpRequestSelfContained) Execute(filePath string) error {
|
||||
}()
|
||||
defer server.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -988,7 +988,7 @@ func (h *httpRequestSelfContainedWithParams) Execute(filePath string) error {
|
||||
}()
|
||||
defer server.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1031,7 +1031,7 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
}
|
||||
defer FileLoc.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-V", "test="+FileLoc.Name())
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-V", "test="+FileLoc.Name(), "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
"github.com/logrusorgru/aurora"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -22,10 +23,9 @@ type TestCaseInfo struct {
|
||||
}
|
||||
|
||||
var (
|
||||
debug = os.Getenv("DEBUG") == "true"
|
||||
githubAction = os.Getenv("GH_ACTION") == "true"
|
||||
customTests = os.Getenv("TESTS")
|
||||
protocol = os.Getenv("PROTO")
|
||||
debug = os.Getenv("DEBUG") == "true"
|
||||
customTests = os.Getenv("TESTS")
|
||||
protocol = os.Getenv("PROTO")
|
||||
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
@@ -103,11 +103,19 @@ func main() {
|
||||
failedTestTemplatePaths := runTests(customTestsList)
|
||||
|
||||
if len(failedTestTemplatePaths) > 0 {
|
||||
if githubAction {
|
||||
debug = true
|
||||
fmt.Println("::group::Failed integration tests in debug mode")
|
||||
_ = runTests(failedTestTemplatePaths)
|
||||
if ci.IsCI() {
|
||||
// run failed tests again assuming they are flaky
|
||||
// if they fail as well only then we assume that there is an actual issue
|
||||
fmt.Println("::group::Running failed tests again")
|
||||
failedTestTemplatePaths = runTests(failedTestTemplatePaths)
|
||||
fmt.Println("::endgroup::")
|
||||
|
||||
if len(failedTestTemplatePaths) > 0 {
|
||||
debug = true
|
||||
fmt.Println("::group::Failed integration tests in debug mode")
|
||||
_ = runTests(failedTestTemplatePaths)
|
||||
fmt.Println("::endgroup::")
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
|
||||
@@ -14,7 +14,7 @@ type multiProtoDynamicExtractor struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *multiProtoDynamicExtractor) Execute(templatePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "blog.projectdiscovery.io", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "docs.projectdiscovery.io", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (h *networkRequestSelContained) Execute(filePath string) error {
|
||||
_, _ = conn.Write([]byte("Authentication successful"))
|
||||
})
|
||||
defer ts.Close()
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -263,6 +263,9 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVar(&options.SignTemplates, "sign", false, "signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable"),
|
||||
flagSet.BoolVar(&options.EnableCodeTemplates, "code", false, "enable loading code protocol-based templates"),
|
||||
flagSet.BoolVarP(&options.DisableUnsignedTemplates, "disable-unsigned-templates", "dut", false, "disable running unsigned templates or templates with mismatched signature"),
|
||||
flagSet.BoolVarP(&options.EnableSelfContainedTemplates, "enable-self-contained", "esc", false, "enable loading self-contained templates"),
|
||||
flagSet.BoolVarP(&options.EnableGlobalMatchersTemplates, "enable-global-matchers", "egm", false, "enable loading global matchers templates"),
|
||||
flagSet.BoolVar(&options.EnableFileTemplates, "file", false, "enable loading file templates"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("filters", "Filtering",
|
||||
@@ -425,6 +428,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "generate memory (heap) profile & trace files"),
|
||||
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "svd", false, "show variables dump for debugging"),
|
||||
flagSet.IntVarP(&options.VarDumpLimit, "var-dump-limit", "vdl", 255, "limit the number of characters displayed in var dump"),
|
||||
flagSet.BoolVarP(&options.EnablePprof, "enable-pprof", "ep", false, "enable pprof debugging server"),
|
||||
flagSet.CallbackVarP(printTemplateVersion, "templates-version", "tv", "shows the version of the installed nuclei-templates"),
|
||||
flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"),
|
||||
@@ -491,6 +495,11 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
options.DAST = true
|
||||
}
|
||||
|
||||
// All cloud-based templates depend on both code and self-contained templates.
|
||||
if options.EnableCodeTemplates {
|
||||
options.EnableSelfContainedTemplates = true
|
||||
}
|
||||
|
||||
// api key hierarchy: cli flag > env var > .pdcp/credential file
|
||||
if pdcpauth == "true" {
|
||||
runner.AuthWithPDCP()
|
||||
|
||||
@@ -69,6 +69,7 @@ func init() {
|
||||
// need to set headless to true for headless templates
|
||||
defaultOpts.Headless = true
|
||||
defaultOpts.EnableCodeTemplates = true
|
||||
defaultOpts.EnableSelfContainedTemplates = true
|
||||
if err := protocolstate.Init(defaultOpts); err != nil {
|
||||
gologger.Fatal().Msgf("Could not initialize protocol state: %s\n", err)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"flag"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/gologger/levels"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
folderutil "github.com/projectdiscovery/utils/folder"
|
||||
)
|
||||
|
||||
var (
|
||||
appConfigDir = folderutil.AppConfigDirOrDefault(".config", "nuclei")
|
||||
defaultCertFile = filepath.Join(appConfigDir, "keys", "nuclei-user.crt")
|
||||
defaultPrivKey = filepath.Join(appConfigDir, "keys", "nuclei-user-private-key.pem")
|
||||
)
|
||||
|
||||
var (
|
||||
template string
|
||||
cert string
|
||||
privKey string
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&template, "template", "", "template to sign (file only)")
|
||||
flag.StringVar(&cert, "cert", defaultCertFile, "certificate file")
|
||||
flag.StringVar(&privKey, "priv-key", defaultPrivKey, "private key file")
|
||||
flag.Parse()
|
||||
|
||||
config.DefaultConfig.LogAllEvents = true
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose)
|
||||
|
||||
if template == "" {
|
||||
gologger.Fatal().Msg("template is required")
|
||||
}
|
||||
if !fileutil.FileExists(template) {
|
||||
gologger.Fatal().Msgf("template file %s does not exist or not a file", template)
|
||||
}
|
||||
|
||||
// get signer
|
||||
tmplSigner, err := signer.NewTemplateSignerFromFiles(cert, privKey)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to create signer: %s", err)
|
||||
}
|
||||
gologger.Info().Msgf("Template Signer: %v\n", tmplSigner.Identifier())
|
||||
|
||||
// read file
|
||||
bin, err := os.ReadFile(template)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to read template file %s: %s", template, err)
|
||||
}
|
||||
|
||||
// extract signature and content
|
||||
sig, content := signer.ExtractSignatureAndContent(bin)
|
||||
hash := sha256.Sum256(content)
|
||||
|
||||
gologger.Info().Msgf("Signature Details:")
|
||||
gologger.Info().Msgf("----------------")
|
||||
gologger.Info().Msgf("Signature: %s", sig)
|
||||
gologger.Info().Msgf("Content Hash (SHA256): %s\n", hex.EncodeToString(hash[:]))
|
||||
|
||||
execOpts := defaultExecutorOpts(template)
|
||||
|
||||
tmpl, err := templates.Parse(template, nil, execOpts)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to parse template: %s", err)
|
||||
}
|
||||
gologger.Info().Msgf("Template Verified: %v\n", tmpl.Verified)
|
||||
|
||||
if !tmpl.Verified {
|
||||
gologger.Info().Msgf("------------------------")
|
||||
gologger.Info().Msg("Template is not verified, signing template")
|
||||
if err := templates.SignTemplate(tmplSigner, template); err != nil {
|
||||
gologger.Fatal().Msgf("Failed to sign template: %s", err)
|
||||
}
|
||||
// verify again by reading file what the new signature and hash is
|
||||
bin2, err := os.ReadFile(template)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to read signed template file %s: %s", template, err)
|
||||
}
|
||||
sig2, content2 := signer.ExtractSignatureAndContent(bin2)
|
||||
hash2 := sha256.Sum256(content2)
|
||||
|
||||
gologger.Info().Msgf("Updated Signature Details:")
|
||||
gologger.Info().Msgf("------------------------")
|
||||
gologger.Info().Msgf("Signature: %s", sig2)
|
||||
gologger.Info().Msgf("Content Hash (SHA256): %s\n", hex.EncodeToString(hash2[:]))
|
||||
}
|
||||
gologger.Info().Msgf("✓ Template signed & verified successfully")
|
||||
}
|
||||
|
||||
func defaultExecutorOpts(templatePath string) protocols.ExecutorOptions {
|
||||
// use parsed options when initializing signer instead of default options
|
||||
options := types.DefaultOptions()
|
||||
templates.UseOptionsForSigner(options)
|
||||
catalog := disk.NewCatalog(filepath.Dir(templatePath))
|
||||
executerOpts := protocols.ExecutorOptions{
|
||||
Catalog: catalog,
|
||||
Options: options,
|
||||
TemplatePath: templatePath,
|
||||
Parser: templates.NewParser(),
|
||||
}
|
||||
return executerOpts
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/projectdiscovery/nuclei/v3
|
||||
|
||||
go 1.21
|
||||
go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
|
||||
@@ -20,12 +20,12 @@ require (
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/clistats v0.1.1
|
||||
github.com/projectdiscovery/fastdialer v0.2.9
|
||||
github.com/projectdiscovery/hmap v0.0.59
|
||||
github.com/projectdiscovery/fastdialer v0.2.10
|
||||
github.com/projectdiscovery/hmap v0.0.69
|
||||
github.com/projectdiscovery/interactsh v1.2.0
|
||||
github.com/projectdiscovery/rawhttp v0.1.67
|
||||
github.com/projectdiscovery/retryabledns v1.0.77
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.78
|
||||
github.com/projectdiscovery/rawhttp v0.1.76
|
||||
github.com/projectdiscovery/retryabledns v1.0.86
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.88
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.5.0
|
||||
@@ -38,9 +38,9 @@ require (
|
||||
github.com/weppos/publicsuffix-go v0.30.2
|
||||
github.com/xanzy/go-gitlab v0.107.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/net v0.31.0
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/text v0.18.0
|
||||
golang.org/x/text v0.20.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -81,32 +81,33 @@ require (
|
||||
github.com/microsoft/go-mssqldb v1.6.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/praetorian-inc/fingerprintx v1.1.9
|
||||
github.com/projectdiscovery/dsl v0.2.1
|
||||
github.com/projectdiscovery/dsl v0.3.3
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
|
||||
github.com/projectdiscovery/goflags v0.1.64
|
||||
github.com/projectdiscovery/gologger v1.1.24
|
||||
github.com/projectdiscovery/goflags v0.1.65
|
||||
github.com/projectdiscovery/gologger v1.1.33
|
||||
github.com/projectdiscovery/gostruct v0.0.2
|
||||
github.com/projectdiscovery/gozero v0.0.2
|
||||
github.com/projectdiscovery/httpx v1.6.8
|
||||
github.com/projectdiscovery/gozero v0.0.3
|
||||
github.com/projectdiscovery/httpx v1.6.9
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/ratelimit v0.0.56
|
||||
github.com/projectdiscovery/ratelimit v0.0.64
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.1.7
|
||||
github.com/projectdiscovery/tlsx v1.1.8
|
||||
github.com/projectdiscovery/uncover v1.0.9
|
||||
github.com/projectdiscovery/useragent v0.0.71
|
||||
github.com/projectdiscovery/utils v0.2.11
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.18
|
||||
github.com/projectdiscovery/useragent v0.0.78
|
||||
github.com/projectdiscovery/utils v0.3.0
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.5
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/seh-msft/burpxml v1.0.1
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
|
||||
github.com/yassinebenaid/godump v0.10.0
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706
|
||||
go.mongodb.org/mongo-driver v1.17.0
|
||||
golang.org/x/term v0.24.0
|
||||
golang.org/x/term v0.26.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
@@ -151,6 +152,7 @@ require (
|
||||
github.com/docker/docker v24.0.9+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/fgprof v0.9.5 // indirect
|
||||
github.com/free5gc/util v1.0.5-0.20230511064842-2e120956883b // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gaissmai/bart v0.9.5 // indirect
|
||||
@@ -209,7 +211,7 @@ require (
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.1 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.1.0 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.6 // 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/refraction-networking/utls v1.6.7 // indirect
|
||||
@@ -245,7 +247,7 @@ require (
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/goleak v1.3.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sync v0.9.0 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
@@ -271,7 +273,7 @@ require (
|
||||
github.com/goburrow/cache v0.1.4 // indirect
|
||||
github.com/gobwas/httphead v0.1.0 // indirect
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
@@ -312,10 +314,10 @@ require (
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
goftp.io/server/v2 v2.0.1 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/crypto v0.29.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/sys v0.27.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
|
||||
@@ -237,12 +237,18 @@ github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk=
|
||||
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
|
||||
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
|
||||
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/logex v1.2.0/go.mod h1:9+9sk7u7pGNWYMkh0hdiL++6OeibzJccyQU4p4MedaY=
|
||||
github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwysCBrQ=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/readline v1.5.0/go.mod h1:x22KAscuvRqlLoK9CsoYsmxoXZMMFVyOl86cAH8qUic=
|
||||
github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v0.0.0-20210722231415-061457976a23/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8=
|
||||
github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME=
|
||||
github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s=
|
||||
github.com/clbanning/x2j v0.0.0-20191024224557-825249438eec/go.mod h1:jMjuTZXRI4dUb/I5gc9Hdhagfvm9+RyrPryS/auMzxE=
|
||||
@@ -325,6 +331,8 @@ github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
|
||||
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/franela/goblin v0.0.0-20200105215937-c9ffbefa60db/go.mod h1:7dvUGVsVBjqR7JHJk0brhHOZYGmfBYOrK0ZhYMEtBr4=
|
||||
@@ -427,8 +435,8 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1 h1:JdqV9zKUdtaa9gdPlywC3aeoEsR681PlKC+4F5gQgeo=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.1/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 h1:au07oEsX2xN0ktxqI+Sida1w446QrXBRJ0nee3SNZlA=
|
||||
@@ -577,6 +585,7 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
|
||||
github.com/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20220319035150-800ac71e25c2/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
|
||||
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
|
||||
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
|
||||
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
|
||||
@@ -671,6 +680,7 @@ github.com/labstack/echo/v4 v4.10.2 h1:n1jAhnq/elIFTHr1EYpiYtyKgx4RW9ccVgkqByZaN
|
||||
github.com/labstack/echo/v4 v4.10.2/go.mod h1:OEyqf2//K1DFdE57vw2DRgWY0M7s65IVQO2FzvI4J5k=
|
||||
github.com/labstack/gommon v0.4.0 h1:y7cvthEAEbU0yHOf4axH8ZG2NH8knB9iNSoTO8dyIk8=
|
||||
github.com/labstack/gommon v0.4.0/go.mod h1:uW6kP17uPlLJsD3ijUYn3/M5bAxtlZhMI6m3MFxTMTM=
|
||||
github.com/ledongthuc/pdf v0.0.0-20220302134840-0c2507a12d80/go.mod h1:imJHygn/1yfhB7XSJJKlFZKl/J+dCPAknuiaGOshXAs=
|
||||
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa h1:KQKuQDgA3DZX6C396lt3WDYB9Um1gLITLbvficVbqXk=
|
||||
@@ -804,6 +814,7 @@ github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5/go.mod h1:/wsWhb9smxS
|
||||
github.com/openzipkin/zipkin-go v0.1.6/go.mod h1:QgAqvLzwWbR/WpD4A3cGpPtJrZXNIiJc5AZX7/PBEpw=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
|
||||
github.com/ory/dockertest/v3 v3.10.0 h1:4K3z2VMe8Woe++invjaTB7VRyQXQy5UY+loujO4aNE4=
|
||||
github.com/ory/dockertest/v3 v3.10.0/go.mod h1:nr57ZbRWMqfsdGdFNLHz5jjNdDb7VVFnzAeW1n5N1Lg=
|
||||
github.com/pact-foundation/pact-go v1.0.4/go.mod h1:uExwJY4kCzNPcHRj+hCR/HBbOOIwwtUjcrb0b5/5kLM=
|
||||
@@ -845,28 +856,28 @@ github.com/projectdiscovery/cdncheck v1.1.0 h1:qDITidmJsejzpk3rMkauCh6sjI2GH9hW/
|
||||
github.com/projectdiscovery/cdncheck v1.1.0/go.mod h1:sZ8U4MjHSsyaTVjBbYWHT1cwUVvUYwDX1W+WvWRicIc=
|
||||
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.2.1 h1:TK3KD4jsg4YbvY7WJqnz1QyH4AOvAwezeBFOX97Evgk=
|
||||
github.com/projectdiscovery/dsl v0.2.1/go.mod h1:IRQXsmi5/g1dDZ79//A9t2vrRtxm4frRSd5t8CZVSbI=
|
||||
github.com/projectdiscovery/fastdialer v0.2.9 h1:vDCqxVMCyUu3oVEizEK1K8K+CCcLkVDW3X2HfiWaVFA=
|
||||
github.com/projectdiscovery/fastdialer v0.2.9/go.mod h1:mYv5QaNBDDSHlZO9DI0niRMw+G5hUzwIhs8QixSElUI=
|
||||
github.com/projectdiscovery/dsl v0.3.3 h1:4Ij5S86cHlb6xFrS7+5zAiJPeBt5h970XBTHqeTkpyU=
|
||||
github.com/projectdiscovery/dsl v0.3.3/go.mod h1:DAjSeaogLM9f0Ves2zDc/vbJrfcv+kEmS51p0dLLaPI=
|
||||
github.com/projectdiscovery/fastdialer v0.2.10 h1:5iciZXMPdynbk/9iuqkJT1gqMXwzgEpFSWdoj/5CHCo=
|
||||
github.com/projectdiscovery/fastdialer v0.2.10/go.mod h1:21rwXMecVsPVdSvON8Up761/GgxC4OSc9Rvx5LNH5fY=
|
||||
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.6 h1:ROqzuXN8JPqkGdBueb3ah691nS2g2p7r3/3x2E33GbI=
|
||||
github.com/projectdiscovery/freeport v0.0.6/go.mod h1:T2kIy+WrbyxBIhI8V3Y9aeNGnuhnM8tEUSK/cm9GjAg=
|
||||
github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk=
|
||||
github.com/projectdiscovery/freeport v0.0.7/go.mod h1:cOhWKvNBe9xM6dFJ3RrrLvJ5vXx2NQ36SecuwjenV2k=
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb h1:rutG906Drtbpz4DwU5mhGIeOhRcktDH4cGQitGUMAsg=
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb/go.mod h1:FLjF1DmZ+POoGEiIQdWuYVwS++C/GwpX8YaCsTSm1RY=
|
||||
github.com/projectdiscovery/goflags v0.1.64 h1:FDfwdt9N97Hi8OuhbkDlKtVttpc/CRMIWQVa08VsHsI=
|
||||
github.com/projectdiscovery/goflags v0.1.64/go.mod h1:3FyHIVQtnycNOc1LE3O1jj/XR5XuMdF9QfHd0ujhnX4=
|
||||
github.com/projectdiscovery/gologger v1.1.24 h1:TmA4k9sA6ZvfyRnprZKQ0Uq34g//u5R9yTDPL9IzTOQ=
|
||||
github.com/projectdiscovery/gologger v1.1.24/go.mod h1:JA0JMJ+ply+J2wD062TN4h85thm6/28jAlrntwccKVU=
|
||||
github.com/projectdiscovery/goflags v0.1.65 h1:rjoj+5lP/FDzgeM0WILUTX9AOOnw0J0LXtl8P1SVeGE=
|
||||
github.com/projectdiscovery/goflags v0.1.65/go.mod h1:cg6+yrLlaekP1hnefBc/UXbH1YGWa0fuzEW9iS1aG4g=
|
||||
github.com/projectdiscovery/gologger v1.1.33 h1:wQxaQ8p/0Rx89lowBp0PnY2QSWiqf9QW1vGYAllsVJ4=
|
||||
github.com/projectdiscovery/gologger v1.1.33/go.mod h1:P/WwqKstshQATJxN39V0KJ9ZuiGLOizmSqHIYrrz1T4=
|
||||
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.0.2 h1:8fJeaCjxL9tpm33uG/RsCQs6HGM/NE6eA3cjkilRQ+E=
|
||||
github.com/projectdiscovery/gozero v0.0.2/go.mod h1:d8bZvDWW07LWNYWrwjZ4OO1I0cpkfqaysyDfSs9ibK8=
|
||||
github.com/projectdiscovery/hmap v0.0.59 h1:xWCr/GY2QJanFzwKydh/EkGdOKM4iAcN9hQvvCMgO6A=
|
||||
github.com/projectdiscovery/hmap v0.0.59/go.mod h1:uHhhnPmvq9qXvCjBSQXCBAlmA1r8JGufP775IkBSbgs=
|
||||
github.com/projectdiscovery/httpx v1.6.8 h1:k0Y5g3ue/7QbDP0+LykIxp/VhPDLfau3UEUyuxtP7qE=
|
||||
github.com/projectdiscovery/httpx v1.6.8/go.mod h1:7BIsDxyRwkBjthqFmEajXrA5f3yb4tlVfLmpNdf0ZXA=
|
||||
github.com/projectdiscovery/gozero v0.0.3 h1:tsYkrSvWw4WdIUJyisd4MB1vRiw1X57TuVVk3p8Z3G8=
|
||||
github.com/projectdiscovery/gozero v0.0.3/go.mod h1:MpJ37Dsh94gy2EKqaemdeh+CzduGVB2SDfhr6Upsjew=
|
||||
github.com/projectdiscovery/hmap v0.0.69 h1:e30pCr6JShf/UyJmKQpx++Yceiijw4GWj3lFHGZ1yko=
|
||||
github.com/projectdiscovery/hmap v0.0.69/go.mod h1:LgZHcgcxOvA3X8tuFtfu4dofJjAHAfpMno27Jx0J34w=
|
||||
github.com/projectdiscovery/httpx v1.6.9 h1:ihyFclesLjvQpiJpRIlAYeebapyIbOI/arDAvvy1ES8=
|
||||
github.com/projectdiscovery/httpx v1.6.9/go.mod h1:zQtX5CtcDYXzIRWne1ztCVtqG0sXCnx84tFwfMHoB8Q=
|
||||
github.com/projectdiscovery/interactsh v1.2.0 h1:Al6jHiR+Usl9egYJDLJaWNHOcH8Rugk8gWMasc8Cmw8=
|
||||
github.com/projectdiscovery/interactsh v1.2.0/go.mod h1:Wxt0fnzxsfrAZQQlpVrf3xMatP4OXZaZbjuDkIQKdYY=
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
|
||||
@@ -879,30 +890,30 @@ github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9 h1:IrlDoYZagNNO8y+7iZeHT8k5izE+nek7TdtvEBwCxqk=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9/go.mod h1:XFJ2Lnv8BE/ziQCFjBHMsH1w6VmkPiQtk+NlBpdMU7M=
|
||||
github.com/projectdiscovery/ratelimit v0.0.56 h1:WliU7NvfMb5hK/IJjOFlWIXU1G7+QRylMhSybaSCTI8=
|
||||
github.com/projectdiscovery/ratelimit v0.0.56/go.mod h1:GbnAo+MbB4R/4jiOI1mH4KAJfovmrPnq4NElcI99fvs=
|
||||
github.com/projectdiscovery/rawhttp v0.1.67 h1:HYzathMk3c8Y83hYjHM4GCBFbz/G+vABe0Lz6ajaowY=
|
||||
github.com/projectdiscovery/rawhttp v0.1.67/go.mod h1:5viJ6odzc9ZuEFppj/E7HdX+u99FoYlvXnhHyTNc7N0=
|
||||
github.com/projectdiscovery/ratelimit v0.0.64 h1:fDjCM+U9HqJU6+NBvJn0kY89PxD20iWvRFX1mLblRB8=
|
||||
github.com/projectdiscovery/ratelimit v0.0.64/go.mod h1:XVuisddIjhmdd9ukw+w90AkSJd3pEKBTG9fBvSjwVxs=
|
||||
github.com/projectdiscovery/rawhttp v0.1.76 h1:O2IoYSyG7unH5oW8r8j3539koCNkimyzcHFmCbx5BDU=
|
||||
github.com/projectdiscovery/rawhttp v0.1.76/go.mod h1:ZxvbdkRV2PBoCbJxHh9B0P0nC5gVG3p1Z5uiua3iC5I=
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gBVSorSzvmm0bFa7gDV4QNSOWPL/fgZ4kTXBxk=
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg=
|
||||
github.com/projectdiscovery/retryabledns v1.0.77 h1:rCFSiTPNI7h9PP1uUvrmcq/6XVZVdxpha2H1ioArKpk=
|
||||
github.com/projectdiscovery/retryabledns v1.0.77/go.mod h1:ce1JTjAOxLujqITtA5VLlbhLRVubx+GETYasivapai4=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.78 h1:If7/XjCWk893YrnTMaW69TNMsfE1Er3i1SWOkWbEk4E=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.78/go.mod h1:NqzTdnGihSRkF9c/aXo+3qTJXEeNwnOuza6GrlPo9qw=
|
||||
github.com/projectdiscovery/retryabledns v1.0.86 h1:8YMJGJ94lFBKKN3t7NOzJfbGsZoh9qNpi49xdfJcZVc=
|
||||
github.com/projectdiscovery/retryabledns v1.0.86/go.mod h1:5PhXvlLkEFmlYOt9i4wiKA1eONLrNiZ6DQE88Ph9rgU=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.88 h1:uR6T+i8Sy1isfG1KClhhsXnOqkOR6E8MAvuyOFq3T10=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.88/go.mod h1:ktjiIKyej+plUeK9vksqRf3wGicqY3E1rW84V/O7p0M=
|
||||
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=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
||||
github.com/projectdiscovery/tlsx v1.1.7 h1:eSsl/SmTDL/z2CMeSrbssk4f/9oOotMP1SgXl3yynSM=
|
||||
github.com/projectdiscovery/tlsx v1.1.7/go.mod h1:g66QQ4/y4tLVjoGbzWIv+Q6xwFzxfJbEDx86Y1dYHDM=
|
||||
github.com/projectdiscovery/tlsx v1.1.8 h1:Y+VkOp6JmUBb4tci1Fbz9U7ELEQ2irFhm+HS58tHruM=
|
||||
github.com/projectdiscovery/tlsx v1.1.8/go.mod h1:6u/dbLuMsLzmux58AWnAB24qh2+Trk0auCK2I9B17Vo=
|
||||
github.com/projectdiscovery/uncover v1.0.9 h1:s5RbkD/V4r8QcPkys4gTTqMuRSgXq0JprejqLSopN9Y=
|
||||
github.com/projectdiscovery/uncover v1.0.9/go.mod h1:2PUF3SpB5QNIJ8epaB2xbRzkPaxEAWRDm3Ir2ijt81U=
|
||||
github.com/projectdiscovery/useragent v0.0.71 h1:Q02L3LV15ztOQ6FfmVSqVmOd5QhvzI+yAgYOc/32Nvg=
|
||||
github.com/projectdiscovery/useragent v0.0.71/go.mod h1:DHPruFLCvCvkd2qqPwwQZrP9sziv0lxQJ0R1rE1fa8E=
|
||||
github.com/projectdiscovery/utils v0.2.11 h1:TO7fBG5QI256sn1YuTD87yn4+4OjGJ2wT1772uEnp4Q=
|
||||
github.com/projectdiscovery/utils v0.2.11/go.mod h1:W0E74DWkKxlcyKS5XwcAwiob7+smoszPPi1NgX3vZyk=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.18 h1:fFgETis0HcsNE7wREaUPYP45JqIyHgGorJaVp1RH7g4=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.18/go.mod h1:/hzgxkBFTMe2wDbA93nFfoMjULw7/vIZ9QPSAnCgUa8=
|
||||
github.com/projectdiscovery/useragent v0.0.78 h1:YpgiY3qXpzygFA88SWVseAyWeV9ZKrIpDkfOY+mQ/UY=
|
||||
github.com/projectdiscovery/useragent v0.0.78/go.mod h1:SQgk2DZu1qCvYqBRYWs2sjenXqLEDnRw65wJJoolwZ4=
|
||||
github.com/projectdiscovery/utils v0.3.0 h1:JNRMUPMx2gSzdk3bOQPtXSucoz+qC1JVkibIn7+kPTk=
|
||||
github.com/projectdiscovery/utils v0.3.0/go.mod h1:k2XlmfaYO4k6T4vAyUa3Kn/0BxPTIlNiBFpM6nVCbz0=
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.5 h1:DhPEgeD+9i6yg+aGlbkT4iOAspQfB5ZzrmpEhwoiMlA=
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.5/go.mod h1:fXiqsyLHaX/ovBNUe/nX0318bWON6SHftLvgq1xaOq0=
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4/go.mod h1:8PIPRcUD55UbtQdcfFR1hpIGRWG0P7alClXNGt1TBik=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@@ -1021,8 +1032,8 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
|
||||
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
|
||||
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9 h1:GXIyLuIJ5Qk46lI8WJ83qHBZKUI3zhmMmuoY9HICUIQ=
|
||||
@@ -1104,6 +1115,8 @@ github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yassinebenaid/godump v0.10.0 h1:FolBA+Ix5uwUiXYBBYOsf1VkT5+0f4gtFNTkYTiIR08=
|
||||
github.com/yassinebenaid/godump v0.10.0/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
@@ -1210,8 +1223,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
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.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/crypto v0.29.0 h1:L5SG1JTTXupVV3n6sUqMTeWbjAyfPwoda2DLX8J8FrQ=
|
||||
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
|
||||
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=
|
||||
@@ -1304,8 +1317,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/net v0.31.0 h1:68CPQngjLL0r2AlUKiSxtQFKvzRVbnzLwMUn5SzcLHo=
|
||||
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
|
||||
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=
|
||||
@@ -1330,8 +1343,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.9.0 h1:fEo0HyrW1GIgZdpbhCRO0PkJajUS5H9IFUztCgEo2jQ=
|
||||
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
@@ -1406,8 +1419,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
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.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.27.0 h1:wBqf8DvsY9Y/2P8gAfPDEYNuS30J4lPHJxXSb/nJZ+s=
|
||||
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -1420,8 +1433,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
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.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/term v0.26.0 h1:WEQa6V3Gja/BhNxg540hBip/kkaYtRg3cxg4oXSw4AU=
|
||||
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
|
||||
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=
|
||||
@@ -1438,8 +1451,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
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.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug=
|
||||
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
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=
|
||||
|
||||
@@ -14,7 +14,7 @@ dns:
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "ghost.io"
|
||||
- ".vercel-dns.com"
|
||||
internal: true
|
||||
|
||||
http:
|
||||
@@ -25,4 +25,4 @@ http:
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "ghost.io"
|
||||
- "html>"
|
||||
@@ -23,4 +23,4 @@ code:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- true
|
||||
# digest: 4a0a0047304502200307590191cb7c766b6c21e5777d345bdddf7adf9d6da8f7d336d585d9ac4a8b022100fd30fb0c7722778eb3d861d60e721d805925b8d8df2b979ef2104c35ec57d5cb:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 490a00463044022048c083c338c0195f5012122d40c1009d2e2030c583e56558e0d6249a41e6f3f4022070656adf748f4874018d7a01fce116db10a3acd1f9b03e12a83906fb625b5c50:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -20,4 +20,4 @@ code:
|
||||
- type: word
|
||||
words:
|
||||
- "hello from input baz"
|
||||
# digest: 4a0a0047304502203fe1d7d52bc2a41886d576a90c82c3be42078baaa4b46e1f3d8519665d6f88b202210081feb82c41150c5b218e226fc4f299ded19f42ba01ef34ba60b0634b4ea6ee12:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 4b0a00483046022100cbbdb7214f669d111b671d271110872dc8af2ab41cf5c312b6e4f64126f55337022100a60547952a0c2bea58388f2d2effe8ad73cd6b6fc92e73eb3c8f88beab6105ec:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -18,4 +18,4 @@ code:
|
||||
- type: word
|
||||
words:
|
||||
- "hello from input"
|
||||
# digest: 4b0a00483046022100afb5ebff14a40e7f9b679ffc4d93ce7849e33eb398ebb47f2e757cd24831f9dd02210089ffa21b2763e99ebce95dfc5b91e1e62da4ccdc9d2ad5c48584fa350ba335af:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 4a0a00473045022032b81e8bb7475abf27639b0ced71355497166d664698021f26498e7031d62a23022100e99ccde578bfc0b658f16427ae9a3d18922849d3ba3e022032ea0d2a8e77fadb:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -26,4 +26,4 @@ code:
|
||||
part: interactsh_protocol
|
||||
words:
|
||||
- "http"
|
||||
# digest: 4b0a00483046022100939f83e74d43932a5bd792b1fd2c100eec2df60f2b2a8dd56b5c8ef5faa92b17022100f93031b0de373af7d78e623968ea5a2d67c4561ef70e3e6da15aef7e5c853115:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 4a0a0047304502201a5dd0eddfab4f02588a5a8ac1947a5fa41fed80b59d698ad5cc00456296efb6022100fe6e608e38c060964800f5f863a7cdc93f686f2d0f4b52854f73948b808b4511:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -21,4 +21,4 @@ code:
|
||||
- type: word
|
||||
words:
|
||||
- "hello from input"
|
||||
# digest: 4a0a00473045022100b8e676ce0c57b60c233a0203539dec20457bbb5f1790d351a5d45405b6668b2602204b1f2fa18e7db099f05329009597ceb2d9b7337562c1a676e8d50ea2f1c6fcbe:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 4b0a00483046022100ced1702728cc68f906c4c7d2c4d05ed071bfabee1e36eec7ebecbeca795a170c022100d20fd41796f130a8f9c4972fee85386d67d61eb5fc1119b1afe2a851eb2f3e65:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -13,7 +13,7 @@ dns:
|
||||
- type: dsl
|
||||
name: blogid
|
||||
dsl:
|
||||
- trim_suffix(cname,'.ghost.io')
|
||||
- trim_suffix(cname,'.vercel-dns.com')
|
||||
internal: true
|
||||
|
||||
http:
|
||||
@@ -24,6 +24,6 @@ http:
|
||||
matchers:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- contains(body,'ProjectDiscovery.io') # check for http string
|
||||
- blogid == 'projectdiscovery' # check for cname (extracted information from dns response)
|
||||
- contains(body,'introduction') # check for http string
|
||||
- blogid == 'cname' # check for cname (extracted information from dns response)
|
||||
condition: and
|
||||
@@ -7,7 +7,7 @@ info:
|
||||
|
||||
|
||||
variables:
|
||||
cname_filtered: '{{trim_suffix(dns_cname,".ghost.io")}}'
|
||||
cname_filtered: '{{trim_suffix(dns_cname,".vercel-dns.com")}}'
|
||||
|
||||
dns:
|
||||
- name: "{{FQDN}}" # DNS Request
|
||||
@@ -24,7 +24,7 @@ http:
|
||||
matchers:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- contains(http_body,'ProjectDiscovery.io') # check for http string
|
||||
- cname_filtered == 'projectdiscovery' # check for cname (extracted information from dns response)
|
||||
- ssl_subject_cn == 'blog.projectdiscovery.io'
|
||||
- contains(http_body,'introduction') # check for http string
|
||||
- cname_filtered == 'cname' # check for cname (extracted information from dns response)
|
||||
- ssl_subject_cn == 'docs.projectdiscovery.io'
|
||||
condition: and
|
||||
@@ -20,7 +20,7 @@ http:
|
||||
matchers:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- contains(http_body,'ProjectDiscovery.io') # check for http string
|
||||
- trim_suffix(dns_cname,'.ghost.io') == 'projectdiscovery' # check for cname (extracted information from dns response)
|
||||
- ssl_subject_cn == 'blog.projectdiscovery.io'
|
||||
- contains(http_body,'introduction') # check for http string
|
||||
- trim_suffix(dns_cname,'.vercel-dns.com') == 'cname' # check for cname (extracted information from dns response)
|
||||
- ssl_subject_cn == 'docs.projectdiscovery.io'
|
||||
condition: and
|
||||
@@ -19,4 +19,4 @@ code:
|
||||
regex:
|
||||
- 'hello from (.*)'
|
||||
group: 1
|
||||
# digest: 490a00463044022050da011362cf08c2cb81e812c7f86d7282afe0562d4bf00d390f1300d19bc910022029e9d305da69e941ac18797645aecb217abde6557f891e141301b48e89a3c0cd:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 490a0046304402206b3648e8d393ac4df82c7d59b1a6ee3731c66c249dbd4d9bf31f0b7f176b37ec02203184d36373e516757c7d708b5799bc16edb1cebc0a64f3442d13ded4b33c42fb:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -18,4 +18,4 @@ code:
|
||||
- type: word
|
||||
words:
|
||||
- "hello from first"
|
||||
# digest: 4b0a00483046022100b3b8759c0df028455eb59b1433ac240e5d4604b011bb0c63680bd3cc159ac6f0022100f44aa11b640d11ad0e2902897f4eb51666ab3cd83c31dfd2590f6e43391e39b0:4a3eb6b4988d95847d4203be25ed1d46
|
||||
# digest: 490a0046304402204cbb1bdf8370e49bb930b17460fb35e15f285a3b48b165736ac0e7ba2f9bc0fb022067c134790c4a2cf646b195aa4488e2c222266436e6bda47931908a28807bdb81:4a3eb6b4988d95847d4203be25ed1d46
|
||||
@@ -3,6 +3,7 @@ package runner
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
@@ -10,9 +11,12 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/writer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
)
|
||||
|
||||
@@ -75,7 +79,25 @@ func GetLazyAuthFetchCallback(opts *AuthLazyFetchOptions) authx.LazyFetchSecret
|
||||
vars := map[string]interface{}{}
|
||||
mainCtx := context.Background()
|
||||
ctx := scan.NewScanContext(mainCtx, contextargs.NewWithInput(mainCtx, d.Input))
|
||||
|
||||
cliVars := map[string]interface{}{}
|
||||
if opts.ExecOpts.Options != nil {
|
||||
// gets variables passed from cli -v and -env-vars
|
||||
cliVars = generators.BuildPayloadFromOptions(opts.ExecOpts.Options)
|
||||
}
|
||||
|
||||
for _, v := range d.Variables {
|
||||
// Check if the template has any env variables and expand them
|
||||
if strings.HasPrefix(v.Value, "$") {
|
||||
env.ExpandWithEnv(&v.Value)
|
||||
}
|
||||
if strings.Contains(v.Value, "{{") {
|
||||
// if variables had value like {{username}}, then replace it with the value from cliVars
|
||||
// variables:
|
||||
// - key: username
|
||||
// value: {{username}}
|
||||
v.Value = replacer.Replace(v.Value, cliVars)
|
||||
}
|
||||
vars[v.Key] = v.Value
|
||||
ctx.Input.Add(v.Key, v.Value)
|
||||
}
|
||||
|
||||
@@ -68,6 +68,7 @@ func ParseOptions(options *types.Options) {
|
||||
|
||||
if options.ShowVarDump {
|
||||
vardump.EnableVarDump = true
|
||||
vardump.Limit = options.VarDumpLimit
|
||||
}
|
||||
if options.ShowActions {
|
||||
gologger.Info().Msgf("Showing available headless actions: ")
|
||||
@@ -303,10 +304,17 @@ func createReportingOptions(options *types.Options) (*reporting.Options, error)
|
||||
OmitRaw: options.OmitRawRequests,
|
||||
}
|
||||
}
|
||||
// Combine options.
|
||||
if options.JSONLExport != "" {
|
||||
reportingOptions.JSONLExporter = &jsonl.Options{
|
||||
File: options.JSONLExport,
|
||||
OmitRaw: options.OmitRawRequests,
|
||||
// Combine the CLI options with the config file options with the CLI options taking precedence
|
||||
if reportingOptions.JSONLExporter != nil {
|
||||
reportingOptions.JSONLExporter.File = options.JSONLExport
|
||||
reportingOptions.JSONLExporter.OmitRaw = options.OmitRawRequests
|
||||
} else {
|
||||
reportingOptions.JSONLExporter = &jsonl.Options{
|
||||
File: options.JSONLExport,
|
||||
OmitRaw: options.OmitRawRequests,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/automaticscan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"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"
|
||||
@@ -391,6 +392,9 @@ func (r *Runner) Close() {
|
||||
if r.tmpDir != "" {
|
||||
_ = os.RemoveAll(r.tmpDir)
|
||||
}
|
||||
|
||||
//this is no-op unless nuclei is built with stats build tag
|
||||
events.Close()
|
||||
}
|
||||
|
||||
// setupPDCPUpload sets up the PDCP upload writer
|
||||
@@ -475,6 +479,7 @@ func (r *Runner) RunEnumeration() error {
|
||||
TemporaryDirectory: r.tmpDir,
|
||||
Parser: r.parser,
|
||||
FuzzParamsFrequency: fuzzFreqCache,
|
||||
GlobalMatchers: globalmatchers.New(),
|
||||
}
|
||||
|
||||
if config.DefaultConfig.IsDebugArgEnabled(config.DebugExportURLPattern) {
|
||||
@@ -725,6 +730,8 @@ func (r *Runner) displayExecutionInfo(store *loader.Store) {
|
||||
stats.ForceDisplayWarning(templates.ExcludedCodeTmplStats)
|
||||
stats.ForceDisplayWarning(templates.ExludedDastTmplStats)
|
||||
stats.ForceDisplayWarning(templates.TemplatesExcludedStats)
|
||||
stats.ForceDisplayWarning(templates.ExcludedFileStats)
|
||||
stats.ForceDisplayWarning(templates.ExcludedSelfContainedStats)
|
||||
}
|
||||
|
||||
if tmplCount == 0 && workflowCount == 0 {
|
||||
|
||||
@@ -380,6 +380,31 @@ func WithSandboxOptions(allowLocalFileAccess bool, restrictLocalNetworkAccess bo
|
||||
func EnableCodeTemplates() NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.EnableCodeTemplates = true
|
||||
e.opts.EnableSelfContainedTemplates = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableSelfContainedTemplates allows loading/executing self-contained templates
|
||||
func EnableSelfContainedTemplates() NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.EnableSelfContainedTemplates = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableGlobalMatchersTemplates allows loading/executing global-matchers templates
|
||||
func EnableGlobalMatchersTemplates() NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.EnableGlobalMatchersTemplates = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnableFileTemplates allows loading/executing file protocol templates
|
||||
func EnableFileTemplates() NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.EnableFileTemplates = true
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -392,6 +417,25 @@ func WithHeaders(headers []string) NucleiSDKOptions {
|
||||
}
|
||||
}
|
||||
|
||||
// WithVars allows setting custom variables to use in templates/workflows context
|
||||
func WithVars(vars []string) NucleiSDKOptions {
|
||||
// Create a goflags.RuntimeMap
|
||||
runtimeVars := goflags.RuntimeMap{}
|
||||
for _, v := range vars {
|
||||
err := runtimeVars.Set(v)
|
||||
if err != nil {
|
||||
return func(e *NucleiEngine) error {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.Vars = runtimeVars
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnablePassiveMode allows enabling passive HTTP response processing mode
|
||||
func EnablePassiveMode() NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
|
||||
+3
-1
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
nuclei "github.com/projectdiscovery/nuclei/v3/lib"
|
||||
"github.com/remeh/sizedwaitgroup"
|
||||
)
|
||||
@@ -78,9 +79,10 @@ func ExampleThreadSafeNucleiEngine() {
|
||||
func TestMain(m *testing.M) {
|
||||
// this file only contains testtables examples https://go.dev/blog/examples
|
||||
// and actual functionality test are in sdk_test.go
|
||||
if os.Getenv("GH_ACTION") != "" || os.Getenv("CI") != "" {
|
||||
if ci.IsCI() {
|
||||
// no need to run this test on github actions
|
||||
return
|
||||
}
|
||||
|
||||
os.Exit(m.Run())
|
||||
}
|
||||
+14
@@ -100,6 +100,7 @@ func (e *NucleiEngine) LoadAllTemplates() error {
|
||||
return errorutil.New("Could not create loader client: %s\n", err)
|
||||
}
|
||||
e.store.Load()
|
||||
e.templatesLoaded = true
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -111,6 +112,14 @@ func (e *NucleiEngine) GetTemplates() []*templates.Template {
|
||||
return e.store.Templates()
|
||||
}
|
||||
|
||||
// GetWorkflows returns all nuclei workflows that are loaded
|
||||
func (e *NucleiEngine) GetWorkflows() []*templates.Template {
|
||||
if !e.templatesLoaded {
|
||||
_ = e.LoadAllTemplates()
|
||||
}
|
||||
return e.store.Workflows()
|
||||
}
|
||||
|
||||
// LoadTargets(urls/domains/ips only) adds targets to the nuclei engine
|
||||
func (e *NucleiEngine) LoadTargets(targets []string, probeNonHttp bool) {
|
||||
for _, target := range targets {
|
||||
@@ -270,6 +279,11 @@ func (e *NucleiEngine) Engine() *core.Engine {
|
||||
return e.engine
|
||||
}
|
||||
|
||||
// Store returns store of nuclei
|
||||
func (e *NucleiEngine) Store() *loader.Store {
|
||||
return e.store
|
||||
}
|
||||
|
||||
// NewNucleiEngineCtx creates a new nuclei engine instance with given context
|
||||
func NewNucleiEngineCtx(ctx context.Context, options ...NucleiSDKOptions) (*NucleiEngine, error) {
|
||||
// default options
|
||||
|
||||
@@ -7,6 +7,8 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -171,6 +173,7 @@ func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
Browser: e.browserInstance,
|
||||
Parser: e.parser,
|
||||
InputHelper: input.NewHelper(),
|
||||
}
|
||||
if len(e.opts.SecretsFile) > 0 {
|
||||
authTmplStore, err := runner.GetAuthTmplStore(*e.opts, e.catalog, e.executerOpts)
|
||||
|
||||
@@ -133,3 +133,37 @@ func TestThreadSafeNuclei(t *testing.T) {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
|
||||
func TestWithVarsNuclei(t *testing.T) {
|
||||
fn := func() {
|
||||
defer func() {
|
||||
// resources like leveldb have a delay to commit in-memory resources
|
||||
// to disk, typically 1-2 seconds, so we wait for 2 seconds
|
||||
time.Sleep(2 * time.Second)
|
||||
goleak.VerifyNone(t, knownLeaks...)
|
||||
}()
|
||||
ne, err := nuclei.NewNucleiEngineCtx(
|
||||
context.TODO(),
|
||||
nuclei.EnableSelfContainedTemplates(),
|
||||
nuclei.WithTemplatesOrWorkflows(nuclei.TemplateSources{Templates: []string{"http/token-spray/api-1forge.yaml"}}),
|
||||
nuclei.WithVars([]string{"token=foobar"}),
|
||||
nuclei.WithVerbosity(nuclei.VerbosityOptions{Debug: true}),
|
||||
)
|
||||
require.Nil(t, err)
|
||||
ne.LoadTargets([]string{"scanme.sh"}, true) // probe http/https target is set to true here
|
||||
err = ne.ExecuteWithCallback(nil)
|
||||
require.Nil(t, err)
|
||||
defer ne.Close()
|
||||
}
|
||||
// this is shared test so needs to be run as seperate process
|
||||
if env.GetEnvOrDefault("TestWithVarsNuclei", false) {
|
||||
cmd := exec.Command(os.Args[0], "-test.run=TestWithVarsNuclei")
|
||||
cmd.Env = append(os.Environ(), "TestWithVarsNuclei=true")
|
||||
out, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
t.Fatalf("process ran with error %s, output: %s", err, out)
|
||||
}
|
||||
} else {
|
||||
fn()
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,22 @@
|
||||
"$id": "https://templates.-template",
|
||||
"$ref": "#/$defs/templates.Template",
|
||||
"$defs": {
|
||||
"analyzers.AnalyzerTemplate": {
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"parameters": {
|
||||
"$ref": "#/$defs/map[string]interface {}"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"type": "object",
|
||||
"required": [
|
||||
"name",
|
||||
"parameters"
|
||||
]
|
||||
},
|
||||
"code.Request": {
|
||||
"properties": {
|
||||
"matchers": {
|
||||
@@ -785,6 +801,11 @@
|
||||
"title": "fuzzin rules for http fuzzing",
|
||||
"description": "Fuzzing describes rule schema to fuzz http requests"
|
||||
},
|
||||
"analyzer": {
|
||||
"$ref": "#/$defs/analyzers.AnalyzerTemplate",
|
||||
"title": "analyzer for http request",
|
||||
"description": "Analyzer for HTTP Request"
|
||||
},
|
||||
"self-contained": {
|
||||
"type": "boolean"
|
||||
},
|
||||
@@ -889,6 +910,11 @@
|
||||
],
|
||||
"title": "condition between the filters",
|
||||
"description": "Operator to use between multiple per-conditions"
|
||||
},
|
||||
"global-matchers": {
|
||||
"type": "boolean",
|
||||
"title": "global matchers",
|
||||
"description": "marks matchers as static and applies globally to all result events from other templates"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -1365,6 +1391,11 @@
|
||||
"title": "read all response stream",
|
||||
"description": "Read all response stream till the server stops sending"
|
||||
},
|
||||
"stop-at-first-match": {
|
||||
"type": "boolean",
|
||||
"title": "stop at first match",
|
||||
"description": "Stop the execution after a match is found"
|
||||
},
|
||||
"matchers": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/matchers.Matcher"
|
||||
|
||||
@@ -31,7 +31,7 @@ const (
|
||||
CLIConfigFileName = "config.yaml"
|
||||
ReportingConfigFilename = "reporting-config.yaml"
|
||||
// Version is the current version of nuclei
|
||||
Version = `v3.3.4`
|
||||
Version = `v3.3.7`
|
||||
// Directory Names of custom templates
|
||||
CustomS3TemplatesDirName = "s3"
|
||||
CustomGitHubTemplatesDirName = "github"
|
||||
|
||||
@@ -298,6 +298,27 @@ func (store *Store) LoadTemplatesOnlyMetadata() error {
|
||||
|
||||
for templatePath := range validPaths {
|
||||
template, _, _ := templatesCache.Has(templatePath)
|
||||
|
||||
if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(template.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
continue
|
||||
}
|
||||
|
||||
if template.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||
continue
|
||||
}
|
||||
|
||||
if template.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
|
||||
continue
|
||||
}
|
||||
|
||||
if template.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
|
||||
continue
|
||||
}
|
||||
|
||||
if template != nil {
|
||||
template.Path = templatePath
|
||||
store.templates = append(store.templates, template)
|
||||
@@ -352,7 +373,22 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
|
||||
if err != nil {
|
||||
if isParsingError("Error occurred parsing template %s: %s\n", templatePath, err) {
|
||||
areTemplatesValid = false
|
||||
continue
|
||||
}
|
||||
} else if template == nil {
|
||||
// NOTE(dwisiswant0): possibly global matchers template.
|
||||
// This could definitely be handled better, for example by returning an
|
||||
// `ErrGlobalMatchersTemplate` during `templates.Parse` and checking it
|
||||
// with `errors.Is`.
|
||||
//
|
||||
// However, I’m not sure if every reference to it should be handled
|
||||
// that way. Returning a `templates.Template` pointer would mean it’s
|
||||
// an active template (sending requests), and adding a specific field
|
||||
// like `isGlobalMatchers` in `templates.Template` (then checking it
|
||||
// with a `*templates.Template.IsGlobalMatchersEnabled` method) would
|
||||
// just introduce more unknown issues - like during template
|
||||
// clustering, AFAIK.
|
||||
continue
|
||||
} else {
|
||||
if existingTemplatePath, found := templateIDPathMap[template.ID]; !found {
|
||||
templateIDPathMap[template.ID] = templatePath
|
||||
@@ -474,6 +510,17 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
|
||||
stats.Increment(templates.ExcludedSelfContainedStats)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
|
||||
stats.Increment(templates.ExcludedFileStats)
|
||||
return
|
||||
}
|
||||
|
||||
// if template has request signature like aws then only signed and verified templates are allowed
|
||||
if parsed.UsesRequestSignature() && !parsed.Verified {
|
||||
stats.Increment(templates.SkippedRequestSignatureStats)
|
||||
|
||||
+31
-5
@@ -137,33 +137,59 @@ getRepo:
|
||||
|
||||
// download the git repo to a given path
|
||||
func (ctr *customTemplateGitHubRepo) cloneRepo(clonePath, githubToken string) error {
|
||||
r, err := git.PlainClone(clonePath, false, &git.CloneOptions{
|
||||
URL: ctr.gitCloneURL,
|
||||
Auth: getAuth(ctr.owner, githubToken),
|
||||
})
|
||||
cloneOpts := &git.CloneOptions{
|
||||
URL: ctr.gitCloneURL,
|
||||
Auth: getAuth(ctr.owner, githubToken),
|
||||
SingleBranch: true,
|
||||
Depth: 1,
|
||||
}
|
||||
|
||||
err := cloneOpts.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := git.PlainClone(clonePath, false, cloneOpts)
|
||||
if err != nil {
|
||||
return errors.Errorf("%s/%s: %s", ctr.owner, ctr.reponame, err.Error())
|
||||
}
|
||||
|
||||
// Add the user as well in the config. By default, user is not set
|
||||
config, _ := r.Storer.Config()
|
||||
config.User.Name = ctr.owner
|
||||
|
||||
return r.SetConfig(config)
|
||||
}
|
||||
|
||||
// performs the git pull on given repo
|
||||
func (ctr *customTemplateGitHubRepo) pullChanges(repoPath, githubToken string) error {
|
||||
pullOpts := &git.PullOptions{
|
||||
RemoteName: "origin",
|
||||
Auth: getAuth(ctr.owner, githubToken),
|
||||
SingleBranch: true,
|
||||
Depth: 1,
|
||||
}
|
||||
|
||||
err := pullOpts.Validate()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r, err := git.PlainOpen(repoPath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
w, err := r.Worktree()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = w.Pull(&git.PullOptions{RemoteName: "origin", Auth: getAuth(ctr.owner, githubToken)})
|
||||
|
||||
err = w.Pull(pullOpts)
|
||||
if err != nil {
|
||||
return errors.Errorf("%s/%s: %s", ctr.owner, ctr.reponame, err.Error())
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
+2
-4
@@ -22,14 +22,12 @@ func TestDownloadCustomTemplatesFromGitHub(t *testing.T) {
|
||||
config.DefaultConfig.SetTemplatesDir(templatesDirectory)
|
||||
|
||||
options := testutils.DefaultOptions
|
||||
options.GitHubTemplateRepo = []string{"projectdiscovery/nuclei-templates", "ehsandeep/nuclei-templates"}
|
||||
options.GitHubToken = os.Getenv("GITHUB_TOKEN")
|
||||
options.GitHubTemplateRepo = []string{"projectdiscovery/nuclei-templates-test"}
|
||||
|
||||
ctm, err := NewCustomTemplatesManager(options)
|
||||
require.Nil(t, err, "could not create custom templates manager")
|
||||
|
||||
ctm.Download(context.Background())
|
||||
|
||||
require.DirExists(t, filepath.Join(templatesDirectory, "github", "projectdiscovery", "nuclei-templates"), "cloned directory does not exists")
|
||||
require.DirExists(t, filepath.Join(templatesDirectory, "github", "ehsandeep", "nuclei-templates"), "cloned directory does not exists")
|
||||
require.DirExists(t, filepath.Join(templatesDirectory, "github", "projectdiscovery", "nuclei-templates-test"), "cloned directory does not exists")
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
package analyzers
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
)
|
||||
|
||||
// Analyzer is an interface for all the analyzers
|
||||
// that can be used for the fuzzer
|
||||
type Analyzer interface {
|
||||
// Name returns the name of the analyzer
|
||||
Name() string
|
||||
// ApplyTransformation applies the transformation to the initial payload.
|
||||
ApplyInitialTransformation(data string, params map[string]interface{}) string
|
||||
// Analyze is the main function for the analyzer
|
||||
Analyze(options *Options) (bool, string, error)
|
||||
}
|
||||
|
||||
// AnalyzerTemplate is the template for the analyzer
|
||||
type AnalyzerTemplate struct {
|
||||
// description: |
|
||||
// Name is the name of the analyzer to use
|
||||
// values:
|
||||
// - time_delay
|
||||
Name string `json:"name" yaml:"name"`
|
||||
// description: |
|
||||
// Parameters is the parameters for the analyzer
|
||||
//
|
||||
// Parameters are different for each analyzer. For example, you can customize
|
||||
// time_delay analyzer with sleep_duration, time_slope_error_range, etc. Refer
|
||||
// to the docs for each analyzer to get an idea about parameters.
|
||||
Parameters map[string]interface{} `json:"parameters" yaml:"parameters"`
|
||||
}
|
||||
|
||||
var (
|
||||
analyzers map[string]Analyzer
|
||||
)
|
||||
|
||||
// RegisterAnalyzer registers a new analyzer
|
||||
func RegisterAnalyzer(name string, analyzer Analyzer) {
|
||||
analyzers[name] = analyzer
|
||||
}
|
||||
|
||||
// GetAnalyzer returns the analyzer for a given name
|
||||
func GetAnalyzer(name string) Analyzer {
|
||||
return analyzers[name]
|
||||
}
|
||||
|
||||
func init() {
|
||||
analyzers = make(map[string]Analyzer)
|
||||
}
|
||||
|
||||
// Options contains the options for the analyzer
|
||||
type Options struct {
|
||||
FuzzGenerated fuzz.GeneratedRequest
|
||||
HttpClient *retryablehttp.Client
|
||||
ResponseTimeDelay time.Duration
|
||||
AnalyzerParameters map[string]interface{}
|
||||
}
|
||||
|
||||
var (
|
||||
random = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
)
|
||||
|
||||
// ApplyPayloadTransformations applies the payload transformations to the payload
|
||||
// It supports the below payloads -
|
||||
// - [RANDNUM] => random number between 1000 and 9999
|
||||
// - [RANDSTR] => random string of 4 characters
|
||||
func ApplyPayloadTransformations(value string) string {
|
||||
randomInt := GetRandomInteger()
|
||||
randomStr := randStringBytesMask(4)
|
||||
|
||||
value = strings.ReplaceAll(value, "[RANDNUM]", strconv.Itoa(randomInt))
|
||||
value = strings.ReplaceAll(value, "[RANDSTR]", randomStr)
|
||||
return value
|
||||
}
|
||||
|
||||
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||
const (
|
||||
letterIdxBits = 6 // 6 bits to represent a letter index
|
||||
letterIdxMask = 1<<letterIdxBits - 1 // All 1-bits, as many as letterIdxBits
|
||||
)
|
||||
|
||||
func randStringBytesMask(n int) string {
|
||||
b := make([]byte, n)
|
||||
for i := 0; i < n; {
|
||||
if idx := int(random.Int63() & letterIdxMask); idx < len(letterBytes) {
|
||||
b[i] = letterBytes[idx]
|
||||
i++
|
||||
}
|
||||
}
|
||||
return string(b)
|
||||
}
|
||||
|
||||
// GetRandomInteger returns a random integer between 1000 and 9999
|
||||
func GetRandomInteger() int {
|
||||
return random.Intn(9000) + 1000
|
||||
}
|
||||
@@ -0,0 +1,168 @@
|
||||
package time
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http/httptrace"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
)
|
||||
|
||||
// Analyzer is a time delay analyzer for the fuzzer
|
||||
type Analyzer struct{}
|
||||
|
||||
const (
|
||||
DefaultSleepDuration = int(5)
|
||||
DefaultRequestsLimit = int(4)
|
||||
DefaultTimeCorrelationErrorRange = float64(0.15)
|
||||
DefaultTimeSlopeErrorRange = float64(0.30)
|
||||
|
||||
defaultSleepTimeDuration = 5 * time.Second
|
||||
)
|
||||
|
||||
var _ analyzers.Analyzer = &Analyzer{}
|
||||
|
||||
func init() {
|
||||
analyzers.RegisterAnalyzer("time_delay", &Analyzer{})
|
||||
}
|
||||
|
||||
// Name is the name of the analyzer
|
||||
func (a *Analyzer) Name() string {
|
||||
return "time_delay"
|
||||
}
|
||||
|
||||
// ApplyInitialTransformation applies the transformation to the initial payload.
|
||||
//
|
||||
// It supports the below payloads -
|
||||
// - [SLEEPTIME] => sleep_duration
|
||||
// - [INFERENCE] => Inference payload for time delay analyzer
|
||||
//
|
||||
// It also applies the payload transformations to the payload
|
||||
// which includes [RANDNUM] and [RANDSTR]
|
||||
func (a *Analyzer) ApplyInitialTransformation(data string, params map[string]interface{}) string {
|
||||
duration := DefaultSleepDuration
|
||||
if len(params) > 0 {
|
||||
if v, ok := params["sleep_duration"]; ok {
|
||||
duration, ok = v.(int)
|
||||
if !ok {
|
||||
duration = DefaultSleepDuration
|
||||
gologger.Warning().Msgf("Invalid sleep_duration parameter type, using default value: %d", duration)
|
||||
}
|
||||
}
|
||||
}
|
||||
data = strings.ReplaceAll(data, "[SLEEPTIME]", strconv.Itoa(duration))
|
||||
data = analyzers.ApplyPayloadTransformations(data)
|
||||
|
||||
// Also support [INFERENCE] for the time delay analyzer
|
||||
if strings.Contains(data, "[INFERENCE]") {
|
||||
randInt := analyzers.GetRandomInteger()
|
||||
data = strings.ReplaceAll(data, "[INFERENCE]", fmt.Sprintf("%d=%d", randInt, randInt))
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
func (a *Analyzer) parseAnalyzerParameters(params map[string]interface{}) (int, int, float64, float64, error) {
|
||||
requestsLimit := DefaultRequestsLimit
|
||||
sleepDuration := DefaultSleepDuration
|
||||
timeCorrelationErrorRange := DefaultTimeCorrelationErrorRange
|
||||
timeSlopeErrorRange := DefaultTimeSlopeErrorRange
|
||||
|
||||
if len(params) == 0 {
|
||||
return requestsLimit, sleepDuration, timeCorrelationErrorRange, timeSlopeErrorRange, nil
|
||||
}
|
||||
var ok bool
|
||||
for k, v := range params {
|
||||
switch k {
|
||||
case "sleep_duration":
|
||||
sleepDuration, ok = v.(int)
|
||||
case "requests_limit":
|
||||
requestsLimit, ok = v.(int)
|
||||
case "time_correlation_error_range":
|
||||
timeCorrelationErrorRange, ok = v.(float64)
|
||||
case "time_slope_error_range":
|
||||
timeSlopeErrorRange, ok = v.(float64)
|
||||
}
|
||||
if !ok {
|
||||
return 0, 0, 0, 0, errors.Errorf("invalid parameter type for %s", k)
|
||||
}
|
||||
}
|
||||
return requestsLimit, sleepDuration, timeCorrelationErrorRange, timeSlopeErrorRange, nil
|
||||
}
|
||||
|
||||
// Analyze is the main function for the analyzer
|
||||
func (a *Analyzer) Analyze(options *analyzers.Options) (bool, string, error) {
|
||||
if options.ResponseTimeDelay < defaultSleepTimeDuration {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
// Parse parameters for this analyzer if any or use default values
|
||||
requestsLimit, sleepDuration, timeCorrelationErrorRange, timeSlopeErrorRange, err :=
|
||||
a.parseAnalyzerParameters(options.AnalyzerParameters)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
gr := options.FuzzGenerated
|
||||
replaced := strings.ReplaceAll(gr.OriginalPayload, "[SLEEPTIME]", strconv.Itoa(delay))
|
||||
replaced = a.ApplyInitialTransformation(replaced, options.AnalyzerParameters)
|
||||
|
||||
if err := gr.Component.SetValue(gr.Key, replaced); err != nil {
|
||||
return 0, errors.Wrap(err, "could not set value in component")
|
||||
}
|
||||
|
||||
rebuilt, err := gr.Component.Rebuild()
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not rebuild request")
|
||||
}
|
||||
gologger.Verbose().Msgf("[%s] Sending request with %d delay for: %s", a.Name(), delay, rebuilt.URL.String())
|
||||
|
||||
timeTaken, err := doHTTPRequestWithTimeTracing(rebuilt, options.HttpClient)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not do request with time tracing")
|
||||
}
|
||||
return timeTaken, nil
|
||||
}
|
||||
matched, matchReason, err := checkTimingDependency(
|
||||
requestsLimit,
|
||||
sleepDuration,
|
||||
timeCorrelationErrorRange,
|
||||
timeSlopeErrorRange,
|
||||
reqSender,
|
||||
)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if matched {
|
||||
return true, matchReason, nil
|
||||
}
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
// doHTTPRequestWithTimeTracing does a http request with time tracing
|
||||
func doHTTPRequestWithTimeTracing(req *retryablehttp.Request, httpclient *retryablehttp.Client) (float64, error) {
|
||||
var ttfb time.Duration
|
||||
var start time.Time
|
||||
|
||||
trace := &httptrace.ClientTrace{
|
||||
GotFirstResponseByte: func() { ttfb = time.Since(start) },
|
||||
}
|
||||
req = req.WithContext(httptrace.WithClientTrace(req.Context(), trace))
|
||||
start = time.Now()
|
||||
resp, err := httpclient.Do(req)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not do request")
|
||||
}
|
||||
|
||||
_, err = io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return 0, errors.Wrap(err, "could not read response body")
|
||||
}
|
||||
return ttfb.Seconds(), nil
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
// Package time implements a time delay analyzer using linear
|
||||
// regression heuristics inspired from ZAP to discover time
|
||||
// based issues.
|
||||
//
|
||||
// The approach is the one used in ZAP for timing based checks.
|
||||
// Advantages of this approach are many compared to the old approach of
|
||||
// heuristics of sleep time.
|
||||
//
|
||||
// As we are building a statistical model, we can predict if the delay
|
||||
// is random or not very quickly. Also, the payloads are alternated to send
|
||||
// a very high sleep and a very low sleep. This way the comparison is
|
||||
// faster to eliminate negative cases. Only legitimate cases are sent for
|
||||
// more verification.
|
||||
//
|
||||
// For more details on the algorithm, follow the links below:
|
||||
// - https://groups.google.com/g/zaproxy-develop/c/KGSkNHlLtqk
|
||||
// - https://github.com/zaproxy/zap-extensions/pull/5053
|
||||
//
|
||||
// This file has been implemented from its original version. It was originally licensed under the Apache License 2.0 (see LICENSE file for details).
|
||||
// The original algorithm is implemented in ZAP Active Scanner.
|
||||
package time
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
type timeDelayRequestSender func(delay int) (float64, error)
|
||||
|
||||
// checkTimingDependency checks the timing dependency for a given request
|
||||
//
|
||||
// It alternates and sends first a high request, then a low request. Each time
|
||||
// it checks if the delay of the application can be predictably controlled.
|
||||
func checkTimingDependency(
|
||||
requestsLimit int,
|
||||
highSleepTimeSeconds int,
|
||||
correlationErrorRange float64,
|
||||
slopeErrorRange float64,
|
||||
requestSender timeDelayRequestSender,
|
||||
) (bool, string, error) {
|
||||
if requestsLimit < 2 {
|
||||
return false, "", errors.New("requests limit should be at least 2")
|
||||
}
|
||||
|
||||
regression := newSimpleLinearRegression()
|
||||
requestsLeft := requestsLimit
|
||||
|
||||
for {
|
||||
if requestsLeft <= 0 {
|
||||
break
|
||||
}
|
||||
|
||||
isCorrelationPossible, err := sendRequestAndTestConfidence(regression, highSleepTimeSeconds, requestSender)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if !isCorrelationPossible {
|
||||
return false, "", nil
|
||||
}
|
||||
|
||||
isCorrelationPossible, err = sendRequestAndTestConfidence(regression, 1, requestSender)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if !isCorrelationPossible {
|
||||
return false, "", nil
|
||||
}
|
||||
requestsLeft = requestsLeft - 2
|
||||
}
|
||||
|
||||
result := regression.IsWithinConfidence(correlationErrorRange, 1.0, slopeErrorRange)
|
||||
if result {
|
||||
resultReason := fmt.Sprintf(
|
||||
"[time_delay] made %d requests successfully, with a regression slope of %.2f and correlation %.2f",
|
||||
requestsLimit,
|
||||
regression.slope,
|
||||
regression.correlation,
|
||||
)
|
||||
return result, resultReason, nil
|
||||
}
|
||||
return result, "", nil
|
||||
}
|
||||
|
||||
// sendRequestAndTestConfidence sends a request and tests the confidence of delay
|
||||
func sendRequestAndTestConfidence(
|
||||
regression *simpleLinearRegression,
|
||||
delay int,
|
||||
requestSender timeDelayRequestSender,
|
||||
) (bool, error) {
|
||||
delayReceived, err := requestSender(delay)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if delayReceived < float64(delay) {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
regression.AddPoint(float64(delay), delayReceived)
|
||||
|
||||
if !regression.IsWithinConfidence(0.3, 1.0, 0.5) {
|
||||
return false, nil
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// simpleLinearRegression is a simple linear regression model that can be updated at runtime.
|
||||
// It is based on the same algorithm in ZAP for doing timing checks.
|
||||
type simpleLinearRegression struct {
|
||||
count float64
|
||||
independentSum float64
|
||||
dependentSum float64
|
||||
|
||||
// Variances
|
||||
independentVarianceN float64
|
||||
dependentVarianceN float64
|
||||
sampleCovarianceN float64
|
||||
|
||||
slope float64
|
||||
intercept float64
|
||||
correlation float64
|
||||
}
|
||||
|
||||
func newSimpleLinearRegression() *simpleLinearRegression {
|
||||
return &simpleLinearRegression{
|
||||
slope: 1,
|
||||
correlation: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func (o *simpleLinearRegression) AddPoint(x, y float64) {
|
||||
independentResidualAdjustment := x - o.independentSum/o.count
|
||||
dependentResidualAdjustment := y - o.dependentSum/o.count
|
||||
|
||||
o.count += 1
|
||||
o.independentSum += x
|
||||
o.dependentSum += y
|
||||
|
||||
if math.IsNaN(independentResidualAdjustment) {
|
||||
return
|
||||
}
|
||||
|
||||
independentResidual := x - o.independentSum/o.count
|
||||
dependentResidual := y - o.dependentSum/o.count
|
||||
|
||||
o.independentVarianceN += independentResidual * independentResidualAdjustment
|
||||
o.dependentVarianceN += dependentResidual * dependentResidualAdjustment
|
||||
o.sampleCovarianceN += independentResidual * dependentResidualAdjustment
|
||||
|
||||
o.slope = o.sampleCovarianceN / o.independentVarianceN
|
||||
o.correlation = o.slope * math.Sqrt(o.independentVarianceN/o.dependentVarianceN)
|
||||
o.correlation *= o.correlation
|
||||
|
||||
// NOTE: zap had the reverse formula, changed it to the correct one
|
||||
// for intercept. Verify if this is correct.
|
||||
o.intercept = o.dependentSum/o.count - o.slope*(o.independentSum/o.count)
|
||||
if math.IsNaN(o.correlation) {
|
||||
o.correlation = 1
|
||||
}
|
||||
}
|
||||
|
||||
func (o *simpleLinearRegression) Predict(x float64) float64 {
|
||||
return o.slope*x + o.intercept
|
||||
}
|
||||
|
||||
func (o *simpleLinearRegression) IsWithinConfidence(correlationErrorRange float64, expectedSlope float64, slopeErrorRange float64,
|
||||
) bool {
|
||||
return o.correlation > 1.0-correlationErrorRange
|
||||
//math.Abs(expectedSlope-o.slope) < slopeErrorRange
|
||||
}
|
||||
@@ -0,0 +1,143 @@
|
||||
// Tests ported from ZAP Java version of the algorithm
|
||||
|
||||
package time
|
||||
|
||||
import (
|
||||
"math"
|
||||
"math/rand"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
correlationErrorRange = float64(0.1)
|
||||
slopeErrorRange = float64(0.2)
|
||||
)
|
||||
|
||||
var rng = rand.New(rand.NewSource(time.Now().UnixNano()))
|
||||
|
||||
func Test_should_generate_alternating_sequences(t *testing.T) {
|
||||
var generatedDelays []float64
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
generatedDelays = append(generatedDelays, float64(delay))
|
||||
return float64(delay), nil
|
||||
}
|
||||
matched, _, err := checkTimingDependency(4, 15, correlationErrorRange, slopeErrorRange, reqSender)
|
||||
require.NoError(t, err)
|
||||
require.True(t, matched)
|
||||
require.EqualValues(t, []float64{15, 1, 15, 1}, generatedDelays)
|
||||
}
|
||||
|
||||
func Test_should_giveup_non_injectable(t *testing.T) {
|
||||
var timesCalled int
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
timesCalled++
|
||||
return 0.5, nil
|
||||
}
|
||||
matched, _, err := checkTimingDependency(4, 15, correlationErrorRange, slopeErrorRange, reqSender)
|
||||
require.NoError(t, err)
|
||||
require.False(t, matched)
|
||||
require.Equal(t, 1, timesCalled)
|
||||
}
|
||||
|
||||
func Test_should_giveup_slow_non_injectable(t *testing.T) {
|
||||
var timesCalled int
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
timesCalled++
|
||||
return 10 + rng.Float64()*0.5, nil
|
||||
}
|
||||
matched, _, err := checkTimingDependency(4, 15, correlationErrorRange, slopeErrorRange, reqSender)
|
||||
require.NoError(t, err)
|
||||
require.False(t, matched)
|
||||
require.LessOrEqual(t, timesCalled, 3)
|
||||
}
|
||||
|
||||
func Test_should_giveup_slow_non_injectable_realworld(t *testing.T) {
|
||||
var timesCalled int
|
||||
var iteration = 0
|
||||
counts := []float64{21, 11, 21, 11}
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
timesCalled++
|
||||
iteration++
|
||||
return counts[iteration-1], nil
|
||||
}
|
||||
matched, _, err := checkTimingDependency(4, 15, correlationErrorRange, slopeErrorRange, reqSender)
|
||||
require.NoError(t, err)
|
||||
require.False(t, matched)
|
||||
require.LessOrEqual(t, timesCalled, 4)
|
||||
}
|
||||
|
||||
func Test_should_detect_dependence_with_small_error(t *testing.T) {
|
||||
reqSender := func(delay int) (float64, error) {
|
||||
return float64(delay) + rng.Float64()*0.5, nil
|
||||
}
|
||||
matched, reason, err := checkTimingDependency(4, 15, correlationErrorRange, slopeErrorRange, reqSender)
|
||||
require.NoError(t, err)
|
||||
require.True(t, matched)
|
||||
require.NotEmpty(t, reason)
|
||||
}
|
||||
|
||||
func Test_LinearRegression_Numerical_stability(t *testing.T) {
|
||||
variables := [][]float64{
|
||||
{1, 1}, {2, 2}, {3, 3}, {4, 4}, {5, 5}, {1, 1}, {2, 2}, {2, 2}, {2, 2},
|
||||
}
|
||||
slope := float64(1)
|
||||
correlation := float64(1)
|
||||
|
||||
regression := newSimpleLinearRegression()
|
||||
for _, v := range variables {
|
||||
regression.AddPoint(v[0], v[1])
|
||||
}
|
||||
require.True(t, almostEqual(regression.slope, slope))
|
||||
require.True(t, almostEqual(regression.correlation, correlation))
|
||||
}
|
||||
|
||||
func Test_LinearRegression_exact_verify(t *testing.T) {
|
||||
variables := [][]float64{
|
||||
{1, 1}, {2, 3},
|
||||
}
|
||||
slope := float64(2)
|
||||
correlation := float64(1)
|
||||
|
||||
regression := newSimpleLinearRegression()
|
||||
for _, v := range variables {
|
||||
regression.AddPoint(v[0], v[1])
|
||||
}
|
||||
require.True(t, almostEqual(regression.slope, slope))
|
||||
require.True(t, almostEqual(regression.correlation, correlation))
|
||||
}
|
||||
|
||||
func Test_LinearRegression_known_verify(t *testing.T) {
|
||||
variables := [][]float64{
|
||||
{1, 1.348520581}, {2, 2.524046187}, {3, 3.276944688}, {4, 4.735374498}, {5, 5.150291657},
|
||||
}
|
||||
slope := float64(0.981487046)
|
||||
correlation := float64(0.979228906)
|
||||
|
||||
regression := newSimpleLinearRegression()
|
||||
for _, v := range variables {
|
||||
regression.AddPoint(v[0], v[1])
|
||||
}
|
||||
require.True(t, almostEqual(regression.slope, slope))
|
||||
require.True(t, almostEqual(regression.correlation, correlation))
|
||||
}
|
||||
|
||||
func Test_LinearRegression_nonlinear_verify(t *testing.T) {
|
||||
variables := [][]float64{
|
||||
{1, 2}, {2, 4}, {3, 8}, {4, 16}, {5, 32},
|
||||
}
|
||||
|
||||
regression := newSimpleLinearRegression()
|
||||
for _, v := range variables {
|
||||
regression.AddPoint(v[0], v[1])
|
||||
}
|
||||
require.Less(t, regression.correlation, 0.9)
|
||||
}
|
||||
|
||||
const float64EqualityThreshold = 1e-8
|
||||
|
||||
func almostEqual(a, b float64) bool {
|
||||
return math.Abs(a-b) <= float64EqualityThreshold
|
||||
}
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/maps"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
|
||||
+18
-2
@@ -50,6 +50,11 @@ type ExecuteRuleInput struct {
|
||||
BaseRequest *retryablehttp.Request
|
||||
// DisplayFuzzPoints is a flag to display fuzz points
|
||||
DisplayFuzzPoints bool
|
||||
|
||||
// ApplyPayloadInitialTransformation is an optional function
|
||||
// to transform the payload initially based on analyzer rules
|
||||
ApplyPayloadInitialTransformation func(string, map[string]interface{}) string
|
||||
AnalyzerParams map[string]interface{}
|
||||
}
|
||||
|
||||
// GeneratedRequest is a single generated request for rule
|
||||
@@ -64,6 +69,15 @@ type GeneratedRequest struct {
|
||||
Component component.Component
|
||||
// Parameter being fuzzed
|
||||
Parameter string
|
||||
|
||||
// Key is the key for the request
|
||||
Key string
|
||||
// Value is the value for the request
|
||||
Value string
|
||||
// OriginalValue is the original value for the request
|
||||
OriginalValue string
|
||||
// OriginalPayload is the original payload for the request
|
||||
OriginalPayload string
|
||||
}
|
||||
|
||||
// Execute executes a fuzzing rule accepting a callback on which
|
||||
@@ -216,7 +230,9 @@ func (rule *Rule) executeRuleValues(input *ExecuteRuleInput, ruleComponent compo
|
||||
// if we are only fuzzing values
|
||||
if len(rule.Fuzz.Value) > 0 {
|
||||
for _, value := range rule.Fuzz.Value {
|
||||
if err := rule.executePartRule(input, ValueOrKeyValue{Value: value}, ruleComponent); err != nil {
|
||||
originalPayload := value
|
||||
|
||||
if err := rule.executePartRule(input, ValueOrKeyValue{Value: value, OriginalPayload: originalPayload}, ruleComponent); err != nil {
|
||||
if component.IsErrSetValue(err) {
|
||||
// this are errors due to format restrictions
|
||||
// ex: fuzzing string value in a json int field
|
||||
@@ -257,7 +273,7 @@ func (rule *Rule) executeRuleValues(input *ExecuteRuleInput, ruleComponent compo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gotErr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", ""); gotErr != nil {
|
||||
if gotErr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", "", "", "", "", ""); gotErr != nil {
|
||||
return gotErr
|
||||
}
|
||||
}
|
||||
|
||||
+23
-13
@@ -42,14 +42,14 @@ func (rule *Rule) executePartComponent(input *ExecuteRuleInput, payload ValueOrK
|
||||
return rule.executePartComponentOnKV(input, payload, ruleComponent.Clone())
|
||||
} else {
|
||||
// for value only fuzzing
|
||||
return rule.executePartComponentOnValues(input, payload.Value, ruleComponent.Clone())
|
||||
return rule.executePartComponentOnValues(input, payload.Value, payload.OriginalPayload, ruleComponent.Clone())
|
||||
}
|
||||
}
|
||||
|
||||
// executePartComponentOnValues executes this rule on a given component and payload
|
||||
// this supports both single and multiple [ruleType] modes
|
||||
// i.e if component has multiple values, they can be replaced once or all depending on mode
|
||||
func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadStr string, ruleComponent component.Component) error {
|
||||
func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadStr, originalPayload string, ruleComponent component.Component) error {
|
||||
finalErr := ruleComponent.Iterate(func(key string, value interface{}) error {
|
||||
valueStr := types.ToString(value)
|
||||
if !rule.matchKeyOrValue(key, valueStr) {
|
||||
@@ -57,8 +57,13 @@ func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadS
|
||||
return nil
|
||||
}
|
||||
|
||||
var evaluated string
|
||||
var evaluated, originalEvaluated string
|
||||
evaluated, input.InteractURLs = rule.executeEvaluate(input, key, valueStr, payloadStr, input.InteractURLs)
|
||||
if input.ApplyPayloadInitialTransformation != nil {
|
||||
evaluated = input.ApplyPayloadInitialTransformation(evaluated, input.AnalyzerParams)
|
||||
originalEvaluated, _ = rule.executeEvaluate(input, key, valueStr, originalPayload, input.InteractURLs)
|
||||
}
|
||||
|
||||
if err := ruleComponent.SetValue(key, evaluated); err != nil {
|
||||
// gologger.Warning().Msgf("could not set value due to format restriction original(%s, %s[%T]) , new(%s,%s[%T])", key, valueStr, value, key, evaluated, evaluated)
|
||||
return nil
|
||||
@@ -70,7 +75,7 @@ func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadS
|
||||
return err
|
||||
}
|
||||
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, valueStr); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, valueStr, originalEvaluated, valueStr, key, evaluated); qerr != nil {
|
||||
return qerr
|
||||
}
|
||||
// fmt.Printf("executed with value: %s\n", evaluated)
|
||||
@@ -92,7 +97,7 @@ func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadS
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", ""); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", "", "", "", "", ""); qerr != nil {
|
||||
err = qerr
|
||||
return err
|
||||
}
|
||||
@@ -127,7 +132,7 @@ func (rule *Rule) executePartComponentOnKV(input *ExecuteRuleInput, payload Valu
|
||||
return err
|
||||
}
|
||||
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, value); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, value, "", "", "", ""); qerr != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -146,12 +151,13 @@ func (rule *Rule) executePartComponentOnKV(input *ExecuteRuleInput, payload Valu
|
||||
}
|
||||
|
||||
// execWithInput executes a rule with input via callback
|
||||
func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.Request, interactURLs []string, component component.Component, parameter, parameterValue string) error {
|
||||
func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.Request, interactURLs []string, component component.Component, parameter, parameterValue, originalPayload, originalValue, key, value string) error {
|
||||
// If the parameter is a number, replace it with the parameter value
|
||||
// or if the parameter is empty and the parameter value is not empty
|
||||
// replace it with the parameter value
|
||||
actualParameter := parameter
|
||||
if _, err := strconv.Atoi(parameter); err == nil || (parameter == "" && parameterValue != "") {
|
||||
parameter = parameterValue
|
||||
actualParameter = parameterValue
|
||||
}
|
||||
// If the parameter is frequent, skip it if the option is enabled
|
||||
if rule.options.FuzzParamsFrequency != nil {
|
||||
@@ -164,11 +170,15 @@ func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.
|
||||
}
|
||||
}
|
||||
request := GeneratedRequest{
|
||||
Request: httpReq,
|
||||
InteractURLs: interactURLs,
|
||||
DynamicValues: input.Values,
|
||||
Component: component,
|
||||
Parameter: parameter,
|
||||
Request: httpReq,
|
||||
InteractURLs: interactURLs,
|
||||
DynamicValues: input.Values,
|
||||
Component: component,
|
||||
Parameter: actualParameter,
|
||||
Key: key,
|
||||
Value: value,
|
||||
OriginalValue: originalValue,
|
||||
OriginalPayload: originalPayload,
|
||||
}
|
||||
if !input.Callback(request) {
|
||||
return types.ErrNoMoreRequests
|
||||
|
||||
@@ -19,6 +19,8 @@ var (
|
||||
type ValueOrKeyValue struct {
|
||||
Key string
|
||||
Value string
|
||||
|
||||
OriginalPayload string
|
||||
}
|
||||
|
||||
func (v *ValueOrKeyValue) IsKV() bool {
|
||||
|
||||
@@ -39,6 +39,11 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
|
||||
}
|
||||
}
|
||||
|
||||
if output.GlobalMatchers {
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(w.aurora.BrightMagenta("global").String())
|
||||
}
|
||||
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(w.aurora.BrightBlue(output.Type).String())
|
||||
builder.WriteString("] ")
|
||||
|
||||
@@ -184,6 +184,9 @@ type ResultEvent struct {
|
||||
MatcherStatus bool `json:"matcher-status"`
|
||||
// Lines is the line count for the specified match
|
||||
Lines []int `json:"matched-line,omitempty"`
|
||||
// GlobalMatchers identifies whether the matches was detected in the response
|
||||
// of another template's result event
|
||||
GlobalMatchers bool `json:"global-matchers,omitempty"`
|
||||
|
||||
// IssueTrackers is the metadata for issue trackers
|
||||
IssueTrackers map[string]IssueTrackerMetadata `json:"issue_trackers,omitempty"`
|
||||
@@ -198,6 +201,7 @@ type ResultEvent struct {
|
||||
FuzzingMethod string `json:"fuzzing_method,omitempty"`
|
||||
FuzzingParameter string `json:"fuzzing_parameter,omitempty"`
|
||||
FuzzingPosition string `json:"fuzzing_position,omitempty"`
|
||||
AnalyzerDetails string `json:"analyzer_details,omitempty"`
|
||||
|
||||
FileToIndexPosition map[string]int `json:"-"`
|
||||
TemplateVerifier string `json:"-"`
|
||||
|
||||
@@ -86,6 +86,12 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
|
||||
Args: request.Args,
|
||||
EarlyCloseFileDescriptor: true,
|
||||
}
|
||||
|
||||
if options.Options.Debug || options.Options.DebugResponse {
|
||||
// enable debug mode for gozero
|
||||
gozeroOptions.DebugMode = true
|
||||
}
|
||||
|
||||
engine, err := gozero.New(gozeroOptions)
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("[%s] engines '%s' not available on host", options.TemplateID, strings.Join(request.Engine, ","))
|
||||
@@ -235,11 +241,26 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
gologger.Verbose().Msgf("[%s] Executed code on local machine %v", request.options.TemplateID, input.MetaInput.Input)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Code Protocol request variables: \n%s\n", vardump.DumpVariables(allvars))
|
||||
gologger.Debug().Msgf("Code Protocol request variables: %s\n", vardump.DumpVariables(allvars))
|
||||
}
|
||||
|
||||
if request.options.Options.Debug || request.options.Options.DebugRequests {
|
||||
gologger.Debug().Msgf("[%s] Dumped Executed Source Code for %v\n\n%v\n", request.options.TemplateID, input.MetaInput.Input, interpretEnvVars(request.Source, allvars))
|
||||
gologger.Debug().MsgFunc(func() string {
|
||||
dashes := strings.Repeat("-", 15)
|
||||
sb := &strings.Builder{}
|
||||
sb.WriteString(fmt.Sprintf("[%s] Dumped Executed Source Code for input/stdin: '%v'", request.options.TemplateID, input.MetaInput.Input))
|
||||
sb.WriteString(fmt.Sprintf("\n%v\n%v\n%v\n", dashes, "Source Code:", dashes))
|
||||
sb.WriteString(interpretEnvVars(request.Source, allvars))
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString(fmt.Sprintf("\n%v\n%v\n%v\n", dashes, "Command Executed:", dashes))
|
||||
sb.WriteString(interpretEnvVars(gOutput.Command, allvars))
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString(fmt.Sprintf("\n%v\n%v\n%v\n", dashes, "Command Output:", dashes))
|
||||
sb.WriteString(gOutput.DebugData.String())
|
||||
sb.WriteString("\n")
|
||||
sb.WriteString("[WRN] Command Output here is stdout+sterr, in response variables they are seperate (use -v -svd flags for more details)")
|
||||
return sb.String()
|
||||
})
|
||||
}
|
||||
|
||||
dataOutputString := fmtStdout(gOutput.Stdout.String())
|
||||
|
||||
@@ -142,6 +142,9 @@ func (metaInput *MetaInput) Unmarshal(data string) error {
|
||||
}
|
||||
|
||||
func (metaInput *MetaInput) Clone() *MetaInput {
|
||||
metaInput.mu.Lock()
|
||||
defer metaInput.mu.Unlock()
|
||||
|
||||
input := NewMetaInput()
|
||||
input.Input = metaInput.Input
|
||||
input.CustomIP = metaInput.CustomIP
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package globalmatchers
|
||||
|
||||
import (
|
||||
"maps"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
)
|
||||
|
||||
// Storage is a struct that holds the global matchers
|
||||
type Storage struct {
|
||||
requests []*Item
|
||||
}
|
||||
|
||||
// Callback is called when a global matcher is matched.
|
||||
// It receives internal event & result of the operator execution.
|
||||
type Callback func(event output.InternalEvent, result *operators.Result)
|
||||
|
||||
// Item is a struct that holds the global matchers
|
||||
// details for a template
|
||||
type Item struct {
|
||||
TemplateID string
|
||||
TemplatePath string
|
||||
TemplateInfo model.Info
|
||||
Operators []*operators.Operators
|
||||
}
|
||||
|
||||
// New creates a new storage for global matchers
|
||||
func New() *Storage {
|
||||
return &Storage{requests: make([]*Item, 0)}
|
||||
}
|
||||
|
||||
// hasStorage checks if the Storage is initialized
|
||||
func (s *Storage) hasStorage() bool {
|
||||
return s != nil
|
||||
}
|
||||
|
||||
// AddOperator adds a new operator to the global matchers
|
||||
func (s *Storage) AddOperator(item *Item) {
|
||||
if !s.hasStorage() {
|
||||
return
|
||||
}
|
||||
|
||||
s.requests = append(s.requests, item)
|
||||
}
|
||||
|
||||
// HasMatchers returns true if we have global matchers
|
||||
func (s *Storage) HasMatchers() bool {
|
||||
if !s.hasStorage() {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(s.requests) > 0
|
||||
}
|
||||
|
||||
// Match matches the global matchers against the response
|
||||
func (s *Storage) Match(
|
||||
event output.InternalEvent,
|
||||
matchFunc operators.MatchFunc,
|
||||
extractFunc operators.ExtractFunc,
|
||||
isDebug bool,
|
||||
callback Callback,
|
||||
) {
|
||||
for _, item := range s.requests {
|
||||
for _, operator := range item.Operators {
|
||||
newEvent := maps.Clone(event)
|
||||
newEvent.Set("origin-template-id", event["template-id"])
|
||||
newEvent.Set("origin-template-info", event["template-info"])
|
||||
newEvent.Set("origin-template-path", event["template-path"])
|
||||
newEvent.Set("template-id", item.TemplateID)
|
||||
newEvent.Set("template-info", item.TemplateInfo)
|
||||
newEvent.Set("template-path", item.TemplatePath)
|
||||
newEvent.Set("global-matchers", true)
|
||||
|
||||
result, matched := operator.Execute(newEvent, matchFunc, extractFunc, isDebug)
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
callback(newEvent, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func CreateEventWithAdditionalOptions(request protocols.Request, outputEvent out
|
||||
// Dump response variables if ran in debug mode
|
||||
if vardump.EnableVarDump {
|
||||
protoName := cases.Title(language.English).String(request.Type().String())
|
||||
gologger.Debug().Msgf("%v Protocol response variables: \n%s\n", protoName, vardump.DumpVariables(outputEvent))
|
||||
gologger.Debug().Msgf("%v Protocol response variables: %s\n", protoName, vardump.DumpVariables(outputEvent))
|
||||
}
|
||||
for _, compiledOperator := range request.GetCompiledOperators() {
|
||||
if compiledOperator != nil {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/network/networkclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/whois/rdapclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
_ "github.com/projectdiscovery/utils/global"
|
||||
)
|
||||
|
||||
// Init initializes the client pools for the protocols
|
||||
|
||||
@@ -19,7 +19,7 @@ var (
|
||||
)
|
||||
|
||||
func StartActiveMemGuardian(ctx context.Context) {
|
||||
if memguardian.DefaultMemGuardian == nil {
|
||||
if memguardian.DefaultMemGuardian == nil || memTimer != nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
package vardump
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
"github.com/yassinebenaid/godump"
|
||||
)
|
||||
|
||||
// EnableVarDump enables var dump for debugging optionally
|
||||
var EnableVarDump bool
|
||||
// variables is a map of variables
|
||||
type variables = map[string]any
|
||||
|
||||
// DumpVariables writes the truncated dump of variables to a string
|
||||
// in a formatted key-value manner.
|
||||
//
|
||||
// The values are truncated to return 50 characters from start and end.
|
||||
func DumpVariables(data map[string]interface{}) string {
|
||||
var counter int
|
||||
// DumpVariables dumps the variables in a pretty format
|
||||
func DumpVariables(data variables) string {
|
||||
if !EnableVarDump {
|
||||
return ""
|
||||
}
|
||||
|
||||
buffer := &strings.Builder{}
|
||||
buffer.Grow(len(data) * 78) // grow buffer to an approximate size
|
||||
d := godump.Dumper{
|
||||
Indentation: " ",
|
||||
HidePrivateFields: false,
|
||||
ShowPrimitiveNamedTypes: true,
|
||||
}
|
||||
|
||||
builder := &strings.Builder{}
|
||||
// sort keys for deterministic output
|
||||
d.Theme = godump.Theme{
|
||||
String: godump.RGB{R: 138, G: 201, B: 38},
|
||||
Quotes: godump.RGB{R: 112, G: 214, B: 255},
|
||||
Bool: godump.RGB{R: 249, G: 87, B: 56},
|
||||
Number: godump.RGB{R: 10, G: 178, B: 242},
|
||||
Types: godump.RGB{R: 0, G: 150, B: 199},
|
||||
Address: godump.RGB{R: 205, G: 93, B: 0},
|
||||
PointerTag: godump.RGB{R: 110, G: 110, B: 110},
|
||||
Nil: godump.RGB{R: 219, G: 57, B: 26},
|
||||
Func: godump.RGB{R: 160, G: 90, B: 220},
|
||||
Fields: godump.RGB{R: 189, G: 176, B: 194},
|
||||
Chan: godump.RGB{R: 195, G: 154, B: 76},
|
||||
UnsafePointer: godump.RGB{R: 89, G: 193, B: 180},
|
||||
Braces: godump.RGB{R: 185, G: 86, B: 86},
|
||||
}
|
||||
|
||||
return d.Sprint(process(data, Limit))
|
||||
}
|
||||
|
||||
// process is a helper function that processes the variables
|
||||
// and returns a new map of variables
|
||||
func process(data variables, limit int) variables {
|
||||
keys := mapsutil.GetSortedKeys(data)
|
||||
vars := make(variables)
|
||||
|
||||
if limit == 0 {
|
||||
limit = 255
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
v := data[k]
|
||||
valueString := types.ToString(v)
|
||||
|
||||
counter++
|
||||
if len(valueString) > 50 {
|
||||
builder.Grow(56)
|
||||
builder.WriteString(valueString[0:25])
|
||||
builder.WriteString(" .... ")
|
||||
builder.WriteString(valueString[len(valueString)-25:])
|
||||
valueString = builder.String()
|
||||
builder.Reset()
|
||||
v := types.ToString(data[k])
|
||||
v = strings.ReplaceAll(strings.ReplaceAll(v, "\r", " "), "\n", " ")
|
||||
if len(v) > limit {
|
||||
v = v[:limit]
|
||||
v += " [...]"
|
||||
}
|
||||
valueString = strings.ReplaceAll(strings.ReplaceAll(valueString, "\r", " "), "\n", " ")
|
||||
|
||||
buffer.WriteString("\t")
|
||||
buffer.WriteString(strconv.Itoa(counter))
|
||||
buffer.WriteString(". ")
|
||||
buffer.WriteString(k)
|
||||
buffer.WriteString(" => ")
|
||||
buffer.WriteString(valueString)
|
||||
buffer.WriteString("\n")
|
||||
vars[k] = v
|
||||
}
|
||||
final := buffer.String()
|
||||
return final
|
||||
|
||||
return vars
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package vardump
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDumpVariables(t *testing.T) {
|
||||
// Enable var dump for testing
|
||||
EnableVarDump = true
|
||||
|
||||
// Test case
|
||||
testVars := variables{
|
||||
"string": "test",
|
||||
"int": 42,
|
||||
"bool": true,
|
||||
"slice": []string{"a", "b", "c"},
|
||||
}
|
||||
|
||||
result := DumpVariables(testVars)
|
||||
|
||||
// Assertions
|
||||
assert.NotEmpty(t, result)
|
||||
assert.Contains(t, result, "string")
|
||||
assert.Contains(t, result, "test")
|
||||
assert.Contains(t, result, "int")
|
||||
assert.Contains(t, result, "42")
|
||||
assert.Contains(t, result, "bool")
|
||||
assert.Contains(t, result, "true")
|
||||
assert.Contains(t, result, "slice")
|
||||
assert.Contains(t, result, "a")
|
||||
assert.Contains(t, result, "b")
|
||||
assert.Contains(t, result, "c")
|
||||
|
||||
// Test with EnableVarDump set to false
|
||||
EnableVarDump = false
|
||||
result = DumpVariables(testVars)
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
testVars := variables{
|
||||
"short": "short string",
|
||||
"long": strings.Repeat("a", 300),
|
||||
"number": 42,
|
||||
}
|
||||
|
||||
processed := process(testVars, 255)
|
||||
|
||||
assert.Equal(t, "short string", processed["short"])
|
||||
assert.Equal(t, strings.Repeat("a", 255)+" [...]", processed["long"])
|
||||
assert.Equal(t, "42", processed["number"])
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package vardump
|
||||
|
||||
var (
|
||||
// EnableVarDump enables var dump for debugging optionally
|
||||
EnableVarDump bool
|
||||
// Limit is the maximum characters to be dumped
|
||||
Limit int = 255
|
||||
)
|
||||
@@ -108,7 +108,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata,
|
||||
func (request *Request) execute(input *contextargs.Context, domain string, metadata, previous output.InternalEvent, vars map[string]interface{}, callback protocols.OutputEventCallback) error {
|
||||
var err error
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("DNS Protocol request variables: \n%s\n", vardump.DumpVariables(vars))
|
||||
gologger.Debug().Msgf("DNS Protocol request variables: %s\n", vardump.DumpVariables(vars))
|
||||
}
|
||||
|
||||
// Compile each request for the template based on the URL
|
||||
|
||||
@@ -334,7 +334,7 @@ func (p *Page) NavigateURL(action *Action, out ActionData, allvars map[string]in
|
||||
allvars = generators.MergeMaps(allvars, defaultReqVars)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: \n%s\n", vardump.DumpVariables(allvars))
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: %s\n", vardump.DumpVariables(allvars))
|
||||
}
|
||||
|
||||
// Evaluate the target url with all variables
|
||||
|
||||
@@ -30,7 +30,7 @@ import (
|
||||
|
||||
var _ protocols.Request = &Request{}
|
||||
|
||||
const errCouldGetHtmlElement = "could get html element"
|
||||
const errCouldNotGetHtmlElement = "could not get html element"
|
||||
|
||||
// Type returns the type of the protocol request
|
||||
func (request *Request) Type() templateTypes.ProtocolType {
|
||||
@@ -117,12 +117,12 @@ func (request *Request) executeRequestWithPayloads(input *contextargs.Context, p
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, errCouldGetHtmlElement)
|
||||
return errors.Wrap(err, errCouldNotGetHtmlElement)
|
||||
}
|
||||
defer instance.Close()
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: \n%s\n", vardump.DumpVariables(payloads))
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: %s\n", vardump.DumpVariables(payloads))
|
||||
}
|
||||
|
||||
instance.SetInteractsh(request.options.Interactsh)
|
||||
@@ -130,7 +130,7 @@ func (request *Request) executeRequestWithPayloads(input *contextargs.Context, p
|
||||
if _, err := url.Parse(input.MetaInput.Input); err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, errCouldGetHtmlElement)
|
||||
return errors.Wrap(err, errCouldNotGetHtmlElement)
|
||||
}
|
||||
options := &engine.Options{
|
||||
Timeout: time.Duration(request.options.Options.PageTimeout) * time.Second,
|
||||
@@ -146,7 +146,7 @@ func (request *Request) executeRequestWithPayloads(input *contextargs.Context, p
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, input.MetaInput.Input, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, errCouldGetHtmlElement)
|
||||
return errors.Wrap(err, errCouldNotGetHtmlElement)
|
||||
}
|
||||
defer page.Close()
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/expressions"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
@@ -55,6 +56,8 @@ type generatedRequest struct {
|
||||
// requestURLPattern tracks unmodified request url pattern without values ( it is used for constant vuln_hash)
|
||||
// ex: {{BaseURL}}/api/exp?param={{randstr}}
|
||||
requestURLPattern string
|
||||
|
||||
fuzzGeneratedRequest fuzz.GeneratedRequest
|
||||
}
|
||||
|
||||
// setReqURLPattern sets the url request pattern for the generated request
|
||||
@@ -204,7 +207,7 @@ func (r *requestGenerator) Make(ctx context.Context, input *contextargs.Context,
|
||||
finalVars := generators.MergeMaps(allVars, payloads)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("HTTP Protocol request variables: \n%s\n", vardump.DumpVariables(finalVars))
|
||||
gologger.Debug().Msgf("HTTP Protocol request variables: %s\n", vardump.DumpVariables(finalVars))
|
||||
}
|
||||
|
||||
// Note: If possible any changes to current logic (i.e evaluate -> then parse URL)
|
||||
|
||||
@@ -3,13 +3,18 @@ package http
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
json "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
_ "github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers/time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
@@ -126,6 +131,9 @@ type Request struct {
|
||||
|
||||
// Fuzzing describes schema to fuzz http requests
|
||||
Fuzzing []*fuzz.Rule `yaml:"fuzzing,omitempty" json:"fuzzing,omitempty" jsonschema:"title=fuzzin rules for http fuzzing,description=Fuzzing describes rule schema to fuzz http requests"`
|
||||
// description: |
|
||||
// Analyzer is an analyzer to use for matching the response.
|
||||
Analyzer *analyzers.AnalyzerTemplate `yaml:"analyzer,omitempty" json:"analyzer,omitempty" jsonschema:"title=analyzer for http request,description=Analyzer for HTTP Request"`
|
||||
|
||||
CompiledOperators *operators.Operators `yaml:"-" json:"-"`
|
||||
|
||||
@@ -223,6 +231,9 @@ type Request struct {
|
||||
// FuzzPreConditionOperator is the operator between multiple PreConditions for fuzzing Default is OR
|
||||
FuzzPreConditionOperator string `yaml:"pre-condition-operator,omitempty" json:"pre-condition-operator,omitempty" jsonschema:"title=condition between the filters,description=Operator to use between multiple per-conditions,enum=and,enum=or"`
|
||||
fuzzPreConditionOperator matchers.ConditionType `yaml:"-" json:"-"`
|
||||
// description: |
|
||||
// GlobalMatchers marks matchers as static and applies globally to all result events from other templates
|
||||
GlobalMatchers bool `yaml:"global-matchers,omitempty" json:"global-matchers,omitempty" jsonschema:"title=global matchers,description=marks matchers as static and applies globally to all result events from other templates"`
|
||||
}
|
||||
|
||||
func (e Request) JSONSchemaExtend(schema *jsonschema.Schema) {
|
||||
@@ -300,6 +311,21 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
|
||||
},
|
||||
RedirectFlow: httpclientpool.DontFollowRedirect,
|
||||
}
|
||||
var customTimeout int
|
||||
if request.Analyzer != nil && request.Analyzer.Name == "time_delay" {
|
||||
var timeoutVal int
|
||||
if timeout, ok := request.Analyzer.Parameters["sleep_duration"]; ok {
|
||||
timeoutVal, _ = timeout.(int)
|
||||
} else {
|
||||
timeoutVal = 5
|
||||
}
|
||||
|
||||
// Add 3x buffer to the timeout
|
||||
customTimeout = int(math.Ceil(float64(timeoutVal) * 3))
|
||||
}
|
||||
if customTimeout > 0 {
|
||||
connectionConfiguration.Connection.CustomMaxTimeout = time.Duration(customTimeout) * time.Second
|
||||
}
|
||||
|
||||
if request.Redirects || options.Options.FollowRedirects {
|
||||
connectionConfiguration.RedirectFlow = httpclientpool.FollowAllRedirect
|
||||
@@ -366,6 +392,12 @@ func (request *Request) Compile(options *protocols.ExecutorOptions) error {
|
||||
}
|
||||
}
|
||||
|
||||
if request.Analyzer != nil {
|
||||
if analyzer := analyzers.GetAnalyzer(request.Analyzer.Name); analyzer == nil {
|
||||
return errors.Errorf("analyzer %s not found", request.Analyzer.Name)
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve payload paths from vars if they exists
|
||||
for name, payload := range request.options.Options.Vars.AsMap() {
|
||||
payloadStr, ok := payload.(string)
|
||||
|
||||
@@ -60,6 +60,9 @@ func Init(options *types.Options) error {
|
||||
type ConnectionConfiguration struct {
|
||||
// DisableKeepAlive of the connection
|
||||
DisableKeepAlive bool
|
||||
// CustomMaxTimeout is the custom timeout for the connection
|
||||
// This overrides all other timeouts and is used for accurate time based fuzzing.
|
||||
CustomMaxTimeout time.Duration
|
||||
cookiejar *cookiejar.Jar
|
||||
mu sync.RWMutex
|
||||
}
|
||||
@@ -108,6 +111,7 @@ func (c *Configuration) Clone() *Configuration {
|
||||
if c.Connection != nil {
|
||||
cloneConnection := &ConnectionConfiguration{
|
||||
DisableKeepAlive: c.Connection.DisableKeepAlive,
|
||||
CustomMaxTimeout: c.Connection.CustomMaxTimeout,
|
||||
}
|
||||
if c.Connection.HasCookieJar() {
|
||||
cookiejar := *c.Connection.GetCookieJar()
|
||||
@@ -135,6 +139,10 @@ func (c *Configuration) Hash() string {
|
||||
builder.WriteString(strconv.FormatBool(c.DisableCookie))
|
||||
builder.WriteString("c")
|
||||
builder.WriteString(strconv.FormatBool(c.Connection != nil))
|
||||
if c.Connection != nil && c.Connection.CustomMaxTimeout > 0 {
|
||||
builder.WriteString("k")
|
||||
builder.WriteString(c.Connection.CustomMaxTimeout.String())
|
||||
}
|
||||
builder.WriteString("r")
|
||||
builder.WriteString(strconv.FormatInt(int64(c.ResponseHeaderTimeout.Seconds()), 10))
|
||||
hash := builder.String()
|
||||
@@ -247,6 +255,9 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
if configuration.ResponseHeaderTimeout != 0 {
|
||||
responseHeaderTimeout = configuration.ResponseHeaderTimeout
|
||||
}
|
||||
if configuration.Connection != nil && configuration.Connection.CustomMaxTimeout > 0 {
|
||||
responseHeaderTimeout = configuration.Connection.CustomMaxTimeout
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
ForceAttemptHTTP2: options.ForceAttemptHTTP2,
|
||||
@@ -313,6 +324,9 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
}
|
||||
if !configuration.NoTimeout {
|
||||
httpclient.Timeout = options.GetTimeouts().HttpTimeout
|
||||
if configuration.Connection != nil && configuration.Connection.CustomMaxTimeout > 0 {
|
||||
httpclient.Timeout = configuration.Connection.CustomMaxTimeout
|
||||
}
|
||||
}
|
||||
client := retryablehttp.NewWithHTTPClient(httpclient, retryableHttpOptions)
|
||||
if jar != nil {
|
||||
|
||||
@@ -166,6 +166,14 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
if types.ToString(wrapped.InternalEvent["path"]) != "" {
|
||||
fields.Path = types.ToString(wrapped.InternalEvent["path"])
|
||||
}
|
||||
var isGlobalMatchers bool
|
||||
if value, ok := wrapped.InternalEvent["global-matchers"]; ok {
|
||||
isGlobalMatchers = value.(bool)
|
||||
}
|
||||
var analyzerDetails string
|
||||
if value, ok := wrapped.InternalEvent["analyzer_details"]; ok {
|
||||
analyzerDetails = value.(string)
|
||||
}
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
@@ -183,11 +191,13 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
Timestamp: time.Now(),
|
||||
MatcherStatus: true,
|
||||
IP: fields.Ip,
|
||||
GlobalMatchers: isGlobalMatchers,
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: request.truncateResponse(wrapped.InternalEvent["response"]),
|
||||
CURLCommand: types.ToString(wrapped.InternalEvent["curl-command"]),
|
||||
TemplateEncoded: request.options.EncodeTemplate(),
|
||||
Error: types.ToString(wrapped.InternalEvent["error"]),
|
||||
AnalyzerDetails: analyzerDetails,
|
||||
}
|
||||
return data
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
@@ -927,8 +928,26 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
matchedURL = responseURL
|
||||
}
|
||||
}
|
||||
|
||||
finalEvent := make(output.InternalEvent)
|
||||
|
||||
if request.Analyzer != nil {
|
||||
analyzer := analyzers.GetAnalyzer(request.Analyzer.Name)
|
||||
analysisMatched, analysisDetails, err := analyzer.Analyze(&analyzers.Options{
|
||||
FuzzGenerated: generatedRequest.fuzzGeneratedRequest,
|
||||
HttpClient: request.httpClient,
|
||||
ResponseTimeDelay: duration,
|
||||
AnalyzerParameters: request.Analyzer.Parameters,
|
||||
})
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("Could not analyze response: %v\n", err)
|
||||
}
|
||||
if analysisMatched {
|
||||
finalEvent["analyzer_details"] = analysisDetails
|
||||
finalEvent["analyzer"] = true
|
||||
}
|
||||
}
|
||||
|
||||
outputEvent := request.responseToDSLMap(respChain.Response(), input.MetaInput.Input, matchedURL, convUtil.String(dumpedRequest), respChain.FullResponse().String(), respChain.Body().String(), respChain.Headers().String(), duration, generatedRequest.meta)
|
||||
// add response fields to template context and merge templatectx variables to output event
|
||||
request.options.AddTemplateVars(input.MetaInput, request.Type(), request.ID, outputEvent)
|
||||
@@ -973,13 +992,22 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
// prune signature internal values if any
|
||||
request.pruneSignatureInternalValues(generatedRequest.meta)
|
||||
|
||||
event := eventcreator.CreateEventWithAdditionalOptions(request, generators.MergeMaps(generatedRequest.dynamicValues, finalEvent), request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
interimEvent := generators.MergeMaps(generatedRequest.dynamicValues, finalEvent)
|
||||
isDebug := request.options.Options.Debug || request.options.Options.DebugResponse
|
||||
event := eventcreator.CreateEventWithAdditionalOptions(request, interimEvent, isDebug, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
internalWrappedEvent.OperatorsResult.PayloadValues = generatedRequest.meta
|
||||
})
|
||||
|
||||
if hasInteractMatchers {
|
||||
event.UsesInteractsh = true
|
||||
}
|
||||
|
||||
if request.options.GlobalMatchers.HasMatchers() {
|
||||
request.options.GlobalMatchers.Match(interimEvent, request.Match, request.Extract, isDebug, func(event output.InternalEvent, result *operators.Result) {
|
||||
callback(eventcreator.CreateEventWithOperatorResults(request, event, result))
|
||||
})
|
||||
}
|
||||
|
||||
// if requrlpattern is enabled, only then it is reflected in result event else it is empty string
|
||||
// consult @Ice3man543 before changing this logic (context: vuln_hash)
|
||||
if request.options.ExportReqURLPattern {
|
||||
|
||||
@@ -6,6 +6,7 @@ package http
|
||||
// -> request.executeGeneratedFuzzingRequest [execute final generated fuzzing request and get result]
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -13,6 +14,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/analyzers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators/matchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
@@ -121,7 +123,7 @@ func (request *Request) executeAllFuzzingRules(input *contextargs.Context, value
|
||||
default:
|
||||
}
|
||||
|
||||
err := rule.Execute(&fuzz.ExecuteRuleInput{
|
||||
input := &fuzz.ExecuteRuleInput{
|
||||
Input: input,
|
||||
DisplayFuzzPoints: request.options.Options.DisplayFuzzPoints,
|
||||
Callback: func(gr fuzz.GeneratedRequest) bool {
|
||||
@@ -135,8 +137,14 @@ func (request *Request) executeAllFuzzingRules(input *contextargs.Context, value
|
||||
return request.executeGeneratedFuzzingRequest(gr, input, callback)
|
||||
},
|
||||
Values: values,
|
||||
BaseRequest: baseRequest.Clone(input.Context()),
|
||||
})
|
||||
BaseRequest: baseRequest.Clone(context.TODO()),
|
||||
}
|
||||
if request.Analyzer != nil {
|
||||
analyzer := analyzers.GetAnalyzer(request.Analyzer.Name)
|
||||
input.ApplyPayloadInitialTransformation = analyzer.ApplyInitialTransformation
|
||||
input.AnalyzerParams = request.Analyzer.Parameters
|
||||
}
|
||||
err := rule.Execute(input)
|
||||
if err == nil {
|
||||
applicable = true
|
||||
continue
|
||||
@@ -166,10 +174,11 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
}
|
||||
request.options.RateLimitTake()
|
||||
req := &generatedRequest{
|
||||
request: gr.Request,
|
||||
dynamicValues: gr.DynamicValues,
|
||||
interactshURLs: gr.InteractURLs,
|
||||
original: request,
|
||||
request: gr.Request,
|
||||
dynamicValues: gr.DynamicValues,
|
||||
interactshURLs: gr.InteractURLs,
|
||||
original: request,
|
||||
fuzzGeneratedRequest: gr,
|
||||
}
|
||||
var gotMatches bool
|
||||
requestErr := request.executeRequest(input, req, gr.DynamicValues, hasInteractMatchers, func(event *output.InternalWrappedEvent) {
|
||||
|
||||
@@ -319,7 +319,7 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV
|
||||
templateCtx.Merge(payloadValues)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Javascript Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("JavaScript Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
if request.PreCondition != "" {
|
||||
|
||||
@@ -85,6 +85,10 @@ type Request struct {
|
||||
// SelfContained specifies if the request is self-contained.
|
||||
SelfContained bool `yaml:"-" json:"-"`
|
||||
|
||||
// description: |
|
||||
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
||||
|
||||
// description: |
|
||||
// ports is post processed list of ports to scan (obtained from Port)
|
||||
ports []string `yaml:"-" json:"-"`
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -99,6 +100,16 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
gologger.Verbose().Msgf("[%v] got errors while checking open ports: %s\n", request.options.TemplateID, err)
|
||||
}
|
||||
|
||||
// stop at first match if requested
|
||||
atomicBool := &atomic.Bool{}
|
||||
shouldStopAtFirstMatch := request.StopAtFirstMatch || request.options.StopAtFirstMatch || request.options.Options.StopAtFirstMatch
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
if event != nil && event.HasOperatorResult() {
|
||||
atomicBool.Store(true)
|
||||
}
|
||||
callback(event)
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
input := target.Clone()
|
||||
// use network port updates input with new port requested in template file
|
||||
@@ -107,9 +118,12 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
if err := input.UseNetworkPort(port, request.ExcludePorts); err != nil {
|
||||
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
|
||||
}
|
||||
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, callback); err != nil {
|
||||
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, wrappedCallback); err != nil {
|
||||
return err
|
||||
}
|
||||
if shouldStopAtFirstMatch && atomicBool.Load() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -141,6 +155,16 @@ func (request *Request) executeOnTarget(input *contextargs.Context, visited maps
|
||||
variablesMap := request.options.Variables.Evaluate(variables)
|
||||
variables = generators.MergeMaps(variablesMap, variables, request.options.Constants)
|
||||
|
||||
// stop at first match if requested
|
||||
atomicBool := &atomic.Bool{}
|
||||
shouldStopAtFirstMatch := request.StopAtFirstMatch || request.options.StopAtFirstMatch || request.options.Options.StopAtFirstMatch
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
if event != nil && event.HasOperatorResult() {
|
||||
atomicBool.Store(true)
|
||||
}
|
||||
callback(event)
|
||||
}
|
||||
|
||||
for _, kv := range request.addresses {
|
||||
select {
|
||||
case <-input.Context().Done():
|
||||
@@ -154,12 +178,13 @@ func (request *Request) executeOnTarget(input *contextargs.Context, visited maps
|
||||
continue
|
||||
}
|
||||
visited.Set(actualAddress, struct{}{})
|
||||
|
||||
if err = request.executeAddress(variables, actualAddress, address, input, kv.tls, previous, callback); err != nil {
|
||||
if err = request.executeAddress(variables, actualAddress, address, input, kv.tls, previous, wrappedCallback); err != nil {
|
||||
outputEvent := request.responseToDSLMap("", "", "", address, "")
|
||||
callback(&output.InternalWrappedEvent{InternalEvent: outputEvent})
|
||||
gologger.Warning().Msgf("[%v] Could not make network request for (%s) : %s\n", request.options.TemplateID, actualAddress, err)
|
||||
continue
|
||||
}
|
||||
if shouldStopAtFirstMatch && atomicBool.Load() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -283,36 +308,37 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
interimValues := generators.MergeMaps(variables, payloads)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Network Protocol request variables: \n%s\n", vardump.DumpVariables(interimValues))
|
||||
gologger.Debug().Msgf("Network Protocol request variables: %s\n", vardump.DumpVariables(interimValues))
|
||||
}
|
||||
|
||||
inputEvents := make(map[string]interface{})
|
||||
|
||||
for _, input := range request.Inputs {
|
||||
data := []byte(input.Data)
|
||||
dataInBytes := []byte(input.Data)
|
||||
var err error
|
||||
|
||||
if request.options.Interactsh != nil {
|
||||
var transformedData string
|
||||
transformedData, interactshURLs = request.options.Interactsh.Replace(string(data), []string{})
|
||||
data = []byte(transformedData)
|
||||
}
|
||||
|
||||
finalData, err := expressions.EvaluateByte(data, interimValues)
|
||||
dataInBytes, err = expressions.EvaluateByte(dataInBytes, interimValues)
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not evaluate template expressions")
|
||||
}
|
||||
|
||||
reqBuilder.Write(finalData)
|
||||
data := string(dataInBytes)
|
||||
if request.options.Interactsh != nil {
|
||||
data, interactshURLs = request.options.Interactsh.Replace(data, []string{})
|
||||
dataInBytes = []byte(data)
|
||||
}
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(string(finalData)); err != nil {
|
||||
reqBuilder.Write(dataInBytes)
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(data); err != nil {
|
||||
gologger.Warning().Msgf("[%s] Could not make network request for %s: %v\n", request.options.TemplateID, actualAddress, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if input.Type.GetType() == hexType {
|
||||
finalData, err = hex.DecodeString(string(finalData))
|
||||
dataInBytes, err = hex.DecodeString(data)
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
@@ -320,7 +346,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := conn.Write(finalData); err != nil {
|
||||
if _, err := conn.Write(dataInBytes); err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not write request to server")
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/projectfile"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"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/utils/excludematchers"
|
||||
@@ -126,6 +127,8 @@ type ExecutorOptions struct {
|
||||
// ExportReqURLPattern exports the request URL pattern
|
||||
// in ResultEvent it contains the exact url pattern (ex: {{BaseURL}}/{{randstr}}/xyz) used in the request
|
||||
ExportReqURLPattern bool
|
||||
// GlobalMatchers is the storage for global matchers with http passive templates
|
||||
GlobalMatchers *globalmatchers.Storage
|
||||
}
|
||||
|
||||
// todo: centralizing components is not feasible with current clogged architecture
|
||||
|
||||
@@ -222,7 +222,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
payloadValues = generators.MergeMaps(variablesMap, payloadValues, request.options.Constants)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("SSL Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("SSL Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
finalAddress, dataErr := expressions.EvaluateByte([]byte(request.Address), payloadValues)
|
||||
|
||||
@@ -207,7 +207,7 @@ func (request *Request) executeRequestWithPayloads(target *contextargs.Context,
|
||||
}
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Websocket Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("WebSocket Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
finalAddress, dataErr := expressions.EvaluateByte([]byte(request.Address), payloadValues)
|
||||
|
||||
@@ -99,7 +99,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
variables := generators.MergeMaps(vars, defaultVars, optionVars, dynamicValues, request.options.Constants)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Whois Protocol request variables: \n%s\n", vardump.DumpVariables(variables))
|
||||
gologger.Debug().Msgf("Whois Protocol request variables: %s\n", vardump.DumpVariables(variables))
|
||||
}
|
||||
|
||||
// and replace placeholders
|
||||
|
||||
@@ -10,16 +10,21 @@ import (
|
||||
)
|
||||
|
||||
type Exporter struct {
|
||||
options *Options
|
||||
mutex *sync.Mutex
|
||||
rows []output.ResultEvent
|
||||
options *Options
|
||||
mutex *sync.Mutex
|
||||
rows []output.ResultEvent
|
||||
outputFile *os.File
|
||||
}
|
||||
|
||||
// Options contains the configuration options for JSONL exporter client
|
||||
type Options struct {
|
||||
// File is the file to export found JSONL result to
|
||||
File string `yaml:"file"`
|
||||
OmitRaw bool `yaml:"omit-raw"`
|
||||
File string `yaml:"file"`
|
||||
// OmitRaw whether to exclude the raw request and response from the output
|
||||
OmitRaw bool `yaml:"omit-raw"`
|
||||
// BatchSize the number of records to keep in memory before writing them out to the JSONL file or 0 to disable
|
||||
// batching (default)
|
||||
BatchSize int `yaml:"batch-size"`
|
||||
}
|
||||
|
||||
// New creates a new JSONL exporter integration client based on options.
|
||||
@@ -32,8 +37,7 @@ func New(options *Options) (*Exporter, error) {
|
||||
return exporter, nil
|
||||
}
|
||||
|
||||
// Export appends the passed result event to the list of objects to be exported to
|
||||
// the resulting JSONL file
|
||||
// Export appends the passed result event to the list of objects to be exported to the resulting JSONL file
|
||||
func (exporter *Exporter) Export(event *output.ResultEvent) error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
@@ -46,40 +50,72 @@ func (exporter *Exporter) Export(event *output.ResultEvent) error {
|
||||
// Add the event to the rows
|
||||
exporter.rows = append(exporter.rows, *event)
|
||||
|
||||
// If the batch size is greater than 0 and the number of rows has reached the batch, flush it to the database
|
||||
if exporter.options.BatchSize > 0 && len(exporter.rows) >= exporter.options.BatchSize {
|
||||
err := exporter.WriteRows()
|
||||
if err != nil {
|
||||
// The error is already logged, return it to bubble up to the caller
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close writes the in-memory data to the JSONL file specified by options.JSONLExport
|
||||
// and closes the exporter after operation
|
||||
func (exporter *Exporter) Close() error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
// Open the JSONL file for writing and create it if it doesn't exist
|
||||
f, err := os.OpenFile(exporter.options.File, os.O_WRONLY|os.O_CREATE, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create JSONL file")
|
||||
// WriteRows writes all rows from the rows list to JSONL file and removes them from the list
|
||||
func (exporter *Exporter) WriteRows() error {
|
||||
// Open the file for writing if it's not already.
|
||||
// This will recreate the file if it exists, but keep the file handle so that batched writes within the same
|
||||
// execution are appended to the same file.
|
||||
var err error
|
||||
if exporter.outputFile == nil {
|
||||
// Open the JSONL file for writing and create it if it doesn't exist
|
||||
exporter.outputFile, err = os.OpenFile(exporter.options.File, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to create JSONL file")
|
||||
}
|
||||
}
|
||||
|
||||
// Loop through the rows and convert each to a JSON byte array and write to file
|
||||
for _, row := range exporter.rows {
|
||||
// Loop through the rows and write them, removing them as they're entered
|
||||
for len(exporter.rows) > 0 {
|
||||
row := exporter.rows[0]
|
||||
|
||||
// Convert the row to JSON byte array and append a trailing newline. This is treated as a single line in JSONL
|
||||
obj, err := json.Marshal(row)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to generate row for JSONL report")
|
||||
}
|
||||
|
||||
// Add a trailing newline to the JSON byte array to confirm with the JSONL format
|
||||
obj = append(obj, '\n')
|
||||
|
||||
// Attempt to append the JSON line to file specified in options.JSONLExport
|
||||
if _, err = f.Write(obj); err != nil {
|
||||
if _, err = exporter.outputFile.Write(obj); err != nil {
|
||||
return errors.Wrap(err, "failed to append JSONL line")
|
||||
}
|
||||
|
||||
// Remove the item from the list
|
||||
exporter.rows = exporter.rows[1:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close writes the in-memory data to the JSONL file specified by options.JSONLExport and closes the exporter after
|
||||
// operation
|
||||
func (exporter *Exporter) Close() error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
// Write any remaining rows to the file
|
||||
// Write all pending rows
|
||||
err := exporter.WriteRows()
|
||||
if err != nil {
|
||||
// The error is already logged, return it to bubble up to the caller
|
||||
return err
|
||||
}
|
||||
|
||||
// Close the file
|
||||
if err := f.Close(); err != nil {
|
||||
if err := exporter.outputFile.Close(); err != nil {
|
||||
return errors.Wrap(err, "failed to close JSONL file")
|
||||
}
|
||||
|
||||
|
||||
@@ -83,7 +83,7 @@ func CreateReportDescription(event *output.ResultEvent, formatter ResultFormatte
|
||||
}
|
||||
}
|
||||
|
||||
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 {
|
||||
if len(event.ExtractedResults) > 0 || len(event.Metadata) > 0 || event.AnalyzerDetails != "" {
|
||||
builder.WriteString("\n")
|
||||
builder.WriteString(formatter.MakeBold("Extra Information"))
|
||||
builder.WriteString("\n\n")
|
||||
@@ -99,6 +99,13 @@ func CreateReportDescription(event *output.ResultEvent, formatter ResultFormatte
|
||||
}
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
if event.AnalyzerDetails != "" {
|
||||
builder.WriteString(formatter.MakeBold("Analyzer Details:"))
|
||||
builder.WriteString("\n\n")
|
||||
|
||||
builder.WriteString(event.AnalyzerDetails)
|
||||
builder.WriteString("\n")
|
||||
}
|
||||
if len(event.Metadata) > 0 {
|
||||
builder.WriteString(formatter.MakeBold("Metadata:"))
|
||||
builder.WriteString("\n\n")
|
||||
|
||||
@@ -197,7 +197,7 @@ func New(options *Options, db string, doNotDedupe bool) (Client, error) {
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// CreateConfigIfNotExists creates report-config if it doesn't exists
|
||||
// CreateConfigIfNotExists creates report-config if it doesn't exist
|
||||
func CreateConfigIfNotExists() error {
|
||||
reportingConfig := config.DefaultConfig.GetReportingConfigFilePath()
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"sync"
|
||||
|
||||
"github.com/andygrunwald/go-jira"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/trivago/tgo/tcontainer"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -241,18 +242,22 @@ func (i *Integration) CreateIssue(event *output.ResultEvent) (*filters.CreateIss
|
||||
if i.options.UpdateExisting {
|
||||
issue, err := i.FindExistingIssue(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "could not find existing issue")
|
||||
} else if issue.ID != "" {
|
||||
_, _, err = i.jira.Issue.AddComment(issue.ID, &jira.Comment{
|
||||
Body: format.CreateReportDescription(event, i, i.options.OmitRaw),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.Wrap(err, "could not add comment to existing issue")
|
||||
}
|
||||
return getIssueResponseFromJira(&issue)
|
||||
}
|
||||
}
|
||||
return i.CreateNewIssue(event)
|
||||
resp, err := i.CreateNewIssue(event)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create new issue")
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (i *Integration) CloseIssue(event *output.ResultEvent) error {
|
||||
@@ -297,7 +302,11 @@ func (i *Integration) CloseIssue(event *output.ResultEvent) error {
|
||||
// FindExistingIssue checks if the issue already exists and returns its ID
|
||||
func (i *Integration) FindExistingIssue(event *output.ResultEvent) (jira.Issue, error) {
|
||||
template := format.GetMatchedTemplateName(event)
|
||||
jql := fmt.Sprintf("summary ~ \"%s\" AND summary ~ \"%s\" AND status != \"%s\" AND project = \"%s\"", template, event.Host, i.options.StatusNot, i.options.ProjectName)
|
||||
project := i.options.ProjectName
|
||||
if i.options.ProjectID != "" {
|
||||
project = i.options.ProjectID
|
||||
}
|
||||
jql := fmt.Sprintf("summary ~ \"%s\" AND summary ~ \"%s\" AND status != \"%s\" AND project = \"%s\"", template, event.Host, i.options.StatusNot, project)
|
||||
|
||||
searchOptions := &jira.SearchOptions{
|
||||
MaxResults: 1, // if any issue exists, then we won't create a new one
|
||||
|
||||
@@ -9,3 +9,6 @@ func AddScanEvent(event ScanEvent) {
|
||||
|
||||
func InitWithConfig(config *ScanConfig, statsDirectory string) {
|
||||
}
|
||||
|
||||
func Close() {
|
||||
}
|
||||
@@ -23,6 +23,7 @@ type ScanStatsWorker struct {
|
||||
config *ScanConfig
|
||||
m *sync.Mutex
|
||||
directory string
|
||||
file *os.File
|
||||
enc *json.Encoder
|
||||
}
|
||||
|
||||
@@ -56,7 +57,7 @@ func (s *ScanStatsWorker) initEventsFile() error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
s.file = f
|
||||
s.enc = json.NewEncoder(f)
|
||||
return nil
|
||||
}
|
||||
@@ -79,3 +80,22 @@ func AddScanEvent(event ScanEvent) {
|
||||
}
|
||||
defaultWorker.AddScanEvent(event)
|
||||
}
|
||||
|
||||
// Close closes the file associated with the worker
|
||||
func (s *ScanStatsWorker) Close() {
|
||||
s.m.Lock()
|
||||
defer s.m.Unlock()
|
||||
|
||||
if s.file != nil {
|
||||
_ = s.file.Close()
|
||||
s.file = nil
|
||||
}
|
||||
}
|
||||
|
||||
// Close closes the file associated with the worker
|
||||
func Close() {
|
||||
if defaultWorker == nil {
|
||||
return
|
||||
}
|
||||
defaultWorker.Close()
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/offlinehttp"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec"
|
||||
@@ -81,6 +82,18 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Executo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if template.isGlobalMatchersEnabled() {
|
||||
item := &globalmatchers.Item{
|
||||
TemplateID: template.ID,
|
||||
TemplatePath: filePath,
|
||||
TemplateInfo: template.Info,
|
||||
}
|
||||
for _, request := range template.RequestsHTTP {
|
||||
item.Operators = append(item.Operators, request.CompiledOperators)
|
||||
}
|
||||
options.GlobalMatchers.AddOperator(item)
|
||||
return nil, nil
|
||||
}
|
||||
// Compile the workflow request
|
||||
if len(template.Workflows) > 0 {
|
||||
compiled := &template.Workflow
|
||||
@@ -96,6 +109,30 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Executo
|
||||
return template, nil
|
||||
}
|
||||
|
||||
// isGlobalMatchersEnabled checks if any of requests in the template
|
||||
// have global matchers enabled. It iterates through all requests and
|
||||
// returns true if at least one request has global matchers enabled;
|
||||
// otherwise, it returns false. If global matchers templates are not
|
||||
// enabled in the options, the method will immediately return false.
|
||||
//
|
||||
// Note: This method only checks the `RequestsHTTP`
|
||||
// field of the template, which is specific to http-protocol-based
|
||||
// templates.
|
||||
//
|
||||
// TODO: support all protocols.
|
||||
func (template *Template) isGlobalMatchersEnabled() bool {
|
||||
if !template.Options.Options.EnableGlobalMatchersTemplates {
|
||||
return false
|
||||
}
|
||||
|
||||
for _, request := range template.RequestsHTTP {
|
||||
if request.GlobalMatchers {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseSelfContainedRequests parses the self contained template requests.
|
||||
func (template *Template) parseSelfContainedRequests() {
|
||||
if template.Signature.Value.String() != "" {
|
||||
@@ -414,6 +451,9 @@ func parseTemplate(data []byte, options protocols.ExecutorOptions) (*Template, e
|
||||
var verifier *signer.TemplateSigner
|
||||
for _, verifier = range signer.DefaultTemplateVerifiers {
|
||||
template.Verified, _ = verifier.Verify(data, template)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Verbose().Msgf("template %v verified by %s : %v", template.ID, verifier.Identifier(), template.Verified)
|
||||
}
|
||||
if template.Verified {
|
||||
template.TemplateVerifier = verifier.Identifier()
|
||||
break
|
||||
|
||||
@@ -10,5 +10,7 @@ const (
|
||||
ExcludedCodeTmplStats = "code-flag-missing-warnings"
|
||||
ExludedDastTmplStats = "fuzz-flag-missing-warnings"
|
||||
SkippedUnsignedStats = "skipped-unsigned-stats" // tracks loading of unsigned templates
|
||||
ExcludedSelfContainedStats = "excluded-self-contained-stats"
|
||||
ExcludedFileStats = "excluded-file-stats"
|
||||
SkippedRequestSignatureStats = "skipped-request-signature-stats"
|
||||
)
|
||||
Loaded 100 of 122 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user