mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 02:08:33 -10:00
Compare commits
2
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
49974403f7 | ||
|
|
7f0026b090 |
No files matched your search
@@ -1,76 +0,0 @@
|
||||
# Nuclei Discussion Guidelines
|
||||
|
||||
## Before Creating a Discussion
|
||||
|
||||
1. **Search existing discussions and issues** to avoid duplicates
|
||||
2. **Check the documentation** and README first
|
||||
3. **Browse the FAQ** and common questions
|
||||
|
||||
## Bug Reports in Discussions
|
||||
|
||||
When reporting a bug in [Q&A Discussions](https://github.com/projectdiscovery/nuclei/discussions/categories/q-a), please include:
|
||||
|
||||
### Required Information:
|
||||
- **Clear title** with `[BUG]` prefix (e.g., "[BUG] Nuclei crashes when...")
|
||||
- **Current behavior** - What's happening now?
|
||||
- **Expected behavior** - What should happen instead?
|
||||
- **Steps to reproduce** - Commands or actions that trigger the issue
|
||||
- **Environment details**:
|
||||
- OS and version
|
||||
- Nuclei version (`nuclei -version`)
|
||||
- Go version (if installed via `go install`)
|
||||
- **Log output** - Run with `-verbose` or `-debug` for detailed logs
|
||||
- **Redact sensitive information** - Remove target URLs, credentials, etc.
|
||||
|
||||
### After Discussion:
|
||||
- Maintainers will review and validate the bug report
|
||||
- Valid bugs will be converted to issues with proper labels and tracking
|
||||
- Questions and misconfigurations will be resolved in the discussion
|
||||
|
||||
## Feature Requests in Discussions
|
||||
|
||||
When requesting a feature in [Ideas Discussions](https://github.com/projectdiscovery/nuclei/discussions/categories/ideas), please include:
|
||||
|
||||
### Required Information:
|
||||
- **Clear title** with `[FEATURE]` prefix (e.g., "[FEATURE] Add support for...")
|
||||
- **Feature description** - What do you want to be added?
|
||||
- **Use case** - Why is this feature needed? What problem does it solve?
|
||||
- **Implementation ideas** - If you have suggestions on how it could work
|
||||
- **Alternatives considered** - What other solutions have you thought about?
|
||||
|
||||
### After Discussion:
|
||||
- Community and maintainers will discuss the feasibility
|
||||
- Popular and viable features will be converted to issues
|
||||
- Similar features may be grouped together
|
||||
- Rejected features will be explained in the discussion
|
||||
|
||||
## Getting Help
|
||||
|
||||
For general questions, troubleshooting, and "how-to" topics:
|
||||
- Use [Q&A Discussions](https://github.com/projectdiscovery/nuclei/discussions/categories/q-a)
|
||||
- Join the [Discord server](https://discord.gg/projectdiscovery) #nuclei channel
|
||||
- Check existing discussions for similar questions
|
||||
|
||||
## Discussion to Issue Conversion Process
|
||||
|
||||
Only maintainers can convert discussions to issues. The process:
|
||||
|
||||
1. **Validation** - Maintainers review the discussion for completeness and validity
|
||||
2. **Classification** - Determine if it's a bug, feature, enhancement, etc.
|
||||
3. **Issue creation** - Create a properly formatted issue with appropriate labels
|
||||
4. **Linking** - Link the issue back to the original discussion
|
||||
5. **Resolution** - Mark the discussion as resolved or close it
|
||||
|
||||
This process ensures:
|
||||
- High-quality issues that are actionable
|
||||
- Proper triage and labeling
|
||||
- Reduced noise in the issue tracker
|
||||
- Community involvement in the validation process
|
||||
|
||||
## Why This Process?
|
||||
|
||||
- **Better organization** - Issues contain only validated, actionable items
|
||||
- **Community input** - Discussions allow for community feedback before escalation
|
||||
- **Quality control** - Maintainers ensure proper formatting and information
|
||||
- **Reduced maintenance** - Fewer invalid or duplicate issues to manage
|
||||
- **Clear separation** - Questions vs. actual bugs/features are clearly distinguished
|
||||
@@ -36,10 +36,7 @@ body:
|
||||
description: |
|
||||
Steps to reproduce the behavior, for example, commands to run Nuclei.
|
||||
|
||||
💡 Prefer copying text output over using screenshots for easier troubleshooting.
|
||||
|
||||
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
|
||||
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
|
||||
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
|
||||
|
||||
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
|
||||
placeholder: |
|
||||
@@ -52,10 +49,7 @@ body:
|
||||
description: |
|
||||
Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
|
||||
|
||||
💡 Prefer copying text output over using screenshots for easier troubleshooting.
|
||||
|
||||
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
|
||||
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
|
||||
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
|
||||
|
||||
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
|
||||
render: shell
|
||||
@@ -64,9 +58,9 @@ body:
|
||||
label: Environment
|
||||
description: |
|
||||
Examples:
|
||||
- **OS**: Ubuntu 24.04
|
||||
- **Nuclei** (`nuclei -version`): v3.6.0
|
||||
- **Go** (`go version`): go1.24.0 _(only if you've installed it via the `go install` command)_
|
||||
- **OS**: Ubuntu 20.04
|
||||
- **Nuclei** (`nuclei -version`): v3.3.1
|
||||
- **Go** (`go version`): go1.22.0 _(only if you've installed it via the `go install` command)_
|
||||
value: |
|
||||
- OS:
|
||||
- Nuclei:
|
||||
@@ -78,7 +72,7 @@ body:
|
||||
attributes:
|
||||
label: Anything else?
|
||||
description: |
|
||||
Links? References? Templates? CPU, memory, and trace profiles? Anything that will give us more context about the issue you are encountering!
|
||||
Links? References? Templates? Anything that will give us more context about the issue you are encountering!
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
|
||||
@@ -2,18 +2,14 @@ blank_issues_enabled: false
|
||||
|
||||
contact_links:
|
||||
|
||||
- name: 💡 Request a Feature (Start with Discussion)
|
||||
url: https://github.com/orgs/projectdiscovery/discussions/new?category=ideas
|
||||
about: Share your feature idea in discussions first. This helps validate and refine the request before creating an issue.
|
||||
- name: Ask an question / advise on using nuclei
|
||||
url: https://github.com/projectdiscovery/nuclei/discussions/categories/q-a
|
||||
about: Ask a question or request support for using nuclei
|
||||
|
||||
- name: ❓ Ask Questions / Get Help
|
||||
url: https://github.com/orgs/projectdiscovery/discussions
|
||||
about: Get help and ask questions about using Nuclei. Many questions don't require issues.
|
||||
- name: Share idea / feature to discuss for nuclei
|
||||
url: https://github.com/projectdiscovery/nuclei/discussions/categories/ideas
|
||||
about: Share idea / feature to discuss for nuclei
|
||||
|
||||
- name: 🔍 Browse Existing Issues
|
||||
url: https://github.com/projectdiscovery/nuclei/issues
|
||||
about: Check existing issues to see if your problem has already been reported or is being worked on.
|
||||
|
||||
- name: 💬 Connect with PD Team (Discord)
|
||||
- name: Connect with PD Team (Discord)
|
||||
url: https://discord.gg/projectdiscovery
|
||||
about: Join our Discord for real-time discussions and community support on the #nuclei channel.
|
||||
about: Connect with PD Team for direct communication
|
||||
File renamed without changes.
@@ -1,45 +0,0 @@
|
||||
# Issue Template References
|
||||
|
||||
## Overview
|
||||
|
||||
This folder contains the preserved issue templates that are **not** directly accessible to users. These templates serve as references for maintainers when converting discussions to issues.
|
||||
|
||||
## New Workflow
|
||||
|
||||
### For Users:
|
||||
1. **All reports start in Discussions** - Users cannot create issues directly
|
||||
2. Bug reports go to [Q&A Discussions](https://github.com/projectdiscovery/nuclei/discussions/categories/q-a)
|
||||
3. Feature requests go to [Ideas Discussions](https://github.com/projectdiscovery/nuclei/discussions/categories/ideas)
|
||||
4. This helps filter out duplicate questions, invalid reports, and ensures proper triage
|
||||
|
||||
### For Maintainers:
|
||||
1. **Review discussions** in both Q&A and Ideas categories
|
||||
2. **Validate the reports** - ensure they're actual bugs/valid feature requests
|
||||
3. **Use reference templates** when converting discussions to issues:
|
||||
- Copy content from `bug-report-reference.yml` or `feature-request-reference.yml`
|
||||
- Create a new issue manually with the appropriate template structure
|
||||
- Link back to the original discussion
|
||||
- Close the discussion or mark it as resolved
|
||||
|
||||
## Benefits
|
||||
|
||||
- **Better triage**: Avoid cluttering issues with questions and invalid reports
|
||||
- **Community involvement**: Discussions allow for community input before creating issues
|
||||
- **Quality control**: Maintainers can ensure issues follow proper format and contain necessary information
|
||||
- **Reduced noise**: Only validated, actionable items become issues
|
||||
|
||||
## Reference Templates
|
||||
|
||||
- `bug-report-reference.yml` - Use when converting bug reports from discussions to issues
|
||||
- `feature-request-reference.yml` - Use when converting feature requests from discussions to issues
|
||||
|
||||
## Converting a Discussion to Issue
|
||||
|
||||
1. Identify a valid discussion that needs to become an issue
|
||||
2. Go to the main repository's Issues tab
|
||||
3. Click "New Issue"
|
||||
4. Manually create the issue using the reference template structure
|
||||
5. Include all relevant information from the discussion
|
||||
6. Add a comment linking back to the original discussion
|
||||
7. Apply appropriate labels
|
||||
8. Close or mark the discussion as resolved with a link to the created issue
|
||||
@@ -2,9 +2,6 @@
|
||||
|
||||
<!-- Describe the overall picture of your modifications to help maintainers understand the pull request. PRs are required to be associated to their related issue tickets or feature request. -->
|
||||
|
||||
### Proof
|
||||
|
||||
<!-- How has this been tested? Please describe the tests that you ran to verify your changes. -->
|
||||
|
||||
## Checklist
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ addReviewers: true
|
||||
reviewers:
|
||||
- dogancanbakir
|
||||
- dwisiswant0
|
||||
- mzack9999
|
||||
|
||||
numberOfReviewers: 1
|
||||
skipKeywords:
|
||||
|
||||
@@ -1,24 +1,28 @@
|
||||
name: 🔀 Auto merge PR
|
||||
name: 🤖 Auto Merge
|
||||
|
||||
on:
|
||||
# pull_request:
|
||||
# types: [opened, synchronize, reopened, ready_for_review]
|
||||
# pull_request_review:
|
||||
# types: [submitted]
|
||||
pull_request_review:
|
||||
types: [submitted]
|
||||
workflow_run:
|
||||
workflows: ["♾️ Compatibility Checks"]
|
||||
types: [completed]
|
||||
workflows: ["♾️ Compatibility Check"]
|
||||
types:
|
||||
- completed
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
issues: write
|
||||
repository-projects: write
|
||||
|
||||
jobs:
|
||||
auto-merge-dependabot:
|
||||
auto-merge:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: projectdiscovery/actions/pr/approve@v1
|
||||
- uses: projectdiscovery/actions/pr/merge@v1
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
auto: "true"
|
||||
token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
|
||||
- uses: ahmadnassri/action-dependabot-auto-merge@v2
|
||||
with:
|
||||
github-token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
target: all
|
||||
@@ -7,14 +7,13 @@ on:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
compat-checks:
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
check:
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go/compat-checks@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
release-test: true
|
||||
@@ -1,61 +0,0 @@
|
||||
name: 📊 Flamegraph
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
|
||||
jobs:
|
||||
flamegraph:
|
||||
name: "Flamegraph"
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
PROFILE_MEM: "/tmp/nuclei-profile"
|
||||
TARGET_URL: "http://honey.scanme.sh/-/?foo=bar"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- run: make build
|
||||
|
||||
- name: "Setup environment (push)"
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: |
|
||||
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
|
||||
- name: "Setup environment (pull_request)"
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
|
||||
|
||||
- run: ./bin/nuclei -update-templates
|
||||
- run: |
|
||||
./bin/nuclei -silent -target="${TARGET_URL}" \
|
||||
-disable-update-check \
|
||||
-no-mhe -no-httpx \
|
||||
-code -dast -file -headless \
|
||||
-enable-self-contained -enable-global-matchers \
|
||||
-rate-limit=0 \
|
||||
-concurrency=250 \
|
||||
-headless-concurrency=100 \
|
||||
-payload-concurrency=250 \
|
||||
-bulk-size=250 \
|
||||
-headless-bulk-size=100 \
|
||||
-profile-mem="${PROFILE_MEM}"
|
||||
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-cpu
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.cpu"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
|
||||
continue-on-error: true
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-mem
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.mem"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
|
||||
continue-on-error: true
|
||||
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
|
||||
run: |
|
||||
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
|
||||
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
|
||||
@@ -1,54 +0,0 @@
|
||||
name: 🧪 Fuzz
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 3 * * 0' # Weekly
|
||||
workflow_dispatch: {}
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
discover-packages:
|
||||
name: Discover fuzz packages
|
||||
if: ${{ github.event_name != 'schedule' || github.repository == 'projectdiscovery/nuclei' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
matrix: ${{ steps.discover.outputs.matrix }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version-file: go.tool.mod
|
||||
cache-dependency-path: go.tool.sum
|
||||
- id: discover
|
||||
run: make discover-fuzz-packages
|
||||
|
||||
fuzz:
|
||||
name: Fuzz ${{ matrix.pkg }}
|
||||
needs: discover-packages
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 35
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix: ${{ fromJson(needs.discover-packages.outputs.matrix) }}
|
||||
env:
|
||||
GOFUZZ_PACKAGE: ${{ matrix.pkg }}
|
||||
FUZZ_DURATION: 15m
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version-file: go.tool.mod
|
||||
cache-dependency-path: go*sum # go.sum + go.tool.sum
|
||||
- id: fuzz
|
||||
run: make fuzz-ci GOFUZZ_PACKAGE="${GOFUZZ_PACKAGE}" FUZZ_DURATION="${FUZZ_DURATION}"
|
||||
- if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ steps.fuzz.outputs.artifact_name }}
|
||||
path: ${{ matrix.pkg }}/.gofuzz
|
||||
if-no-files-found: warn
|
||||
overwrite: true
|
||||
retention-days: 14
|
||||
@@ -4,14 +4,14 @@ on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch: {}
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
publish-docs:
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- run: make syntax-docs
|
||||
|
||||
@@ -1,50 +1,55 @@
|
||||
name: 👤 Generate PGO
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
workflow_call: {}
|
||||
push:
|
||||
branches: ["dev"]
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
# TODO(dwisiswant0): https://go.dev/doc/pgo#merging-profiles
|
||||
|
||||
jobs:
|
||||
pgo:
|
||||
strategy:
|
||||
matrix:
|
||||
targets: [150]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
permissions:
|
||||
contents: write
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
PGO_FILE: "cmd/nuclei/default.pgo"
|
||||
TARGET: "https://honey.scanme.sh"
|
||||
TARGET_COUNT: "100"
|
||||
TARGET_LIST: "/tmp/targets.txt"
|
||||
PROFILE_MEM: "/tmp/nuclei-profile"
|
||||
LIST_FILE: "/tmp/targets-${{ matrix.targets }}.txt"
|
||||
PROFILE_MEM: "/tmp/nuclei-profile-${{ matrix.targets }}-targets"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- run: |
|
||||
for i in {1..${{ env.TARGET_COUNT }}}; do
|
||||
echo "${{ env.TARGET }}/-/?_=${i}" >> "${{ env.TARGET_LIST }}";
|
||||
done
|
||||
- run: make build
|
||||
- run: ./bin/nuclei -update-templates
|
||||
- run: |
|
||||
./bin/nuclei -silent -list="${TARGET_LIST}" \
|
||||
-disable-update-check \
|
||||
-no-mhe -no-httpx \
|
||||
-code -dast -file -headless \
|
||||
-enable-self-contained -enable-global-matchers \
|
||||
-rate-limit=0 \
|
||||
-concurrency=250 \
|
||||
-headless-concurrency=100 \
|
||||
-payload-concurrency=250 \
|
||||
-bulk-size=250 \
|
||||
-headless-bulk-size=100 \
|
||||
-profile-mem="${PROFILE_MEM}" \
|
||||
env:
|
||||
DISABLE_STDOUT: "1"
|
||||
- run: mv "${PROFILE_MEM}.cpu" "${PGO_FILE}"
|
||||
- uses: actions/upload-artifact@v7
|
||||
- name: Generate list
|
||||
run: for i in {1..${{ matrix.targets }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
|
||||
# NOTE(dwisiswant0): use `-no-mhe` flag to get better samples.
|
||||
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}" -no-mhe
|
||||
working-directory: cmd/nuclei/
|
||||
- run: mv "${PROFILE_MEM}.cpu" ${PGO_FILE}
|
||||
# NOTE(dwisiswant0): shall we prune $PGO_FILE git history?
|
||||
# if we prune it, this won't be linear since it requires a force-push.
|
||||
# if we don't, the git objects will just keep growing bigger.
|
||||
#
|
||||
# Ref:
|
||||
# - https://go.dev/blog/pgo#:~:text=We%20recommend%20committing%20default.pgo%20files%20to%20your%20repository
|
||||
# - https://gist.github.com/nottrobin/5758221
|
||||
- uses: projectdiscovery/actions/commit@v1
|
||||
with:
|
||||
files: "${PGO_FILE}"
|
||||
message: "build: update PGO profile :robot:"
|
||||
- run: git push origin $GITHUB_REF
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: "pgo"
|
||||
path: "${{ env.PGO_FILE }}"
|
||||
overwrite: true
|
||||
@@ -3,7 +3,7 @@ name: 🐛 govulncheck
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Weekly
|
||||
workflow_dispatch: {}
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
govulncheck:
|
||||
@@ -16,15 +16,11 @@ jobs:
|
||||
env:
|
||||
OUTPUT: "/tmp/results.sarif"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version-file: go.tool.mod
|
||||
cache-dependency-path: go.tool.sum
|
||||
- run: |
|
||||
go tool -modfile=go.tool.mod govulncheck -scan package -format sarif ./... | \
|
||||
jq '(.runs[].tool.driver.rules[]?.properties.tags)? |= unique' > $OUTPUT
|
||||
- uses: github/codeql-action/upload-sarif@v4
|
||||
- run: go install golang.org/x/vuln/cmd/govulncheck@latest
|
||||
- run: govulncheck -scan package -format sarif ./... > $OUTPUT
|
||||
- uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: "${{ env.OUTPUT }}"
|
||||
category: "govulncheck"
|
||||
@@ -1,30 +0,0 @@
|
||||
name: 💾 Memoize Functions
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
paths:
|
||||
- 'pkg/js/libs/**'
|
||||
- 'cmd/memogen/**'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
memogen:
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- run: make memogen
|
||||
- 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: |
|
||||
pkg/js/libs/
|
||||
message: 'chore(js): update memoized functions 🤖'
|
||||
- run: git push origin $GITHUB_REF
|
||||
if: steps.status.outputs.CHANGES > 0
|
||||
@@ -1,8 +1,8 @@
|
||||
name: 🔨 Performance Regression
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
workflow_dispatch: {}
|
||||
workflow_call:
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
perf-regression:
|
||||
@@ -11,14 +11,13 @@ jobs:
|
||||
env:
|
||||
BENCH_OUT: "/tmp/bench.out"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- run: make build-test
|
||||
- run: ./bin/nuclei.test -test.run - -test.bench=. -test.benchmem ./cmd/nuclei/ | tee $BENCH_OUT
|
||||
env:
|
||||
DISABLE_STDOUT: "1"
|
||||
- uses: actions/cache/restore@v5
|
||||
- uses: actions/cache/restore@v4
|
||||
with:
|
||||
path: ./cache
|
||||
key: ${{ runner.os }}-benchmark
|
||||
@@ -32,7 +31,7 @@ jobs:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-on-alert: true
|
||||
summary-always: true
|
||||
- uses: actions/cache/save@v5
|
||||
- uses: actions/cache/save@v4
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
path: ./cache
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
name: 🔨 Performance Test
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Weekly
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
perf-test:
|
||||
strategy:
|
||||
matrix:
|
||||
count: [50, 100, 150]
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
env:
|
||||
LIST_FILE: "/tmp/targets-${{ matrix.count }}.txt"
|
||||
PROFILE_MEM: "/tmp/nuclei-perf-test-${{ matrix.count }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make verify
|
||||
- name: Generate list
|
||||
run: for i in {1..${{ matrix.count }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
|
||||
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}"
|
||||
env:
|
||||
NUCLEI_ARGS: host-error-stats
|
||||
working-directory: cmd/nuclei/
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-cpu
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.cpu"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
|
||||
continue-on-error: true
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-mem
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.mem"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
|
||||
continue-on-error: true
|
||||
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
|
||||
run: |
|
||||
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
|
||||
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
|
||||
@@ -4,29 +4,17 @@ on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
workflow_dispatch: {}
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
pgo:
|
||||
name: "Generate PGO"
|
||||
uses: ./.github/workflows/generate-pgo.yaml
|
||||
|
||||
release:
|
||||
name: "Release"
|
||||
needs: ["pgo"]
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: "pgo"
|
||||
path: "cmd/nuclei/"
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: docker/login-action@v4
|
||||
- uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
- uses: actions/stale@v9
|
||||
with:
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
@@ -37,4 +37,5 @@ jobs:
|
||||
it, please comment or feel free to reopen it.
|
||||
close-issue-label: "Status: Abandoned"
|
||||
close-pr-label: "Status: Abandoned"
|
||||
exempt-issue-labels: "Type: Enhancement"
|
||||
exempt-issue-labels: "Status: Abandoned"
|
||||
exempt-pr-labels: "Status: Abandoned"
|
||||
@@ -19,42 +19,31 @@ concurrency:
|
||||
jobs:
|
||||
lint:
|
||||
name: "Lint"
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/golangci-lint/v2@v1
|
||||
- uses: projectdiscovery/actions/golangci-lint@v1
|
||||
|
||||
tests:
|
||||
name: "Tests"
|
||||
needs: ["lint"]
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- uses: projectdiscovery/actions/free-disk-space@v1
|
||||
with:
|
||||
llvm: 'false'
|
||||
php: 'false'
|
||||
mongodb: 'false'
|
||||
mysql: 'false'
|
||||
misc-packages: 'false'
|
||||
docker-images: 'false'
|
||||
tools-cache: 'false'
|
||||
- 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 ${{ github.workspace }}/internal/tests/functional/testdata/targets.txt -id tech-detect,tls-version
|
||||
- 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/
|
||||
|
||||
@@ -62,72 +51,61 @@ jobs:
|
||||
name: "Run example SDK"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- name: "Simple"
|
||||
run: go run .
|
||||
working-directory: examples/simple/
|
||||
# - run: go run . # Temporarily disabled very flaky in github actions
|
||||
# working-directory: examples/advanced/
|
||||
|
||||
# TODO: FIX with ExecutionID (ref: https://github.com/projectdiscovery/nuclei/pull/6296)
|
||||
# - name: "with Speed Control"
|
||||
# run: go run .
|
||||
# working-directory: examples/with_speed_control/
|
||||
- name: "with Speed Control"
|
||||
run: go run .
|
||||
working-directory: examples/with_speed_control/
|
||||
|
||||
integration:
|
||||
name: "Integration tests"
|
||||
needs: ["tests"]
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/python@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make integration
|
||||
- uses: projectdiscovery/actions/setup/python@v1
|
||||
- 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"]
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/python@v1
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action@v3
|
||||
with:
|
||||
version: latest
|
||||
install-only: true
|
||||
- run: make functional
|
||||
- uses: projectdiscovery/actions/setup/python@v1
|
||||
- run: bash run.sh
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
working-directory: cmd/functional-test/
|
||||
|
||||
validate:
|
||||
name: "Template validate"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- run: make template-validate
|
||||
|
||||
codeql:
|
||||
@@ -139,30 +117,61 @@ jobs:
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: github/codeql-action/init@v4
|
||||
- uses: actions/checkout@v4
|
||||
- uses: github/codeql-action/init@v3
|
||||
with:
|
||||
languages: 'go'
|
||||
- uses: github/codeql-action/autobuild@v4
|
||||
- uses: github/codeql-action/analyze@v4
|
||||
- uses: github/codeql-action/autobuild@v3
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
|
||||
release:
|
||||
name: "Release test"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: actions/checkout@v4
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
|
||||
flamegraph:
|
||||
name: "Flamegraph"
|
||||
needs: ["tests"]
|
||||
uses: ./.github/workflows/flamegraph.yaml
|
||||
env:
|
||||
PROFILE_MEM: "/tmp/nuclei"
|
||||
TARGET_URL: "http://scanme.sh/a/?b=c"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- run: make build
|
||||
- name: "Setup environment (push)"
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
run: |
|
||||
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
|
||||
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
|
||||
- name: "Setup environment (pull_request)"
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
run: |
|
||||
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
|
||||
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
|
||||
- run: ./bin/nuclei -silent -update-templates
|
||||
- run: ./bin/nuclei -silent -u "${TARGET_URL}" -profile-mem="${PROFILE_MEM}"
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-cpu
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.cpu"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
|
||||
continue-on-error: true
|
||||
- uses: projectdiscovery/actions/flamegraph@v1
|
||||
id: flamegraph-mem
|
||||
with:
|
||||
profile: "${{ env.PROFILE_MEM }}.mem"
|
||||
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
|
||||
continue-on-error: true
|
||||
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
|
||||
run: |
|
||||
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
|
||||
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
|
||||
|
||||
# perf-regression:
|
||||
# name: "Performance regression"
|
||||
# needs: ["tests"]
|
||||
# uses: ./.github/workflows/perf-regression.yaml
|
||||
perf-regression:
|
||||
needs: ["tests"]
|
||||
uses: ./.github/workflows/perf-regression.yaml
|
||||
@@ -1,20 +0,0 @@
|
||||
name: 🔤 Typos
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dev"]
|
||||
pull_request:
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
typos:
|
||||
name: "Spell Check"
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: crate-ci/[email protected]
|
||||
+1
-3
@@ -2,14 +2,13 @@
|
||||
**/*-cache
|
||||
**/*-config
|
||||
**/.cache
|
||||
**/.gofuzz/
|
||||
**/*.DS_Store
|
||||
**/*.exe
|
||||
.devcontainer
|
||||
.gitignore
|
||||
.idea
|
||||
.vscode
|
||||
.claude/settings.local.json
|
||||
|
||||
# Binaries
|
||||
/bin/*
|
||||
/bindgen
|
||||
@@ -29,7 +28,6 @@
|
||||
/scrapefunc
|
||||
/scrapefuncs
|
||||
/tsgen
|
||||
*.test
|
||||
|
||||
# Templates
|
||||
/*.yaml
|
||||
|
||||
+2
-3
@@ -11,9 +11,8 @@ builds:
|
||||
id: nuclei-cli
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOEXPERIMENT=greenteagc
|
||||
goos: [windows, linux, darwin]
|
||||
goarch: [amd64, '386', arm, arm64]
|
||||
goos: [windows,linux,darwin]
|
||||
goarch: [amd64,'386',arm,arm64]
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: '386'
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="DSLFunctionsIT" type="GoApplicationRunConfiguration" factoryName="Go Application">
|
||||
<module name="nuclei" />
|
||||
<working_directory value="$PROJECT_DIR$/integration_tests" />
|
||||
<envs>
|
||||
<env name="DEBUG" value="true" />
|
||||
<env name="TESTS" value="http/dsl-functions.yaml" />
|
||||
</envs>
|
||||
<kind value="PACKAGE" />
|
||||
<package value="github.com/projectdiscovery/nuclei/v3/cmd/integration-test" />
|
||||
<directory value="$PROJECT_DIR$" />
|
||||
<filePath value="$PROJECT_DIR$/cmd/integration-test/integration-test.go" />
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="IntegrationTests" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/integration_tests/run.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/integration_tests/" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||
<envs>
|
||||
<env name="DEBUG" value="true" />
|
||||
</envs>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -0,0 +1,19 @@
|
||||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="RegressionTests" type="ShConfigurationType">
|
||||
<option name="SCRIPT_TEXT" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
|
||||
<option name="SCRIPT_PATH" value="$PROJECT_DIR$/cmd/functional-test/run.sh" />
|
||||
<option name="SCRIPT_OPTIONS" value="" />
|
||||
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
|
||||
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$/cmd/functional-test/" />
|
||||
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
|
||||
<option name="INTERPRETER_PATH" value="/bin/zsh" />
|
||||
<option name="INTERPRETER_OPTIONS" value="" />
|
||||
<option name="EXECUTE_IN_TERMINAL" value="true" />
|
||||
<option name="EXECUTE_SCRIPT_FILE" value="true" />
|
||||
<envs>
|
||||
<env name="DEBUG" value="true" />
|
||||
</envs>
|
||||
<method v="2" />
|
||||
</configuration>
|
||||
</component>
|
||||
@@ -1,84 +0,0 @@
|
||||
# CLAUDE.md
|
||||
|
||||
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
|
||||
|
||||
## Project Overview
|
||||
|
||||
Nuclei is a modern, high-performance vulnerability scanner built in Go that leverages YAML-based templates for customizable vulnerability detection. It supports multiple protocols (HTTP, DNS, TCP, SSL, WebSocket, WHOIS, JavaScript, Code) and is designed for zero false positives through real-world condition simulation.
|
||||
|
||||
## Development Commands
|
||||
|
||||
### Building and Testing
|
||||
- `make build` - Build the main nuclei binary to ./bin/nuclei
|
||||
- `make test` - Run unit tests with race detection
|
||||
- `make integration` - Run the native integration suite via `go test -tags=integration ./internal/tests/integration`
|
||||
- `make functional` - CI-only functional suite entry point
|
||||
- `make vet` - Run go vet for code analysis
|
||||
- `make tidy` - Clean up go modules
|
||||
|
||||
### Validation and Linting
|
||||
- `make template-validate` - Validate nuclei templates using the built binary
|
||||
- `go fmt ./...` - Format Go code
|
||||
- `go vet ./...` - Static analysis
|
||||
|
||||
### Development Tools
|
||||
- `make devtools-all` - Build all development tools (bindgen, tsgen, scrapefuncs)
|
||||
- `make jsupdate-all` - Update JavaScript bindings and TypeScript definitions
|
||||
- `make docs` - Generate documentation
|
||||
- `make memogen` - Generate memoization code for JavaScript libraries
|
||||
|
||||
### Testing Specific Components
|
||||
- Run single test: `go test -v ./pkg/path/to/package -run TestName`
|
||||
- Integration tests are in `internal/tests/integration/` and run via `go test -tags=integration ./internal/tests/integration`
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
### Core Components
|
||||
- **cmd/nuclei** - Main CLI entry point with flag parsing and configuration
|
||||
- **internal/runner** - Core runner that orchestrates the entire scanning process
|
||||
- **pkg/core** - Execution engine with work pools and template clustering
|
||||
- **pkg/templates** - Template parsing, compilation, and management
|
||||
- **pkg/protocols** - Protocol implementations (HTTP, DNS, Network, etc.)
|
||||
- **pkg/operators** - Matching and extraction logic (matchers/extractors)
|
||||
- **pkg/catalog** - Template discovery and loading from disk/remote sources
|
||||
|
||||
### Protocol Architecture
|
||||
Each protocol (HTTP, DNS, Network, etc.) implements:
|
||||
- Request interface with Compile(), ExecuteWithResults(), Match(), Extract() methods
|
||||
- Operators embedding for matching/extraction functionality
|
||||
- Protocol-specific request building and execution logic
|
||||
|
||||
### Template System
|
||||
- Templates are YAML files defining vulnerability detection logic
|
||||
- Compiled into executable requests with operators (matchers/extractors)
|
||||
- Support for workflows (multistep template execution)
|
||||
- Template clustering optimizes identical requests across multiple templates
|
||||
|
||||
### Key Execution Flow
|
||||
1. Template loading and compilation via pkg/catalog/loader
|
||||
2. Input provider setup for targets
|
||||
3. Engine creation with work pools for concurrency
|
||||
4. Template execution with result collection via operators
|
||||
5. Output writing and reporting integration
|
||||
|
||||
### JavaScript Integration
|
||||
- Custom JavaScript runtime for code protocol templates
|
||||
- Auto-generated bindings in pkg/js/generated/
|
||||
- Library implementations in pkg/js/libs/
|
||||
- Development tools for binding generation in pkg/js/devtools/
|
||||
|
||||
## Template Development
|
||||
- Templates located in separate nuclei-templates repository
|
||||
- YAML format with info, requests, and operators sections
|
||||
- Support for multiple protocol types in single template
|
||||
- Built-in DSL functions for dynamic content generation
|
||||
- Template validation available via `make template-validate`
|
||||
|
||||
## Key Directories
|
||||
- **lib/** - SDK for embedding nuclei as a library
|
||||
- **examples/** - Usage examples for different scenarios
|
||||
- **internal/tests/integration/** - Native integration suite and owned testdata for harness coverage
|
||||
- **internal/tests/functional/** - CI-only native functional comparison suite and testcase assets
|
||||
- **pkg/fuzz/** - Fuzzing engine and DAST capabilities
|
||||
- **pkg/input/** - Input processing for various formats (Burp, OpenAPI, etc.)
|
||||
- **pkg/reporting/** - Result export and issue tracking integrations
|
||||
+1
-20
@@ -20,26 +20,7 @@ When creating a PR, please follow these guidelines:
|
||||
|
||||
## Code Style
|
||||
|
||||
Please adhere to the existing coding style for consistency.
|
||||
|
||||
## Development
|
||||
|
||||
To ensure your changes work as expected and strictly adhere to the project's standards, please run the following commands before submitting a PR:
|
||||
|
||||
- **Run tests**:
|
||||
```sh
|
||||
make test
|
||||
```
|
||||
|
||||
- **Run linters/vet**:
|
||||
```sh
|
||||
make vet
|
||||
```
|
||||
|
||||
- **Build the project**:
|
||||
```sh
|
||||
make build
|
||||
```
|
||||
Please adhere to the existing coding style for consistency.
|
||||
|
||||
## Questions
|
||||
|
||||
|
||||
@@ -22,7 +22,7 @@ When this flag is provided, nuclei will print the list of all helper functions a
|
||||
|
||||
- `-svd` flag
|
||||
|
||||
When this flag is provided, nuclei will print all `variables` pre- and post-execution of a request for a template. This is useful to understand what variables are available for a template and what values they have.
|
||||
When this flag is provided, nuclei will print all `variables` pre and post execution of a request for a template. This is useful to understand what variables are available for a template and what values they have.
|
||||
|
||||
- `-elog = errors.txt` flag
|
||||
|
||||
@@ -40,8 +40,6 @@ Nuclei was built with some environment variables in mind to help with debugging.
|
||||
| `SHOW_DSL_ERRORS=true` | Enables Printing DSL Errors (that are hidden by default) |
|
||||
| `HIDE_TEMPLATE_SIG_WARNING=true` | Hides Template Signature Verification Warnings |
|
||||
| `NUCLEI_LOG_ALL=true` | Log All Events that were skipped in verbose mode |
|
||||
| `NUCLEI_CONFIG_DIR` | Sets custom configuration directory path |
|
||||
| `NUCLEI_TEMPLATES_DIR` | Sets custom nuclei templates directory path |
|
||||
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ type Exporter interface {
|
||||
}
|
||||
```
|
||||
|
||||
Exporters include `Elasticsearch`, `markdown`, `sarif` . Trackers include `GitHub`, `GitLab` and `Jira`.
|
||||
Exporters include `Elasticsearch`, `markdown`, `sarif` . Trackers include `GitHub` , `GitLab` and `Jira`.
|
||||
|
||||
Each exporter and trackers implement their own configuration in YAML format and are very modular in nature, so adding new ones is easy.
|
||||
|
||||
@@ -454,7 +454,7 @@ func (template *Template) compileProtocolRequests(options protocols.ExecuterOpti
|
||||
}
|
||||
```
|
||||
|
||||
That's it, you've added a new protocol to Nuclei. The next good step would be to add native integration coverage under `internal/tests/integration` and run it with `go test -tags=integration ./internal/tests/integration`.
|
||||
That's it, you've added a new protocol to Nuclei. The next good step would be to write integration tests which are described in `integration-tests` and `cmd/integration-tests` directories.
|
||||
|
||||
|
||||
## Profiling and Tracing
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# Build
|
||||
FROM golang:1.24-alpine AS builder
|
||||
FROM golang:1.23-alpine AS builder
|
||||
|
||||
RUN apk add build-base
|
||||
WORKDIR /app
|
||||
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
# Fuzzing
|
||||
|
||||
Nuclei uses `go-fuzz` for targeted harnesses behind the `gofuzz` build tag.
|
||||
|
||||
## Targets
|
||||
|
||||
The Makefile exposes these go-fuzz targets:
|
||||
|
||||
```bash
|
||||
make build-fuzz GOFUZZ_PACKAGE=./pkg/operators/matchers
|
||||
make fuzz GOFUZZ_PACKAGE=./pkg/operators/matchers
|
||||
```
|
||||
|
||||
Required variable:
|
||||
|
||||
- `GOFUZZ_PACKAGE`: any package path that `go list` can resolve from the repo root. The normal form in this repo is a relative package path such as `./pkg/operators/matchers`.
|
||||
|
||||
`build-fuzz`, `fuzz`, and `fuzz-ci` derive everything else from that package path:
|
||||
|
||||
- workdir: `<package>/.gofuzz/`
|
||||
- seed corpus: `<package>/testdata/gofuzz-corpus/`
|
||||
- instrumented archive: `<package>/.gofuzz/<pkgname>-fuzz.zip`
|
||||
|
||||
`discover-fuzz-packages` emits the GitHub Actions matrix JSON to stdout, and writes `matrix=...` to `GITHUB_OUTPUT` when that file is present.
|
||||
|
||||
`fuzz-ci` wraps `make fuzz` in the CI timeout.
|
||||
|
||||
`build-fuzz` and `fuzz` fail immediately if `GOFUZZ_PACKAGE` is missing, the package cannot be resolved, or the seed corpus directory is missing or empty.
|
||||
|
||||
## Examples
|
||||
|
||||
Operators (matchers & extractors) harness:
|
||||
|
||||
```bash
|
||||
make fuzz GOFUZZ_PACKAGE=./pkg/operators/matchers
|
||||
make fuzz GOFUZZ_PACKAGE=./pkg/operators/extractors
|
||||
```
|
||||
|
||||
**Harness Coverage**:
|
||||
|
||||
Matchers exercise `Matcher.CompileMatchers()` through a compact line-based grammar. Supported keys are:
|
||||
|
||||
- `type`
|
||||
- `condition`
|
||||
- `part`
|
||||
- `encoding`
|
||||
- `negative`
|
||||
- `case-insensitive`
|
||||
- `match-all`
|
||||
- `name`
|
||||
- `value`
|
||||
- `status`
|
||||
- `size`
|
||||
|
||||
Extractors exercise `Extractor.CompileExtractors()` and the matching extraction path with fixed local corpora. Supported keys are:
|
||||
|
||||
- `type`
|
||||
- `part`
|
||||
- `name`
|
||||
- `internal`
|
||||
- `case-insensitive`
|
||||
- `group`
|
||||
- `attribute`
|
||||
- `value`
|
||||
- `regex`
|
||||
- `kval`
|
||||
- `json`
|
||||
- `xpath`
|
||||
- `dsl`
|
||||
|
||||
## Artifacts
|
||||
|
||||
Each run writes under `<package>/.gofuzz/`:
|
||||
|
||||
- `corpus/`: evolving seed corpus used by `go-fuzz`.
|
||||
- `crashers/`: saved crashing inputs.
|
||||
- `*-fuzz.zip`: the instrumented build output created by `go-fuzz-build`.
|
||||
|
||||
Promote real crashers into ordinary regression tests in the owning package once the underlying issue is fixed.
|
||||
|
||||
## GitHub Actions
|
||||
|
||||
The repository also has a dedicated GitHub Actions fuzz workflow in `.github/workflows/fuzz.yaml`.
|
||||
|
||||
- Triggers: `workflow_dispatch` and a weekly `schedule`.
|
||||
- Scheduled runs: limited to `projectdiscovery/nuclei`; manual dispatch still works in forks.
|
||||
- Matrix source: any package that has committed seeds under `testdata/gofuzz-corpus/` and a sibling `fuzz.go` harness.
|
||||
- Current package matrix: `./pkg/operators/matchers` and `./pkg/operators/extractors`.
|
||||
- Runtime model: discovery runs `make discover-fuzz-packages`, and each package runs `CI=true make fuzz-ci GOFUZZ_PACKAGE=... FUZZ_DURATION=15m`.
|
||||
- Artifacts: each matrix job uploads the full `<package>/.gofuzz/` as the artifacts.
|
||||
- Failure policy: crashers are summarized and uploaded, but they do not fail the workflow; build/setup errors still fail the job.
|
||||
@@ -2,41 +2,34 @@
|
||||
GOCMD := go
|
||||
GOBUILD := $(GOCMD) build
|
||||
GOBUILD_OUTPUT :=
|
||||
GOBUILD_OUTPUT_EXT :=
|
||||
GOBUILD_PACKAGES :=
|
||||
GOBUILD_ADDITIONAL_ARGS :=
|
||||
GOMOD := $(GOCMD) mod
|
||||
GOTEST := $(GOCMD) test
|
||||
GOFLAGS := -v
|
||||
GOFUZZ_PACKAGE ?=
|
||||
FUZZ_DURATION ?= 15m
|
||||
# This should be disabled if the binary uses pprof
|
||||
LDFLAGS := -s -w
|
||||
|
||||
ifneq ($(shell go env GOOS),darwin)
|
||||
LDFLAGS += -extldflags "-static"
|
||||
endif
|
||||
|
||||
ifeq ($(shell go env GOOS),windows)
|
||||
GOBUILD_OUTPUT_EXT := .exe
|
||||
endif
|
||||
|
||||
.PHONY: all build build-stats clean devtools-all devtools-bindgen devtools-scrapefuncs fuzz fuzz-ci fuzz-tools
|
||||
.PHONY: devtools-tsgen docs docgen dsl-docs functional go-build lint lint-strict fuzzplayground syntax-docs
|
||||
.PHONY: integration integration-debug jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test test-with-lint
|
||||
.PHONY: tidy ts verify download vet template-validate build-fuzz discover-fuzz-packages
|
||||
|
||||
.PHONY: all build build-stats clean devtools-all devtools-bindgen devtools-scrapefuncs
|
||||
.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
|
||||
|
||||
clean:
|
||||
rm -f '${GOBUILD_OUTPUT}${GOBUILD_OUTPUT_EXT}' 2>/dev/null
|
||||
rm -f '${GOBUILD_OUTPUT}' 2>/dev/null
|
||||
|
||||
go-build: clean
|
||||
go-build:
|
||||
CGO_ENABLED=0 $(GOBUILD) -trimpath $(GOFLAGS) -ldflags '${LDFLAGS}' $(GOBUILD_ADDITIONAL_ARGS) \
|
||||
-o '${GOBUILD_OUTPUT}${GOBUILD_OUTPUT_EXT}' $(GOBUILD_PACKAGES)
|
||||
-o '${GOBUILD_OUTPUT}' $(GOBUILD_PACKAGES)
|
||||
|
||||
build: GOFLAGS = -pgo=auto
|
||||
build: GOFLAGS = -v -pgo=auto
|
||||
build: GOBUILD_OUTPUT = ./bin/nuclei
|
||||
build: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
build: go-build
|
||||
@@ -47,7 +40,7 @@ build-test: GOBUILD_PACKAGES = ./cmd/nuclei/
|
||||
build-test: clean
|
||||
build-test:
|
||||
CGO_ENABLED=0 $(GOCMD) test -c -trimpath $(GOFLAGS) -ldflags '${LDFLAGS}' $(GOBUILD_ADDITIONAL_ARGS) \
|
||||
-o '${GOBUILD_OUTPUT}${GOBUILD_OUTPUT_EXT}' ${GOBUILD_PACKAGES}
|
||||
-o '${GOBUILD_OUTPUT}' ${GOBUILD_PACKAGES}
|
||||
|
||||
build-stats: GOBUILD_OUTPUT = ./bin/nuclei-stats
|
||||
build-stats: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
@@ -82,24 +75,15 @@ syntax-docs: docgen
|
||||
syntax-docs:
|
||||
./bin/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
|
||||
test: GOFLAGS = -race -v -timeout 1h -count 1
|
||||
test: GOFLAGS = -race -v
|
||||
test:
|
||||
$(GOTEST) $(GOFLAGS) ./...
|
||||
|
||||
integration:
|
||||
$(GOTEST) -tags=integration -timeout 1h ./internal/tests/integration
|
||||
cd integration_tests; bash run.sh
|
||||
|
||||
integration-debug:
|
||||
$(GOTEST) -tags=integration ./internal/tests/integration -v $(GO_TEST_ARGS) -args $(INTEGRATION_ARGS)
|
||||
|
||||
functional: build
|
||||
@release_binary="$$(command -v nuclei.exe 2>/dev/null || command -v nuclei 2>/dev/null)"; \
|
||||
if [ -z "$$release_binary" ]; then \
|
||||
echo "release nuclei binary not found on PATH"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
RELEASE_BINARY="$$release_binary" DEV_BINARY="$(PWD)/bin/nuclei" \
|
||||
$(GOTEST) -tags=functional -timeout 1h ./internal/tests/functional
|
||||
functional:
|
||||
cd cmd/functional-test; bash run.sh
|
||||
|
||||
tidy:
|
||||
$(GOMOD) tidy
|
||||
@@ -148,121 +132,6 @@ fuzzplayground: GOBUILD_PACKAGES = cmd/tools/fuzzplayground/main.go
|
||||
fuzzplayground: LDFLAGS = -s -w
|
||||
fuzzplayground: go-build
|
||||
|
||||
fuzz-tools:
|
||||
@$(GOCMD) tool -modfile=go.tool.mod -n go-fuzz-build >/dev/null
|
||||
@$(GOCMD) tool -modfile=go.tool.mod -n go-fuzz >/dev/null
|
||||
|
||||
discover-fuzz-packages:
|
||||
@set -eu; \
|
||||
entries_file="$$(mktemp)"; \
|
||||
trap 'rm -f "$$entries_file"' EXIT HUP INT TERM; \
|
||||
find . -type d -path '*/testdata/gofuzz-corpus' | LC_ALL=C sort | while IFS= read -r corpus_dir; do \
|
||||
pkg_dir="$${corpus_dir%/testdata/gofuzz-corpus}"; \
|
||||
if [ ! -f "$$pkg_dir/fuzz.go" ]; then \
|
||||
continue; \
|
||||
fi; \
|
||||
pkg_dir="$${pkg_dir#./}"; \
|
||||
pkg="./$$pkg_dir"; \
|
||||
if ! $(GOCMD) list "$$pkg" >/dev/null 2>&1; then \
|
||||
echo "failed to resolve $$pkg"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
jq -cn --arg pkg "$$pkg" '{pkg: $$pkg}' >> "$$entries_file"; \
|
||||
done; \
|
||||
if [ ! -s "$$entries_file" ]; then \
|
||||
echo "no fuzzable packages discovered"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
matrix="$$(jq -cs '{include: .}' "$$entries_file")"; \
|
||||
if [ -n "$${GITHUB_OUTPUT:-}" ]; then \
|
||||
printf 'matrix=%s\n' "$$matrix" >> "$${GITHUB_OUTPUT}"; \
|
||||
else \
|
||||
printf '%s\n' "$$matrix"; \
|
||||
fi
|
||||
|
||||
build-fuzz: fuzz-tools
|
||||
@if [ -z "$(GOFUZZ_PACKAGE)" ]; then echo "GOFUZZ_PACKAGE is required"; exit 1; fi
|
||||
@PACKAGE_DIR="$$( $(GOCMD) list -f '{{.Dir}}' $(GOFUZZ_PACKAGE) 2>/dev/null )" || { \
|
||||
echo "failed to resolve GOFUZZ_PACKAGE: $(GOFUZZ_PACKAGE)"; \
|
||||
exit 1; \
|
||||
}; \
|
||||
WORKDIR="$$PACKAGE_DIR/.gofuzz"; \
|
||||
CORPUS_DIR="$$PACKAGE_DIR/testdata/gofuzz-corpus"; \
|
||||
BIN_PATH="$$WORKDIR/$$(basename "$$PACKAGE_DIR")-fuzz.zip"; \
|
||||
if [ ! -d "$$CORPUS_DIR" ]; then \
|
||||
echo "seed corpus directory does not exist: $$CORPUS_DIR"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
set -- "$$CORPUS_DIR"/*; \
|
||||
if [ "$$1" = "$$CORPUS_DIR/*" ]; then \
|
||||
echo "seed corpus directory has no seed files: $$CORPUS_DIR"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
mkdir -p "$$WORKDIR/corpus"; \
|
||||
cp -f "$$CORPUS_DIR"/* "$$WORKDIR/corpus/"; \
|
||||
GO111MODULE=on $(GOCMD) tool -modfile=go.tool.mod go-fuzz-build -o "$$BIN_PATH" $(GOFUZZ_PACKAGE)
|
||||
|
||||
fuzz: build-fuzz
|
||||
@PACKAGE_DIR="$$( $(GOCMD) list -f '{{.Dir}}' $(GOFUZZ_PACKAGE) 2>/dev/null )" || { \
|
||||
echo "failed to resolve GOFUZZ_PACKAGE: $(GOFUZZ_PACKAGE)"; \
|
||||
exit 1; \
|
||||
}; \
|
||||
WORKDIR="$$PACKAGE_DIR/.gofuzz"; \
|
||||
BIN_PATH="$$WORKDIR/$$(basename "$$PACKAGE_DIR")-fuzz.zip"; \
|
||||
$(GOCMD) tool -modfile=go.tool.mod go-fuzz -bin="$$BIN_PATH" -workdir="$$WORKDIR"
|
||||
|
||||
fuzz-ci:
|
||||
@if [ "$${CI:-}" != "true" ]; then echo "fuzz-ci should be run in CI; use 'make fuzz' locally"; exit 1; fi
|
||||
@if [ -z "$(GOFUZZ_PACKAGE)" ]; then echo "GOFUZZ_PACKAGE is required"; exit 1; fi
|
||||
@set -eu; \
|
||||
pkg_dir="$(GOFUZZ_PACKAGE)"; \
|
||||
pkg_dir="$${pkg_dir#./}"; \
|
||||
workdir="$$pkg_dir/.gofuzz"; \
|
||||
pkg_id="$$(printf '%s' "$$pkg_dir" | tr '/.' '-')"; \
|
||||
artifact_name="gofuzz-$$pkg_id"; \
|
||||
if [ -n "$${GITHUB_OUTPUT:-}" ]; then \
|
||||
printf 'artifact_name=%s\n' "$$artifact_name" >> "$${GITHUB_OUTPUT}"; \
|
||||
fi; \
|
||||
mkdir -p "$$workdir"; \
|
||||
timeout_bin="$$(command -v timeout || command -v gtimeout || true)"; \
|
||||
if [ -z "$$timeout_bin" ]; then \
|
||||
echo "timeout command not found"; \
|
||||
exit 1; \
|
||||
fi; \
|
||||
exit_code=0; \
|
||||
"$$timeout_bin" "$(FUZZ_DURATION)" $(MAKE) --no-print-directory fuzz GOFUZZ_PACKAGE="$(GOFUZZ_PACKAGE)" || exit_code=$$?; \
|
||||
if [ "$$exit_code" -ne 0 ] && [ "$$exit_code" -ne 124 ]; then \
|
||||
echo "fuzzing failed with exit code $$exit_code"; \
|
||||
exit "$$exit_code"; \
|
||||
fi; \
|
||||
count_files() { \
|
||||
dir="$$1"; \
|
||||
if [ ! -d "$$dir" ]; then \
|
||||
echo 0; \
|
||||
return; \
|
||||
fi; \
|
||||
if [ "$$(basename "$$dir")" = "crashers" ]; then \
|
||||
find "$$dir" -maxdepth 1 -type f -printf '%f\n' | sed -E 's/\.(output|quoted)$$//' | sort -u | wc -l | tr -d '[:space:]'; \
|
||||
return; \
|
||||
fi; \
|
||||
find "$$dir" -maxdepth 1 -type f | wc -l | tr -d '[:space:]'; \
|
||||
}; \
|
||||
crasher_count="$$(count_files "$$workdir/crashers")"; \
|
||||
corpus_count="$$(count_files "$$workdir/corpus")"; \
|
||||
suppressions_count="$$(count_files "$$workdir/suppressions")"; \
|
||||
if [ -n "$${GITHUB_STEP_SUMMARY:-}" ]; then \
|
||||
{ \
|
||||
echo "## \`$(GOFUZZ_PACKAGE)\`"; \
|
||||
echo; \
|
||||
echo "- Corpus: $$corpus_count"; \
|
||||
echo "- Crashers: $$crasher_count"; \
|
||||
echo "- Suppressions: $$suppressions_count"; \
|
||||
echo; \
|
||||
} >> "$${GITHUB_STEP_SUMMARY}"; \
|
||||
else \
|
||||
printf 'Package: %s\nCorpus: %s\nCrashers: %s\nSuppressions: %s\n' "$(GOFUZZ_PACKAGE)" "$$corpus_count" "$$crasher_count" "$$suppressions_count"; \
|
||||
fi
|
||||
|
||||
memogen: GOBUILD_OUTPUT = ./bin/memogen
|
||||
memogen: GOBUILD_PACKAGES = cmd/memogen/memogen.go
|
||||
memogen: go-build
|
||||
@@ -277,14 +146,5 @@ dsl-docs:
|
||||
template-validate: build
|
||||
template-validate:
|
||||
./bin/nuclei -ut
|
||||
./bin/nuclei -validate \
|
||||
-et http/technologies \
|
||||
-t dns \
|
||||
-t ssl \
|
||||
-t network \
|
||||
-t http/exposures \
|
||||
-ept code
|
||||
./bin/nuclei -validate \
|
||||
-w workflows \
|
||||
-et http/technologies \
|
||||
-ept code
|
||||
./bin/nuclei -validate -et http/technologies
|
||||
./bin/nuclei -validate -w workflows -et http/technologies
|
||||
@@ -8,13 +8,14 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">`Indonesia`</a> •
|
||||
<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> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_TR.md">`Türkçe`</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_PT-BR.md">`Portuguese`</a>
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<img src="https://img.shields.io/badge/go-1.22-%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>
|
||||
@@ -34,7 +35,7 @@
|
||||
- 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.
|
||||
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS JavaScript, Code and more.
|
||||
- Integrate with Jira, Splunk, GitHub, Elastic, GitLab.
|
||||
|
||||
<br>
|
||||
@@ -84,7 +85,7 @@ _For security teams and enterprises, we provide a cloud-hosted service built on
|
||||
|
||||
- 50x faster scans
|
||||
- Large scale scanning with high accuracy
|
||||
- Integrations with cloud services (AWS, GCP, Azure, Cloudflare, Fastly, Terraform, Kubernetes)
|
||||
- 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
|
||||
@@ -98,7 +99,7 @@ _For security teams and enterprises, we provide a cloud-hosted service built on
|
||||
|
||||
## Documentation
|
||||
|
||||
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).
|
||||
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).
|
||||
|
||||
<div align="center">
|
||||
|
||||
@@ -110,7 +111,7 @@ Browse the full Nuclei [**`documentation here`**](https://docs.projectdiscovery.
|
||||
|
||||
### Installation
|
||||
|
||||
`nuclei` requires **go >= 1.24.2** to install successfully. Run the following command to get the repo:
|
||||
`nuclei` requires **go1.23** to install successfully. Run the following command to get the repo:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -141,7 +142,7 @@ TARGET:
|
||||
-u, -target string[] target URLs/hosts to scan
|
||||
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
|
||||
-eh, -exclude-hosts string[] hosts to exclude to scan from the input list (ip, cidr, hostname)
|
||||
-resume string resume scan from and save to specified file (clustering will be disabled)
|
||||
-resume string resume scan using resume.cfg (clustering will be disabled)
|
||||
-sa, -scan-all-ips scan all the IP's associated with dns record
|
||||
-iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4)
|
||||
|
||||
@@ -162,7 +163,7 @@ TEMPLATES:
|
||||
-validate validate the passed templates to nuclei
|
||||
-nss, -no-strict-syntax disable strict syntax check on templates
|
||||
-td, -template-display displays the templates content
|
||||
-tl list all templates matching current filters
|
||||
-tl list all available templates
|
||||
-tgl list all available tags
|
||||
-sign signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable
|
||||
-code enable loading code protocol-based templates
|
||||
@@ -285,7 +286,6 @@ RATE-LIMIT:
|
||||
-jsc, -js-concurrency int maximum number of javascript runtimes to be executed in parallel (default 120)
|
||||
-pc, -payload-concurrency int max payload concurrency for each template (default 25)
|
||||
-prc, -probe-concurrency int http probe concurrency with httpx (default 50)
|
||||
-tlc, -template-loading-concurrency int maximum number of concurrent template loading operations (default 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int time to wait in seconds before timeout (default 10)
|
||||
@@ -309,7 +309,6 @@ HEADLESS:
|
||||
-sb, -show-browser show the browser on the screen when running templates with headless mode
|
||||
-ho, -headless-options string[] start headless chrome with additional options
|
||||
-sc, -system-chrome use local installed Chrome browser instead of nuclei installed
|
||||
-cdpe, -cdp-endpoint string use remote browser via Chrome DevTools Protocol (CDP) endpoint
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
@@ -338,11 +337,6 @@ UPDATE:
|
||||
-ud, -update-template-dir string custom directory to install / update nuclei-templates
|
||||
-duc, -disable-update-check disable automatic nuclei/templates update check
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
STATISTICS:
|
||||
-stats display statistics about the running scan
|
||||
-sj, -stats-json display statistics in JSONL(ines) format
|
||||
@@ -362,7 +356,6 @@ CLOUD:
|
||||
AUTHENTICATION:
|
||||
-sf, -secret-file string[] path to config file containing secrets for nuclei authenticated scan
|
||||
-ps, -prefetch-secrets prefetch secrets from the secrets file
|
||||
# NOTE: Headers in secrets files preserve exact casing (useful for case-sensitive APIs)
|
||||
|
||||
|
||||
EXAMPLES:
|
||||
@@ -381,11 +374,11 @@ Run nuclei with a JSON output:
|
||||
Run nuclei with sorted Markdown outputs (with environment variables):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.projectdiscovery.io/getting-started/running
|
||||
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
||||
|
||||
```
|
||||
|
||||
Additional documentation is available at: [**`docs.projectdiscovery.io/getting-started/running`**](https://docs.projectdiscovery.io/getting-started/running?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme)
|
||||
Additional documentation is available at: [**`docs.nuclei.sh/getting-started/running`**](https://docs.nuclei.sh/getting-started/running?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
+4
-11
@@ -59,7 +59,7 @@ Nuclei使用零误报的定制模板向目标发送请求,同时可以对主
|
||||
|
||||
# 安装Nuclei
|
||||
|
||||
Nuclei需要 **go1.24.2** 才能安装成功。执行下列命令安装最新版本的Nuclei
|
||||
Nuclei需要 **go1.22** 才能安装成功。执行下列命令安装最新版本的Nuclei
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -119,7 +119,7 @@ Nuclei是一款注重于可配置性、可扩展性和易用性的基于模板
|
||||
目标:
|
||||
-u, -target string[] 指定扫描的目标URL/主机(多个目标则指定多个-u参数)
|
||||
-l, -list string 指定包含要扫描的目标URL/主机列表的文件路径(一行一个)
|
||||
-resume string 从指定文件恢复扫描并保存到指定文件(将禁用请求聚类)
|
||||
-resume string 使用指定的resume.cfg文件恢复扫描(将禁用请求聚类)
|
||||
-sa, -scan-all-ips 扫描由目标解析出来的所有IP(针对域名对应多个IP的情况)
|
||||
-iv, -ip-version string[] 要扫描的主机名的IP版本(4,6)-(默认为4)
|
||||
|
||||
@@ -238,7 +238,6 @@ UNCOVER引擎:
|
||||
-c, -concurrency int 并行执行的最大模板数量(默认:25)
|
||||
-hbs, -headless-bulk-size int 每个模板并行运行的无头主机最大数量(默认:10)
|
||||
-headc, -headless-concurrency int 并行指定无头主机最大数量(默认:10)
|
||||
-tlc, -template-loading-concurrency int 最大并发模板加载操作数(默认:50)
|
||||
|
||||
|
||||
优化:
|
||||
@@ -263,7 +262,6 @@ UNCOVER引擎:
|
||||
-sb, -show-brower 在无界面浏览器运行模板时,显示浏览器
|
||||
-ho, -headless-options string[] 使用附加选项启动无界面浏览器
|
||||
-sc, -system-chrome 不使用Nuclei自带的浏览器,使用本地浏览器
|
||||
-cdpe, -cdp-endpoint string 通过Chrome DevTools Protocol (CDP)端点使用远程浏览器
|
||||
-lha, -list-headless-action 列出可用的无界面操作
|
||||
|
||||
调试:
|
||||
@@ -291,11 +289,6 @@ UNCOVER引擎:
|
||||
-ud, -update-template-dir string 指定模板目录
|
||||
-duc, -disable-update-check 禁用nuclei程序与模板更新
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
统计:
|
||||
-stats 显示正在扫描的统计信息
|
||||
-sj, -stats-json 将统计信息以JSONL格式输出到文件
|
||||
@@ -325,7 +318,7 @@ HONEYPOT:
|
||||
|
||||
```
|
||||
|
||||
更多信息请参考文档: https://docs.projectdiscovery.io/getting-started/running
|
||||
更多信息请参考文档: https://docs.nuclei.sh/getting-started/running
|
||||
|
||||
|
||||
### 运行Nuclei
|
||||
@@ -377,7 +370,7 @@ Nuclei允许您定制自己的测试方法,可以轻松的运行您的程序
|
||||
- 可以在几分钟处理上千台主机
|
||||
- 使用YAML语法定制自动化测试
|
||||
|
||||
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](https://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](https://chaos.projectdiscovery.io)。
|
||||
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](http://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](http://chaos.projectdiscovery.io)。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+3
-10
@@ -56,7 +56,7 @@ Tenemos un [repositorio dedicado](https://github.com/projectdiscovery/nuclei-tem
|
||||
|
||||
# Instalación de Nuclei
|
||||
|
||||
Nuclei requiere **go1.24.2** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
|
||||
Nuclei requiere **go1.22** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -118,7 +118,7 @@ TARGET:
|
||||
-u, -target string[] URLs/hosts a escanear
|
||||
-l, -list string ruta al archivo que contiene la lista de URLs/hosts a escanear (uno por línea)
|
||||
-eh, -exclude-hosts string[] hosts a excluir para escanear de la lista de entrada (ip, cidr, hostname)
|
||||
-resume string reanudar el escaneo desde y guardar en el archivo especificado (la clusterización quedará inhabilitada)
|
||||
-resume string reanudar el escaneo usando resume.cfg (la clusterización quedará inhabilitada)
|
||||
-sa, -scan-all-ips escanear todas las IP asociadas al registro dns
|
||||
-iv, -ip-version string[] versión IP a escanear del nombre de host (4,6) - (por defecto 4)
|
||||
|
||||
@@ -244,7 +244,6 @@ RATE-LIMIT:
|
||||
-headc, -headless-concurrency int número máximo de plantillas headless a ejecutar en paralelo (por defecto 10)
|
||||
-jsc, -js-concurrency int número máximo de entornos de ejecución de JavaScript a ejecutar en paralelo (por defecto 120)
|
||||
-pc, -payload-concurrency int concurrencia máxima de carga útil para cada plantilla (por defecto 25)
|
||||
-tlc, -template-loading-concurrency int número máximo de operaciones de carga de plantillas concurrentes (por defecto 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int tiempo de espera en segundos (por defecto 10)
|
||||
@@ -268,7 +267,6 @@ HEADLESS:
|
||||
-sb, -show-browser muestra el navegador en la pantalla al ejecutar plantillas con modo sin interfaz
|
||||
-ho, -headless-options string[] inicia Chrome en modo sin interfaz con opciones adicionales
|
||||
-sc, -system-chrome utiliza el navegador Chrome instalado localmente en lugar del instalado por nuclei
|
||||
-cdpe, -cdp-endpoint string usar navegador remoto a través del endpoint del Protocolo de Herramientas de Desarrollador de Chrome (CDP)
|
||||
-lha, -list-headless-action lista de acciones sin interfaz disponibles
|
||||
|
||||
DEBUG:
|
||||
@@ -296,11 +294,6 @@ UPDATE:
|
||||
-ud, -update-template-dir string directorio personalizado para instalar/actualizar nuclei-templates
|
||||
-duc, -disable-update-check deshabilita la comprobación automática de actualizaciones de nuclei/templates
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
STATISTICS:
|
||||
-stats muestra estadísticas sobre el escaneo en ejecución
|
||||
-sj, -stats-json muestra estadísticas en formato JSONL(ines)
|
||||
@@ -333,7 +326,7 @@ Ejecutar nuclei con una salida JSON:
|
||||
Ejecutar nuclei con salidas Markdown ordenadas (con variables de entorno):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Documentación adicional disponible en: https://docs.projectdiscovery.io/getting-started/running
|
||||
Documentación adicional disponible en: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Ejecutando Nuclei
|
||||
|
||||
+4
-11
@@ -54,7 +54,7 @@ Kami memiliki [repositori khusus](https://github.com/projectdiscovery/nuclei-tem
|
||||
|
||||
# Instalasi Nuclei
|
||||
|
||||
Nuclei membutuhkan **go1.24.2** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
|
||||
Nuclei membutuhkan **go1.22** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -98,7 +98,7 @@ Flags:
|
||||
TARGET:
|
||||
-u, -target string[] target URLs/hosts to scan
|
||||
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
|
||||
-resume string resume scan from and save to specified file (clustering will be disabled)
|
||||
-resume string resume scan using resume.cfg (clustering will be disabled)
|
||||
-sa, -scan-all-ips scan all the IP's associated with dns record
|
||||
-iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4)
|
||||
|
||||
@@ -211,7 +211,6 @@ RATE-LIMIT:
|
||||
-c, -concurrency int maximum number of templates to be executed in parallel (default 25)
|
||||
-hbs, -headless-bulk-size int maximum number of headless hosts to be analyzed in parallel per template (default 10)
|
||||
-headc, -headless-concurrency int maximum number of headless templates to be executed in parallel (default 10)
|
||||
-tlc, -template-loading-concurrency int maximum number of concurrent template loading operations (default 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int time to wait in seconds before timeout (default 10)
|
||||
@@ -234,7 +233,6 @@ HEADLESS:
|
||||
-page-timeout int seconds to wait for each page in headless mode (default 20)
|
||||
-sb, -show-browser show the browser on the screen when running templates with headless mode
|
||||
-sc, -system-chrome use local installed Chrome browser instead of nuclei installed
|
||||
-cdpe, -cdp-endpoint string use remote browser via Chrome DevTools Protocol (CDP) endpoint
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
@@ -262,11 +260,6 @@ UPDATE:
|
||||
-ud, -update-template-dir string custom directory to install / update nuclei-templates
|
||||
-duc, -disable-update-check disable automatic nuclei/templates update check
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
STATISTICS:
|
||||
-stats display statistics about the running scan
|
||||
-sj, -stats-json display statistics in JSONL(ines) format
|
||||
@@ -296,7 +289,7 @@ Run nuclei with a JSON output:
|
||||
Run nuclei with sorted Markdown outputs (with environment variables):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.projectdiscovery.io/getting-started/running
|
||||
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Menjalankan Nuclei
|
||||
@@ -348,7 +341,7 @@ Nuclei memungkinkan Anda untuk menyesuaikan pendekatan pengujian Anda dengan ran
|
||||
- Dapat memproses ribuan host hanya dalam beberapa menit.
|
||||
- Mudah mengotomatiskan pendekatan pengujian khusus Anda dengan sintaks DSL berbasis YAML sederhana kami.
|
||||
|
||||
Silakan periksa proyek sumber terbuka kami yang lain yang mungkin cocok dengan alur kerja celah berhadiah Anda: [github.com/projectdiscovery](https://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](https://chaos.projectdiscovery.io).
|
||||
Silakan periksa proyek sumber terbuka kami yang lain yang mungkin cocok dengan alur kerja celah berhadiah Anda: [github.com/projectdiscovery](http://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](http://chaos.projectdiscovery.io).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ Nucleiは、テンプレートに基づいてターゲット間でリクエス
|
||||
|
||||
# Nucleiのインストール
|
||||
|
||||
Nucleiを正常にインストールするには、**go1.24.2**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
Nucleiを正常にインストールするには、**go1.22**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -113,7 +113,7 @@ Nucleiは、広範な設定可能性、大規模な拡張性、および使い
|
||||
ターゲット:
|
||||
-u, -target string[] スキャンする対象のURL/ホスト
|
||||
-l, -list string スキャンする対象のURL/ホストのリストが含まれているファイルへのパス(1行に1つ)
|
||||
-resume string 指定されたファイルからスキャンを再開し、指定されたファイルに保存(クラスタリングは無効になります)
|
||||
-resume string resume.cfgを使用してスキャンを再開(クラスタリングは無効になります)
|
||||
-sa, -scan-all-ips DNSレコードに関連付けられているすべてのIPをスキャン
|
||||
-iv, -ip-version string[] ホスト名のスキャンするIPバージョン(4,6)-(デフォルトは4)
|
||||
|
||||
|
||||
+4
-11
@@ -52,7 +52,7 @@ Nuclei는 템플릿을 기반으로 대상 간에 요청을 보내기 위해 사
|
||||
|
||||
# 설치
|
||||
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.24.2**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.22**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -96,7 +96,7 @@ Nuclei는 빠르고, 템플릿 기반의 취약점 스캐너로
|
||||
TARGET:
|
||||
-u, -target string[] 스캔할 대상 URL/호스트
|
||||
-l, -list string 스캔할 대상 URL/호스트 목록이 있는 파일 경로 (한 줄에 하나씩)
|
||||
-resume string 지정된 파일에서 스캔을 재개하고 지정된 파일에 저장 (클러스터링은 비활성화됨)
|
||||
-resume string resume.cfg를 사용하여 스캔 재개 (클러스터링은 비활성화됨)
|
||||
-sa, -scan-all-ips dns 레코드와 관련된 모든 IP 스캔
|
||||
-iv, -ip-version string[] 스캔할 호스트의 IP 버전 (4,6) - (기본값 4)
|
||||
|
||||
@@ -209,7 +209,6 @@ RATE-LIMIT:
|
||||
-c, -concurrency int 병렬로 실행할 최대 템플릿 수 (기본값 25)
|
||||
-hbs, -headless-bulk-size int 템플릿당 병렬로 분석할 최대 headless 호스트 수 (기본값 10)
|
||||
-headc, -headless-concurrency int 병렬로 실행할 최대 headless 템플릿 수 (기본값 10)
|
||||
-tlc, -template-loading-concurrency int 최대 동시 템플릿 로딩 작업 수 (기본값 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int 타임아웃 전에 기다릴 초 수 (기본값 10)
|
||||
@@ -233,7 +232,6 @@ HEADLESS:
|
||||
-sb, -show-browser headless 모드로 실행하는 템플릿에서 브라우저 화면 표시
|
||||
-ho, -headless-options string[] 추가 옵션으로 headless chrome 시작
|
||||
-sc, -system-chrome nuclei가 설치한 Chrome 대신 로컬에 설치된 Chrome 브라우저 사용
|
||||
-cdpe, -cdp-endpoint string Chrome DevTools Protocol (CDP) 엔드포인트를 통한 원격 브라우저 사용
|
||||
-lha, -list-headless-action 사용 가능한 headless 액션 목록 표시
|
||||
|
||||
DEBUG:
|
||||
@@ -261,11 +259,6 @@ UPDATE:
|
||||
-ud, -update-template-dir string nuclei-templates를 설치/업데이트할 사용자 지정 디렉토리
|
||||
-duc, -disable-update-check 자동 nuclei/templates 업데이트 확인 비활성화
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
STATISTICS:
|
||||
-stats 실행 중인 스캔에 대한 통계 표시
|
||||
-sj, -stats-json JSONL(ines) 형식으로 통계 표시
|
||||
@@ -294,7 +287,7 @@ JSON 출력으로 nuclei 실행:
|
||||
정렬된 Markdown 출력으로 nuclei 실행 (환경 변수 사용):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
추가 문서는 여기에서 확인할 수 있습니다: https://docs.projectdiscovery.io/getting-started/running
|
||||
추가 문서는 여기에서 확인할 수 있습니다: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Nuclei 실행
|
||||
@@ -348,7 +341,7 @@ Nuclei를 사용하면 자체 검사 모음으로 테스트 접근 방식을 사
|
||||
- 몇 분 안에 수천 개의 호스트를 처리할 수 있음.
|
||||
- 간단한 YAML DSL로 사용자 지정 테스트 접근 방식을 쉽게 자동화할 수 있음.
|
||||
|
||||
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](https://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](https://chaos.projectdiscovery.io).
|
||||
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](http://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](http://chaos.projectdiscovery.io).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+3
-10
@@ -56,7 +56,7 @@ Temos um [repositório dedicado](https://github.com/projectdiscovery/nuclei-temp
|
||||
|
||||
# Instalação do Nuclei
|
||||
|
||||
O Nuclei requer **go1.24.2** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
|
||||
O Nuclei requer **go1.22** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -118,7 +118,7 @@ TARGET:
|
||||
-u, -target string[] URLs/hosts a serem escaneados
|
||||
-l, -list string caminho do arquivo contendo a lista de URLs/hosts a serem escaneados (um por linha)
|
||||
-eh, -exclude-hosts string[] hosts a serem excluídos do escaneamento na lista de entrada (ip, cidr, hostname)
|
||||
-resume string retomar o escaneamento a partir de e salvar no arquivo especificado (a clusterização será desabilitada)
|
||||
-resume string retomar o escaneamento usando resume.cfg (a clusterização será desabilitada)
|
||||
-sa, -scan-all-ips escanear todos os IPs associados ao registro DNS
|
||||
-iv, -ip-version string[] versão de IP a escanear do nome do host (4,6) - (padrão 4)
|
||||
|
||||
@@ -244,7 +244,6 @@ RATE-LIMIT:
|
||||
-headc, -headless-concurrency int número máximo de templates headless a serem executados em paralelo (padrão 10)
|
||||
-jsc, -js-concurrency int número máximo de ambientes de execução de JavaScript a serem executados em paralelo (padrão 120)
|
||||
-pc, -payload-concurrency int concorrência máxima de payload para cada template (padrão 25)
|
||||
-tlc, -template-loading-concurrency int número máximo de operações de carregamento de templates concorrentes (padrão 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int tempo limite em segundos (padrão 10)
|
||||
@@ -268,7 +267,6 @@ HEADLESS:
|
||||
-sb, -show-browser exibe o navegador na tela ao executar templates no modo headless
|
||||
-ho, -headless-options string[] inicia o Chrome no modo headless com opções adicionais
|
||||
-sc, -system-chrome utiliza o navegador Chrome instalado localmente em vez do instalado pelo nuclei
|
||||
-cdpe, -cdp-endpoint string usar navegador remoto via endpoint do Protocolo de Ferramentas de Desenvolvedor do Chrome (CDP)
|
||||
-lha, -list-headless-action lista ações disponíveis para o modo headless
|
||||
|
||||
DEBUG:
|
||||
@@ -296,11 +294,6 @@ UPDATE:
|
||||
-ud, -update-template-dir string diretório personalizado para instalar/atualizar os nuclei-templates
|
||||
-duc, -disable-update-check desativa a verificação automática de atualizações do nuclei/templates
|
||||
|
||||
HONEYPOT:
|
||||
-hpd, -honeypot-detect detect potential honeypot hosts based on match concentration
|
||||
-hpt, -honeypot-threshold int number of distinct template IDs required to flag a honeypot host (default 15)
|
||||
-shp, -suppress-honeypot suppress output for flagged honeypot hosts
|
||||
|
||||
STATISTICS:
|
||||
-stats exibe estatísticas sobre o scan em execução
|
||||
-sj, -stats-json exibe estatísticas no formato JSONL(ines)
|
||||
@@ -333,7 +326,7 @@ Executar nuclei com saída JSON:
|
||||
Executar nuclei com saídas Markdown organizadas (com variáveis de ambiente):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Documentação adicional disponível em: https://docs.projectdiscovery.io/getting-started/running
|
||||
Documentação adicional disponível em: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Executando Nuclei
|
||||
|
||||
-294
@@ -1,294 +0,0 @@
|
||||

|
||||
|
||||
<div align="center">
|
||||
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">`English`</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">`中文`</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">`Korean`</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">`Indonesia`</a> •
|
||||
<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> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_PT-BR.md">`Portuguese`</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_TR.md">`Türkçe`</a>
|
||||
|
||||
</div>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<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>
|
||||
|
||||
<hr>
|
||||
|
||||
</p>
|
||||
|
||||
<br>
|
||||
|
||||
**Nuclei, basit YAML tabanlı şablonlardan yararlanan modern, yüksek performanslı bir zafiyet tarayıcısıdır. Gerçek dünya koşullarını taklit eden özel zafiyet tespit senaryoları tasarlamanıza olanak tanıyarak sıfır hatalı pozitif sonuç sağlar.**
|
||||
|
||||
- Güvenlik açığı şablonları oluşturmak ve özelleştirmek için basit YAML formatı.
|
||||
- Trend olan güvenlik açıklarını ele almak için binlerce güvenlik uzmanı tarafından katkıda bulunulmuştur.
|
||||
- Bir güvenlik açığını doğrulamak için gerçek dünya adımlarını simüle ederek hatalı pozitifleri azaltır.
|
||||
- Ultra hızlı paralel tarama işleme ve istek kümeleme.
|
||||
- Zafiyet tespiti ve regresyon testi için CI/CD hatlarına entegre edilebilir.
|
||||
- TCP, DNS, HTTP, SSL, WHOIS, JavaScript, Code ve daha fazlası gibi birçok protokolü destekler.
|
||||
- Jira, Splunk, GitHub, Elastic, GitLab ile entegre olur.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## İçindekiler
|
||||
|
||||
- [**`Başlarken`**](#başlarken)
|
||||
- [_`1. Nuclei CLI`_](#1-nuclei-cli)
|
||||
- [_`2. Pro ve Kurumsal Sürümler`_](#2-pro-ve-kurumsal-sürümler)
|
||||
- [**`Dokümantasyon`**](#dokümantasyon)
|
||||
- [_`Komut Satırı Bayrakları`_](#komut-satırı-bayrakları)
|
||||
- [_`Tek hedef tarama`_](#tek-hedef-tarama)
|
||||
- [_`Çoklu hedef tarama`_](#çoklu-hedef-tarama)
|
||||
- [_`Ağ taraması`_](#ağ-taraması)
|
||||
- [_`Özel şablonunuzla tarama`_](#özel-şablonunuzla-tarama)
|
||||
- [_`Nuclei'yi ProjectDiscovery'ye Bağlayın`_](#nucleiyi-projectdiscoveryye-bağlayın)
|
||||
- [**`Nuclei Şablonları, Topluluk ve Ödüller`**](#nuclei-şablonları-topluluk-ve-ödüller-) 💎
|
||||
- [**`Misyonumuz`**](#misyonumuz)
|
||||
- [**`Katkıda Bulunanlar`**](#katkıda-bulunanlar) ❤
|
||||
- [**`Lisans`**](#lisans)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Başlarken
|
||||
|
||||
### **1. Nuclei CLI**
|
||||
|
||||
_Nuclei'yi makinenize kurun. [**`Buradaki`**](https://docs.projectdiscovery.io/tools/nuclei/install?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme) kurulum kılavuzunu takip ederek başlayın. Ayrıca, [**`ücretsiz bir bulut katmanı`**](https://cloud.projectdiscovery.io/sign-up) sağlıyoruz ve cömert aylık ücretsiz limitlerle birlikte geliyor:_
|
||||
|
||||
- Zafiyet bulgularınızı saklayın ve görselleştirin
|
||||
- nuclei şablonlarınızı yazın ve yönetin
|
||||
- En son nuclei şablonlarına erişin
|
||||
- Hedeflerinizi keşfedin ve saklayın
|
||||
|
||||
> [!Important]
|
||||
> |**Bu proje aktif geliştirme aşamasındadır**. Sürümlerle birlikte kırılma değişiklikleri bekleyin. Güncellemeden önce sürüm değişiklik günlüğünü inceleyin.|
|
||||
> |:--------------------------------|
|
||||
> | Bu proje öncelikle bağımsız bir CLI aracı olarak kullanılmak üzere oluşturulmuştur. **Nuclei'yi bir servis olarak çalıştırmak güvenlik riskleri oluşturabilir.** Dikkatli kullanılması ve ek güvenlik önlemleri alınması önerilir. |
|
||||
|
||||
<br>
|
||||
|
||||
### **2. Pro ve Kurumsal Sürümler**
|
||||
|
||||
_Güvenlik ekipleri ve kuruluşlar için, ekibiniz ve mevcut iş akışlarınızla ölçekli olarak sürekli zafiyet taramaları yapmanıza yardımcı olmak üzere ince ayarlanmış, Nuclei OSS üzerine inşa edilmiş bulut tabanlı bir hizmet sunuyoruz:_
|
||||
|
||||
- 50x daha hızlı taramalar
|
||||
- Yüksek doğrulukla büyük ölçekli tarama
|
||||
- Bulut hizmetleri ile entegrasyonlar (AWS, GCP, Azure, Cloudflare, Fastly, Terraform, Kubernetes)
|
||||
- Jira, Slack, Linear, API'ler ve Webhook'lar
|
||||
- Yönetici ve uyumluluk raporlaması
|
||||
- Artı: Gerçek zamanlı tarama, SAML SSO, SOC 2 uyumlu platform (AB ve ABD barındırma seçenekleri ile), paylaşılan ekip çalışma alanları ve daha fazlası
|
||||
- Sürekli olarak [**`yeni özellikler ekliyoruz`**](https://feedback.projectdiscovery.io/changelog)!
|
||||
- **Şunlar için ideal:** Sızma testi yapanlar, güvenlik ekipleri ve kuruluşlar
|
||||
|
||||
Büyük bir organizasyonunuz ve karmaşık gereksinimleriniz varsa [**`Pro'ya kaydolun`**](https://projectdiscovery.io/pricing?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme) veya [**`ekibimizle konuşun`**](https://projectdiscovery.io/request-demo?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme).
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Dokümantasyon
|
||||
|
||||
Nuclei'nin tam [**`dokümantasyonuna buradan`**](https://docs.projectdiscovery.io/tools/nuclei/running) göz atın. Nuclei'de yeniyseniz, [**`temel YouTube serimize`**](https://www.youtube.com/playlist?list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl) göz atın.
|
||||
|
||||
<div align="center">
|
||||
|
||||
<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>
|
||||
|
||||
</div>
|
||||
|
||||
<br>
|
||||
|
||||
### Kurulum
|
||||
|
||||
`nuclei` yüklemek için **go >= 1.24.2** gerektirir. Repoyu almak için aşağıdaki komutu çalıştırın:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
Nuclei kurulumu hakkında daha fazla bilgi edinmek için `https://docs.projectdiscovery.io/tools/nuclei/install` adresine bakın.
|
||||
|
||||
### Komut Satırı Bayrakları
|
||||
|
||||
Aracın tüm bayraklarını görüntülemek için:
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Tüm yardım bayraklarını genişlet</summary>
|
||||
|
||||
```yaml
|
||||
Nuclei, kapsamlı yapılandırılabilirlik, devasa genişletilebilirlik ve kullanım kolaylığına odaklanan hızlı, şablon tabanlı bir zafiyet tarayıcısıdır.
|
||||
|
||||
Kullanım:
|
||||
./nuclei [bayraklar]
|
||||
|
||||
Bayraklar:
|
||||
TARGET:
|
||||
-u, -target string[] taranacak hedef URL'ler/hostlar
|
||||
-l, -list string taranacak hedef URL'leri/hostları içeren dosya yolu (her satırda bir tane)
|
||||
-eh, -exclude-hosts string[] girilen listeden tarama dışında tutulacak hostlar (ip, cidr, hostname)
|
||||
-resume string taramayı belirtilen dosyadan devam ettir ve kaydet (kümeleme devre dışı bırakılır)
|
||||
-sa, -scan-all-ips dns kaydı ile ilişkili tüm IP'leri tara
|
||||
-iv, -ip-version string[] taranacak hostun IP versiyonu (4,6) - (varsayılan 4)
|
||||
|
||||
TARGET-FORMAT:
|
||||
-im, -input-mode string girdi dosyasının modu (list, burp, jsonl, yaml, openapi, swagger) (varsayılan "list")
|
||||
-ro, -required-only istekler oluşturulurken girdi formatındaki sadece zorunlu alanları kullan
|
||||
-sfv, -skip-format-validation girdi dosyasını ayrıştırırken format doğrulamasını atla (eksik değişkenler gibi)
|
||||
|
||||
TEMPLATES:
|
||||
-nt, -new-templates sadece en son nuclei-templates sürümünde eklenen yeni şablonları çalıştır
|
||||
-ntv, -new-templates-version string[] belirli bir sürümde eklenen yeni şablonları çalıştır
|
||||
-as, -automatic-scan wappalyzer teknoloji tespiti ile etiket eşlemesini kullanarak otomatik web taraması
|
||||
-t, -templates string[] çalıştırılacak şablon veya şablon dizini listesi (virgülle ayrılmış, dosya)
|
||||
-turl, -template-url string[] çalıştırılacak şablon url'si veya şablon url'lerini içeren liste (virgülle ayrılmış, dosya)
|
||||
-ai, -prompt string yapay zeka istemi kullanarak şablon oluştur ve çalıştır
|
||||
-w, -workflows string[] çalıştırılacak iş akışı veya iş akışı dizini listesi (virgülle ayrılmış, dosya)
|
||||
-wurl, -workflow-url string[] çalıştırılacak iş akışı url'si veya iş akışı url'lerini içeren liste (virgülle ayrılmış, dosya)
|
||||
-validate nuclei'ye iletilen şablonları doğrula
|
||||
-nss, -no-strict-syntax şablonlarda katı sözdizimi kontrolünü devre dışı bırak
|
||||
-td, -template-display şablon içeriğini görüntüler
|
||||
-tl mevcut filtrelerle eşleşen tüm şablonları listele
|
||||
-tgl tüm mevcut etiketleri listele
|
||||
-sign şablonları NUCLEI_SIGNATURE_PRIVATE_KEY ortam değişkeninde tanımlanan özel anahtarla imzala
|
||||
-code kod protokolü tabanlı şablonların yüklenmesini etkinleştir
|
||||
-dut, -disable-unsigned-templates imzasız şablonların veya imzası eşleşmeyen şablonların çalıştırılmasını devre dışı bırak
|
||||
-esc, -enable-self-contained kendi kendine yeten (self-contained) şablonların yüklenmesini etkinleştir
|
||||
-egm, -enable-global-matchers global eşleştirici şablonların yüklenmesini etkinleştir
|
||||
-file dosya şablonlarının yüklenmesini etkinleştir
|
||||
|
||||
... (Diğer bayraklar orijinalindeki gibi, tam çeviri için çok uzun olabilir, ancak bağlam için yeterli)
|
||||
```
|
||||
|
||||
Ek dokümantasyon şu adreste mevcuttur: [**`docs.projectdiscovery.io/getting-started/running`**](https://docs.projectdiscovery.io/getting-started/running?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme)
|
||||
|
||||
</details>
|
||||
|
||||
### Tek hedef tarama
|
||||
|
||||
Web uygulamasında hızlı bir tarama yapmak için:
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com
|
||||
```
|
||||
|
||||
### Çoklu hedef tarama
|
||||
|
||||
Nuclei, bir hedef listesi sağlayarak toplu taramayı gerçekleştirebilir. Birden fazla URL içeren bir dosya kullanabilirsiniz.
|
||||
|
||||
```sh
|
||||
nuclei -list urls.txt
|
||||
```
|
||||
|
||||
### Ağ taraması
|
||||
|
||||
Bu, açık portlar veya yanlış yapılandırılmış servisler gibi ağla ilgili sorunlar için tüm alt ağı tarayacaktır.
|
||||
|
||||
```sh
|
||||
nuclei -target 192.168.1.0/24
|
||||
```
|
||||
|
||||
### Özel şablonunuzla tarama
|
||||
|
||||
Kendi şablonunuzu yazmak ve kullanmak için, belirli kurallara sahip bir `.yaml` dosyası oluşturun ve ardından aşağıdaki gibi kullanın.
|
||||
|
||||
```sh
|
||||
nuclei -u https://example.com -t /path/to/your-template.yaml
|
||||
```
|
||||
|
||||
### Nuclei'yi ProjectDiscovery'ye Bağlayın
|
||||
|
||||
Taramaları makinenizde çalıştırabilir ve sonuçları daha fazla analiz ve düzeltme için bulut platformuna yükleyebilirsiniz.
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com -dashboard
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Bu özellik tamamen ücretsizdir ve herhangi bir abonelik gerektirmez. Ayrıntılı bir kılavuz için [**`dokümantasyona`**](https://docs.projectdiscovery.io/cloud/scanning/nuclei-scan?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme) bakın.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Nuclei Şablonları, Topluluk ve Ödüller 💎
|
||||
[**Nuclei şablonları**](https://github.com/projectdiscovery/nuclei-templates), isteklerin nasıl gönderileceğini ve işleneceğini tanımlayan YAML tabanlı şablon dosyaları kavramına dayanır. Bu, nuclei'ye kolay genişletilebilirlik yetenekleri sağlar. Şablonlar, yürütme sürecini hızlı bir şekilde tanımlamak için insan tarafından okunabilir basit bir format belirten YAML ile yazılmıştır.
|
||||
|
||||
**[**`Buraya tıklayarak`**](https://cloud.projectdiscovery.io/templates) ücretsiz yapay zeka destekli Nuclei Şablon Editörü ile çevrimiçi deneyin.**
|
||||
|
||||
Nuclei Şablonları, önem dereceleri ve tespit yöntemleri gibi temel ayrıntıları birleştirerek güvenlik açıklarını tanımlamak ve iletmek için akıcı bir yol sunar. Bu açık kaynaklı, topluluk tarafından geliştirilen araç, tehdit yanıtını hızlandırır ve siber güvenlik dünyasında geniş çapta tanınmaktadır. Nuclei şablonları, dünya çapında binlerce güvenlik araştırmacısı tarafından aktif olarak katkıda bulunulmaktadır. Katılımcılarımız için iki program yürütüyoruz: [**`Öncüler (Pioneers)`**](https://projectdiscovery.io/pioneers) ve [**`💎 ödüller`**](https://github.com/projectdiscovery/nuclei-templates/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20Bounty%22).
|
||||
|
||||
|
||||
<p align="left">
|
||||
<a href="/static/nuclei-templates-teamcity.png" target="_blank"><img src="/static/nuclei-templates-teamcity.png" width="1200px" alt="TeamCity yanlış yapılandırmasını tespit etmek için Nuclei şablon örneği" /></a>
|
||||
</p>
|
||||
|
||||
#### Örnekler
|
||||
|
||||
Kullanım durumları ve fikirler için [**dokümantasyonumuzu**](https://docs.projectdiscovery.io/templates/introduction) ziyaret edin.
|
||||
|
||||
| Kullanım durumu | Nuclei şablonu |
|
||||
| :----------------------------------- | :------------------------------------------------- |
|
||||
| Bilinen CVE'leri tespit et | **[CVE-2021-44228 (Log4Shell)](https://cloud.projectdiscovery.io/public/CVE-2021-45046)** |
|
||||
| Bant Dışı (Out-of-Band) zafiyetlerini belirle | **[Blind SQL Injection via OOB](https://cloud.projectdiscovery.io/public/CVE-2024-22120)** |
|
||||
| SQL Injection tespiti | **[Generic SQL Injection](https://cloud.projectdiscovery.io/public/CVE-2022-34265)** |
|
||||
| Siteler Arası Komut Dosyası Çalıştırma (XSS) | **[Reflected XSS Detection](https://cloud.projectdiscovery.io/public/CVE-2023-4173)** |
|
||||
| Varsayılan veya zayıf şifreler | **[Default Credentials Check](https://cloud.projectdiscovery.io/public/airflow-default-login)** |
|
||||
| Gizli dosyalar veya veri ifşası | **[Sensitive File Disclosure](https://cloud.projectdiscovery.io/public/airflow-configuration-exposure)** |
|
||||
| Açık yönlendirmeleri (open redirects) belirle | **[Open Redirect Detection](https://cloud.projectdiscovery.io/public/open-redirect)** |
|
||||
| Alt alan adı devralmalarını (takeover) tespit et | **[Subdomain Takeover Templates](https://cloud.projectdiscovery.io/public/azure-takeover-detection)** |
|
||||
| Güvenlik yanlış yapılandırmaları | **[Unprotected Jenkins Console](https://cloud.projectdiscovery.io/public/unauthenticated-jenkins)** |
|
||||
| Zayıf SSL/TLS yapılandırmaları | **[SSL Certificate Expiry](https://cloud.projectdiscovery.io/public/expired-ssl)** |
|
||||
| Yanlış yapılandırılmış bulut hizmetleri | **[Open S3 Bucket Detection](https://cloud.projectdiscovery.io/public/s3-public-read-acp)** |
|
||||
| Uzaktan kod yürütme zafiyetleri | **[RCE Detection Templates](https://cloud.projectdiscovery.io/public/CVE-2024-29824)** |
|
||||
| Dizin geçiş (path traversal) saldırıları | **[Path Traversal Detection](https://cloud.projectdiscovery.io/public/oracle-fatwire-lfi)** |
|
||||
| Dosya dahil etme (file inclusion) zafiyetleri | **[Local/Remote File Inclusion](https://cloud.projectdiscovery.io/public/CVE-2023-6977)** |
|
||||
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Misyonumuz
|
||||
|
||||
Geleneksel zafiyet tarayıcıları on yıllar önce inşa edildi. Kapalı kaynaklıdırlar, inanılmaz derecede yavaştırlar ve satıcı odaklıdırlar. Günümüzün saldırganları, eskiden yıllar süren süreçlerin aksine, yeni yayınlanan CVE'leri günler içinde internet genelinde kitlesel olarak istismar ediyor. Bu değişim, internetteki trend olan istismarlarla mücadele etmek için tamamen farklı bir yaklaşım gerektiriyor.
|
||||
|
||||
Bu zorluğu çözmek için Nuclei'yi inşa ettik. Tüm tarama motoru çerçevesini açık ve özelleştirilebilir hale getirdik; bu sayede küresel güvenlik topluluğunun işbirliği yapmasına ve internet üzerindeki trend saldırı vektörlerini ve zafiyetlerini ele almasına olanak tanıdık. Nuclei artık Fortune 500 şirketleri, devlet kurumları ve üniversiteler tarafından kullanılmakta ve katkıda bulunulmaktadır.
|
||||
|
||||
Kodumuza, [**`şablon kitaplığımıza`**](https://github.com/projectdiscovery/nuclei-templates) katkıda bulunarak veya [**`ekibimize katılarak`**](https://projectdiscovery.io/) siz de yer alabilirsiniz.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Katkıda Bulunanlar :heart:
|
||||
|
||||
Projeyi güncel tuttukları ve [**`PR gönderdikleri için harika topluluk katkıda bulunanlara`**](https://github.com/projectdiscovery/nuclei/graphs/contributors) teşekkür ederiz. :heart:
|
||||
|
||||
(Katkıda bulunanların listesi orijinalindeki gibi korunmuştur)
|
||||
<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>
|
||||
<!-- Diğer katkıda bulunanlar buraya gelecek, görsel olarak aynı kalmalı -->
|
||||
...
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<sub>**`nuclei`** [**MIT Lisansı**](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md) altında dağıtılmaktadır.</sub>
|
||||
|
||||
</div>
|
||||
Regular → Executable
+3
-5
@@ -2066,8 +2066,6 @@ Valid values:
|
||||
|
||||
|
||||
- <code>time_delay</code>
|
||||
|
||||
- <code>xss_context</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
@@ -3220,8 +3218,8 @@ Inputs contains inputs for the network socket
|
||||
<div class="dt">
|
||||
|
||||
description: |
|
||||
Port is the port to send network requests to. this acts as default port but is overridden if target/input contains
|
||||
non-http(s) ports like 80,8080,8081 etc. Supports both numeric ports and IANA service names (e.g. ftp, ssh, smtp).
|
||||
Port is the port to send network requests to. this acts as default port but is overriden if target/input contains
|
||||
non-http(s) ports like 80,8080,8081 etc
|
||||
|
||||
</div>
|
||||
|
||||
@@ -4514,7 +4512,7 @@ permutations and combinations for all payloads.
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Payload concurrency i.e threads for sending requests.
|
||||
Payload concurreny i.e threads for sending requests.
|
||||
|
||||
|
||||
|
||||
|
||||
-65
@@ -1,65 +0,0 @@
|
||||
[files]
|
||||
extend-exclude = [
|
||||
# Non-English translations
|
||||
"README_CN.md",
|
||||
"README_ES.md",
|
||||
"README_ID.md",
|
||||
"README_JP.md",
|
||||
"README_KR.md",
|
||||
"README_PT-BR.md",
|
||||
"README_TR.md",
|
||||
# Test fixtures and data files
|
||||
"internal/tests/integration/testdata/",
|
||||
"pkg/input/formats/testdata/",
|
||||
"pkg/output/stats/waf/",
|
||||
# Deserialization test data
|
||||
"pkg/protocols/common/helpers/deserialization/testdata/",
|
||||
# Certificate/encoded data in test utilities
|
||||
"internal/tests/testutils/integration.go",
|
||||
# Vendor directory
|
||||
"vendor/",
|
||||
# Go checksum file
|
||||
"go.sum",
|
||||
]
|
||||
|
||||
[default.extend-identifiers]
|
||||
# Go identifiers that cannot be renamed without breaking API
|
||||
MisMatched = "MisMatched"
|
||||
NoopWriter = "NoopWriter"
|
||||
AllowdTypes = "AllowdTypes"
|
||||
# Deprecated alias kept for backward compatibility
|
||||
ExludedDastTmplStats = "ExludedDastTmplStats"
|
||||
|
||||
[default.extend-words]
|
||||
# External package API (fileutil.IsWriteable)
|
||||
Writeable = "Writeable" # TODO(dwisiswant0): Fix in upstream.
|
||||
# Variable name used for flow annotations
|
||||
fo = "fo"
|
||||
# Serbian test data in XML
|
||||
alo = "alo"
|
||||
# SQL test data (Spanish)
|
||||
algoritmos = "algoritmos"
|
||||
# Base64/hex encoded test data
|
||||
Noo = "Noo"
|
||||
# Certificate data fragments
|
||||
ba = "ba"
|
||||
nd = "nd"
|
||||
# Base64 encoded test data fragments
|
||||
Iif = "Iif"
|
||||
ser = "ser"
|
||||
|
||||
[type.go.extend-identifiers]
|
||||
# Short CLI flag names that appear in help strings
|
||||
ot = "ot"
|
||||
ue = "ue"
|
||||
ine = "ine"
|
||||
ines = "ines"
|
||||
hae = "hae"
|
||||
|
||||
[type.md.extend-identifiers]
|
||||
# CLI flag references in documentation
|
||||
ot = "ot"
|
||||
ue = "ue"
|
||||
ine = "ine"
|
||||
ines = "ines"
|
||||
hae = "hae"
|
||||
@@ -0,0 +1,127 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var (
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
|
||||
mainNucleiBinary = flag.String("main", "", "Main Branch Nuclei Binary")
|
||||
devNucleiBinary = flag.String("dev", "", "Dev Branch Nuclei Binary")
|
||||
testcases = flag.String("testcases", "", "Test cases file for nuclei functional tests")
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
|
||||
debug := os.Getenv("DEBUG") == "true" || os.Getenv("RUNNER_DEBUG") == "1"
|
||||
|
||||
if err, errored := runFunctionalTests(debug); err != nil {
|
||||
log.Fatalf("Could not run functional tests: %s\n", err)
|
||||
} else if errored {
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func runFunctionalTests(debug bool) (error, bool) {
|
||||
file, err := os.Open(*testcases)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open test cases"), true
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
errored, failedTestCases := runTestCases(file, debug)
|
||||
|
||||
if ci.IsCI() {
|
||||
fmt.Println("::group::Failed tests with debug")
|
||||
for _, failedTestCase := range failedTestCases {
|
||||
_ = runTestCase(failedTestCase, true)
|
||||
}
|
||||
fmt.Println("::endgroup::")
|
||||
}
|
||||
|
||||
return nil, errored
|
||||
}
|
||||
|
||||
func runTestCases(file *os.File, debug bool) (bool, []string) {
|
||||
errored := false
|
||||
var failedTestCases []string
|
||||
|
||||
scanner := bufio.NewScanner(file)
|
||||
for scanner.Scan() {
|
||||
testCase := strings.TrimSpace(scanner.Text())
|
||||
if testCase == "" {
|
||||
continue
|
||||
}
|
||||
// skip comments
|
||||
if strings.HasPrefix(testCase, "#") {
|
||||
continue
|
||||
}
|
||||
if runTestCase(testCase, debug) {
|
||||
errored = true
|
||||
failedTestCases = append(failedTestCases, testCase)
|
||||
}
|
||||
}
|
||||
return errored, failedTestCases
|
||||
}
|
||||
|
||||
func runTestCase(testCase string, debug bool) bool {
|
||||
if err := runIndividualTestCase(testCase, debug); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, testCase, err)
|
||||
return true
|
||||
} else {
|
||||
fmt.Printf("%s Test \"%s\" passed!\n", success, testCase)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func runIndividualTestCase(testcase string, debug bool) error {
|
||||
quoted := false
|
||||
|
||||
// split upon unquoted spaces
|
||||
parts := strings.FieldsFunc(testcase, func(r rune) bool {
|
||||
if r == '"' {
|
||||
quoted = !quoted
|
||||
}
|
||||
return !quoted && r == ' '
|
||||
})
|
||||
|
||||
// Quoted strings containing spaces are expressions and must have trailing \" removed
|
||||
for index, part := range parts {
|
||||
if strings.Contains(part, " ") {
|
||||
parts[index] = strings.Trim(part, "\"")
|
||||
}
|
||||
}
|
||||
|
||||
var finalArgs []string
|
||||
if len(parts) > 1 {
|
||||
finalArgs = parts[1:]
|
||||
}
|
||||
mainOutput, err := testutils.RunNucleiBinaryAndGetLoadedTemplates(*mainNucleiBinary, debug, finalArgs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not run nuclei main test")
|
||||
}
|
||||
devOutput, err := testutils.RunNucleiBinaryAndGetLoadedTemplates(*devNucleiBinary, debug, finalArgs)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not run nuclei dev test")
|
||||
}
|
||||
if mainOutput == devOutput {
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("%s main is not equal to %s dev", mainOutput, devOutput)
|
||||
}
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ "${RUNNER_OS}" == "Windows" ]; then
|
||||
EXT=".exe"
|
||||
elif [ "${RUNNER_OS}" == "macOS" ]; then
|
||||
if [ "${CI}" == "true" ]; then
|
||||
sudo sysctl -w kern.maxfiles{,perproc}=524288
|
||||
sudo launchctl limit maxfiles 65536 524288
|
||||
fi
|
||||
|
||||
ORIGINAL_ULIMIT="$(ulimit -n)"
|
||||
ulimit -n 65536 || true
|
||||
fi
|
||||
|
||||
mkdir -p .nuclei-config/nuclei/
|
||||
touch .nuclei-config/nuclei/.nuclei-ignore
|
||||
|
||||
echo "::group::Building functional-test binary"
|
||||
go build -o "functional-test${EXT}"
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Building Nuclei binary from current branch"
|
||||
go build -o "nuclei-dev${EXT}" ../nuclei
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Building latest release of nuclei"
|
||||
go build -o "nuclei${EXT}" -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Installing nuclei templates"
|
||||
eval "./nuclei-dev${EXT} -update-templates"
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Validating templates"
|
||||
eval "./nuclei-dev${EXT} -validate"
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "Starting Nuclei functional test"
|
||||
eval "./functional-test${EXT} -main ./nuclei${EXT} -dev ./nuclei-dev${EXT} -testcases testcases.txt"
|
||||
|
||||
if [ "${RUNNER_OS}" == "macOS" ]; then
|
||||
ulimit -n "${ORIGINAL_ULIMIT}" || true
|
||||
fi
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+50
-37
@@ -2,60 +2,73 @@
|
||||
{{binary}}
|
||||
|
||||
# Template tags filter
|
||||
{{binary}} -t http/cves/
|
||||
{{binary}} -t http/cves/ -t http/exposures/
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical -author geeknik,pdteam
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -exclude-templates http/cves/2017/CVE-2017-7269.yaml
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -exclude-templates http/cves/2021/
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -include-templates http/cves/2017/CVE-2017-7269.yaml
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tags config,ssrf
|
||||
{{binary}} -tags 'cve','exposure' -tags 'token','logs'
|
||||
{{binary}} -tags "cve,exposure" -author "geeknik,pdteam" -severity high,critical
|
||||
{{binary}} -tags "cve","exposure" -tags "token","logs"
|
||||
{{binary}} -tags cve
|
||||
{{binary}} -tags cve -author geeknik
|
||||
{{binary}} -tags cve -author geeknik -severity high
|
||||
{{binary}} -tags cve -author geeknik,pdteam
|
||||
{{binary}} -tags cve -etags ssrf
|
||||
{{binary}} -tags cve -exclude-tags cve2020
|
||||
{{binary}} -tags cve -exclude-templates http/cves/2020/
|
||||
{{binary}} -tags cve -exclude-templates http/cves/2020/CVE-2020-9757.yaml
|
||||
{{binary}} -tags cve -exclude-templates http/cves/2020/CVE-2020-9757.yaml -exclude-templates http/cves/2021/
|
||||
{{binary}} -tags cve -include-tags dos,fuzz
|
||||
{{binary}} -tags cve -ntv 8.8.8,8.8.9
|
||||
{{binary}} -tags cve -severity high
|
||||
{{binary}} -tags cve,dos,fuzz
|
||||
{{binary}} -tags cve
|
||||
{{binary}} -tags cve,exposure
|
||||
{{binary}} -tags cve,exposure -tags token
|
||||
{{binary}} -tags cve,exposure -tags token,logs
|
||||
{{binary}} -tags "cve","exposure" -tags "token","logs"
|
||||
{{binary}} -tags 'cve','exposure' -tags 'token','logs'
|
||||
{{binary}} -tags cve -severity high
|
||||
{{binary}} -tags cve,exposure -severity high,critical
|
||||
{{binary}} -tags cve,exposure -severity high,critical,medium
|
||||
{{binary}} -tags cve -author geeknik
|
||||
{{binary}} -tags cve -author geeknik,pdteam
|
||||
{{binary}} -tags cve -author geeknik -severity high
|
||||
{{binary}} -tags cve
|
||||
{{binary}} -tags cve,exposure
|
||||
{{binary}} -tags cve,exposure -tags token
|
||||
{{binary}} -tags cve,exposure -tags token,logs
|
||||
{{binary}} -tags "cve","exposure" -tags "token","logs"
|
||||
{{binary}} -tags 'cve','exposure' -tags 'token','logs'
|
||||
{{binary}} -tags cve -severity high
|
||||
{{binary}} -tags cve,exposure -severity high,critical
|
||||
{{binary}} -tags cve,exposure -severity high,critical,medium
|
||||
{{binary}} -tags cve -author geeknik
|
||||
{{binary}} -tags cve -author geeknik,pdteam
|
||||
{{binary}} -tags cve -author geeknik -severity high
|
||||
{{binary}} -tags cve,exposure -author geeknik,pdteam -severity high,critical
|
||||
{{binary}} -tags "cve,exposure" -author "geeknik,pdteam" -severity high,critical
|
||||
{{binary}} -tags cve -etags ssrf
|
||||
{{binary}} -tags cve,exposure -etags ssrf,config
|
||||
{{binary}} -tags cve,exposure -etags ssrf,config -severity high
|
||||
{{binary}} -tags cve,exposure -etags ssrf,config -severity high -author geeknik
|
||||
{{binary}} -tags cve,exposure -severity high,critical
|
||||
{{binary}} -tags cve,exposure -severity high,critical,medium
|
||||
{{binary}} -tags cve,exposure -tags token
|
||||
{{binary}} -tags cve,exposure -tags token,logs
|
||||
{{binary}} -tags cve,dos,fuzz
|
||||
{{binary}} -tags cve -include-tags dos,fuzz
|
||||
{{binary}} -tags cve -exclude-tags cve2020
|
||||
{{binary}} -tags cve -exclude-templates cves/2020/
|
||||
{{binary}} -tags cve -exclude-templates cves/2020/CVE-2020-9757.yaml
|
||||
{{binary}} -tags cve -exclude-templates cves/2020/CVE-2020-9757.yaml -exclude-templates cves/2021/
|
||||
{{binary}} -t cves/
|
||||
{{binary}} -t cves/ -t exposures/
|
||||
{{binary}} -t cves/ -t exposures/ -tags config
|
||||
{{binary}} -t cves/ -t exposures/ -tags config,ssrf
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical -author geeknik,pdteam
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -exclude-templates cves/2021/
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -exclude-templates cves/2017/CVE-2017-7269.yaml
|
||||
{{binary}} -t cves/ -t exposures/ -tags config -severity high,critical -author geeknik,pdteam -etags sqli -include-templates cves/2017/CVE-2017-7269.yaml
|
||||
|
||||
# Advanced Filtering
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tc contains(tags,'cve') -exclude-templates http/cves/2020/CVE-2020-9757.yaml
|
||||
{{binary}} -tags cve -author geeknik,pdteam -tc severity=='high'
|
||||
{{binary}} -tc contains(authors,'pdteam')
|
||||
{{binary}} -tc contains(description,'wordpress')
|
||||
{{binary}} -tc contains(extractor_type,'regex')
|
||||
{{binary}} -tc contains(http_method,'GET')
|
||||
{{binary}} -tc contains(matcher_type,'word')
|
||||
{{binary}} -tc len(body)>0
|
||||
{{binary}} -t cves/ -t exposures/ -tc contains(tags,'cve') -exclude-templates cves/2020/CVE-2020-9757.yaml
|
||||
{{binary}} -tc protocol=='dns'
|
||||
{{binary}} -tc contains(http_method,'GET')
|
||||
{{binary}} -tc len(body)>0
|
||||
{{binary}} -tc contains(matcher_type,'word')
|
||||
{{binary}} -tc contains(extractor_type,'regex')
|
||||
{{binary}} -tc contains(description,'wordpress')
|
||||
|
||||
# Workflow Filters
|
||||
{{binary}} -w workflows
|
||||
{{binary}} -w workflows -author geeknik,pdteam
|
||||
{{binary}} -w workflows -author geeknik,pdteam -severity high,critical
|
||||
{{binary}} -w workflows -severity high,critical
|
||||
{{binary}} -w workflows -author geeknik,pdteam -severity high,critical
|
||||
|
||||
# Input Types
|
||||
# http protocol
|
||||
# host
|
||||
{{binary}} -id tech-detect -u scanme.sh
|
||||
# host:port
|
||||
@@ -78,5 +91,5 @@
|
||||
{{binary}} -id tls-version -u scanme.sh:22
|
||||
|
||||
# Options
|
||||
# TLS Impersonate
|
||||
# Tls Impersonate
|
||||
{{binary}} -id tech-detect -tlsi -u https://scanme.sh
|
||||
@@ -1,35 +1,21 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"sync"
|
||||
"path/filepath"
|
||||
|
||||
osutils "github.com/projectdiscovery/utils/os"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var isCodeDisabled = func() bool { return osutils.IsWindows() && os.Getenv("CI") == "true" }
|
||||
var signedCodeTemplates sync.Once
|
||||
var signedCodeTemplatesErr error
|
||||
|
||||
func hasAnyExecutable(names ...string) bool {
|
||||
for _, name := range names {
|
||||
if _, err := exec.LookPath(name); err == nil {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var codeTestCases = []integrationCase{
|
||||
var codeTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/code/py-snippet.yaml", TestCase: &codeSnippet{}, DisableOn: isCodeDisabled},
|
||||
{Path: "protocols/code/py-file.yaml", TestCase: &codeFile{}, DisableOn: isCodeDisabled},
|
||||
{Path: "protocols/code/py-env-var.yaml", TestCase: &codeEnvVar{}, DisableOn: isCodeDisabled},
|
||||
@@ -38,9 +24,6 @@ var codeTestCases = []integrationCase{
|
||||
{Path: "protocols/code/py-interactsh.yaml", TestCase: &codeSnippet{}, DisableOn: isCodeDisabled},
|
||||
{Path: "protocols/code/ps1-snippet.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsWindows() || isCodeDisabled() }},
|
||||
{Path: "protocols/code/pre-condition.yaml", TestCase: &codePreCondition{}, DisableOn: isCodeDisabled},
|
||||
{Path: "protocols/code/sh-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() || !hasAnyExecutable("docker", "podman") }, Serial: true},
|
||||
{Path: "protocols/code/py-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() || !hasAnyExecutable("docker", "podman") }, Serial: true},
|
||||
{Path: "protocols/code/pwsh-echo.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return isCodeDisabled() || !hasAnyExecutable("pwsh", "powershell", "powershell.exe") }},
|
||||
}
|
||||
|
||||
const (
|
||||
@@ -50,62 +33,49 @@ const (
|
||||
|
||||
var testcertpath = ""
|
||||
|
||||
func ensureSignedCodeTemplates() error {
|
||||
func init() {
|
||||
if isCodeDisabled() {
|
||||
return nil
|
||||
// skip executing code protocol in CI on windows
|
||||
return
|
||||
}
|
||||
signedCodeTemplates.Do(func() {
|
||||
previousWD, err := os.Getwd()
|
||||
// allow local file access to load content of file references in template
|
||||
// in order to sign them for testing purposes
|
||||
templates.TemplateSignerLFA()
|
||||
|
||||
tsigner, err := signer.NewTemplateSignerFromFiles(testCertFile, testKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
testcertpath, _ = filepath.Abs(testCertFile)
|
||||
|
||||
for _, v := range codeTestCases {
|
||||
templatePath := v.Path
|
||||
testCase := v.TestCase
|
||||
|
||||
if v.DisableOn != nil && v.DisableOn() {
|
||||
// skip ps1 test case on non-windows platforms
|
||||
continue
|
||||
}
|
||||
|
||||
templatePath, err := filepath.Abs(templatePath)
|
||||
if err != nil {
|
||||
signedCodeTemplatesErr = err
|
||||
return
|
||||
}
|
||||
if err := os.Chdir(suite.fixturesDir); err != nil {
|
||||
signedCodeTemplatesErr = err
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
if chdirErr := os.Chdir(previousWD); chdirErr != nil && signedCodeTemplatesErr == nil {
|
||||
signedCodeTemplatesErr = chdirErr
|
||||
}
|
||||
}()
|
||||
|
||||
templates.TemplateSignerLFA()
|
||||
|
||||
certPath := fixturePath(testCertFile)
|
||||
keyPath := fixturePath(testKeyFile)
|
||||
testcertpath = certPath
|
||||
|
||||
tsigner, err := signer.NewTemplateSignerFromFiles(certPath, keyPath)
|
||||
if err != nil {
|
||||
signedCodeTemplatesErr = err
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
templatesToSign := []string{
|
||||
"workflow/code-template-1.yaml",
|
||||
"workflow/code-template-2.yaml",
|
||||
// skip
|
||||
// - unsigned test cases
|
||||
if _, ok := testCase.(*unsignedCode); ok {
|
||||
continue
|
||||
}
|
||||
for _, v := range codeTestCases {
|
||||
if v.DisableOn != nil && v.DisableOn() {
|
||||
continue
|
||||
}
|
||||
if _, ok := v.TestCase.(*unsignedCode); ok {
|
||||
continue
|
||||
}
|
||||
if _, ok := v.TestCase.(*codePyNoSig); ok {
|
||||
continue
|
||||
}
|
||||
templatesToSign = append(templatesToSign, v.Path)
|
||||
if _, ok := testCase.(*codePyNoSig); ok {
|
||||
continue
|
||||
}
|
||||
for _, templatePath := range templatesToSign {
|
||||
if err := templates.SignTemplate(tsigner, fixturePath(templatePath)); err != nil {
|
||||
signedCodeTemplatesErr = err
|
||||
return
|
||||
}
|
||||
if err := templates.SignTemplate(tsigner, templatePath); err != nil {
|
||||
log.Fatalf("Could not sign template %v got: %s\n", templatePath, err)
|
||||
}
|
||||
})
|
||||
return signedCodeTemplatesErr
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func getEnvValues() []string {
|
||||
@@ -0,0 +1,40 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
type customConfigDirTest struct{}
|
||||
|
||||
var customConfigDirTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &customConfigDirTest{}},
|
||||
}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *customConfigDirTest) Execute(filePath string) error {
|
||||
customTempDirectory, err := os.MkdirTemp("", "")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(customTempDirectory)
|
||||
}()
|
||||
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, []string{"NUCLEI_CONFIG_DIR=" + customTempDirectory}, "-t", filePath, "-u", "8x8exch02.8x8.com")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) == 0 {
|
||||
return nil
|
||||
}
|
||||
files, err := os.ReadDir(customTempDirectory)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var fileNames []string
|
||||
for _, file := range files {
|
||||
fileNames = append(fileNames, file.Name())
|
||||
}
|
||||
return expectResultsCount(fileNames, 4)
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var dnsTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/dns/a.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/aaaa.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/cname.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/srv.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/ns.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/txt.yaml", TestCase: &dnsBasic{}},
|
||||
{Path: "protocols/dns/ptr.yaml", TestCase: &dnsPtr{}},
|
||||
{Path: "protocols/dns/caa.yaml", TestCase: &dnsCAA{}},
|
||||
{Path: "protocols/dns/tlsa.yaml", TestCase: &dnsTLSA{}},
|
||||
{Path: "protocols/dns/variables.yaml", TestCase: &dnsVariables{}},
|
||||
{Path: "protocols/dns/payload.yaml", TestCase: &dnsPayload{}},
|
||||
{Path: "protocols/dns/dsl-matcher-variable.yaml", TestCase: &dnsDSLMatcherVariable{}},
|
||||
}
|
||||
|
||||
type dnsBasic struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsBasic) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "one.one.one.one", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type dnsPtr struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsPtr) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "1.1.1.1", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type dnsCAA struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsCAA) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "google.com", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type dnsTLSA struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsTLSA) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
|
||||
type dnsVariables struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsVariables) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "one.one.one.one", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type dnsPayload struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsPayload) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "google.com", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectResultsCount(results, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// override payload from CLI
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, "google.com", debug, "-var", "subdomain_wordlist=subdomains.txt")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 4)
|
||||
}
|
||||
|
||||
type dnsDSLMatcherVariable struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *dnsDSLMatcherVariable) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "one.one.one.one", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -9,10 +6,10 @@ import (
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var dslTestcases = []integrationCase{
|
||||
var dslTestcases = []TestCaseInfo{
|
||||
{Path: "dsl/hide-version-warning.yaml", TestCase: &dslVersionWarning{}},
|
||||
{Path: "dsl/show-version-warning.yaml", TestCase: &dslShowVersionWarning{}},
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var fileTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/file/matcher-with-or.yaml", TestCase: &fileWithOrMatcher{}},
|
||||
{Path: "protocols/file/matcher-with-and.yaml", TestCase: &fileWithAndMatcher{}},
|
||||
{Path: "protocols/file/matcher-with-nested-and.yaml", TestCase: &fileWithAndMatcher{}},
|
||||
{Path: "protocols/file/extract.yaml", TestCase: &fileWithExtractor{}},
|
||||
}
|
||||
|
||||
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, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
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, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
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, "-file")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var flowTestcases = []TestCaseInfo{
|
||||
{Path: "flow/conditional-flow.yaml", TestCase: &conditionalFlow{}},
|
||||
{Path: "flow/conditional-flow-negative.yaml", TestCase: &conditionalFlowNegative{}},
|
||||
{Path: "flow/iterate-values-flow.yaml", TestCase: &iterateValuesFlow{}},
|
||||
{Path: "flow/iterate-one-value-flow.yaml", TestCase: &iterateOneValueFlow{}},
|
||||
{Path: "flow/dns-ns-probe.yaml", TestCase: &dnsNsProbe{}},
|
||||
{Path: "flow/flow-hide-matcher.yaml", TestCase: &flowHideMatcher{}},
|
||||
}
|
||||
|
||||
type conditionalFlow struct{}
|
||||
|
||||
func (t *conditionalFlow) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "cloud.projectdiscovery.io", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type conditionalFlowNegative struct{}
|
||||
|
||||
func (t *conditionalFlowNegative) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
|
||||
type iterateValuesFlow struct{}
|
||||
|
||||
func (t *iterateValuesFlow) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
testemails := []string{
|
||||
"[email protected]",
|
||||
"[email protected]",
|
||||
}
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprint(w, testemails)
|
||||
})
|
||||
router.GET("/user/"+getBase64(testemails[0]), func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("Welcome ! This is test matcher text"))
|
||||
})
|
||||
|
||||
router.GET("/user/"+getBase64(testemails[1]), func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = w.Write([]byte("Welcome ! This is test matcher text"))
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
|
||||
type iterateOneValueFlow struct{}
|
||||
|
||||
func (t *iterateOneValueFlow) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "https://scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type dnsNsProbe struct{}
|
||||
|
||||
func (t *dnsNsProbe) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "oast.fun", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
|
||||
func getBase64(input string) string {
|
||||
return base64.StdEncoding.EncodeToString([]byte(input))
|
||||
}
|
||||
|
||||
type flowHideMatcher struct{}
|
||||
|
||||
func (t *flowHideMatcher) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// this matcher should not return any results
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -10,8 +7,8 @@ import (
|
||||
"net/url"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
)
|
||||
|
||||
@@ -19,7 +16,7 @@ const (
|
||||
targetFile = "fuzz/testData/ginandjuice.proxify.yaml"
|
||||
)
|
||||
|
||||
var fuzzingTestCases = []integrationCase{
|
||||
var fuzzingTestCases = []TestCaseInfo{
|
||||
{Path: "fuzz/fuzz-mode.yaml", TestCase: &fuzzModeOverride{}},
|
||||
{Path: "fuzz/fuzz-multi-mode.yaml", TestCase: &fuzzMultipleMode{}},
|
||||
{Path: "fuzz/fuzz-type.yaml", TestCase: &fuzzTypeOverride{}},
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
@@ -13,11 +10,11 @@ import (
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
permissionutil "github.com/projectdiscovery/utils/permission"
|
||||
)
|
||||
|
||||
var genericTestcases = []integrationCase{
|
||||
var genericTestcases = []TestCaseInfo{
|
||||
{Path: "generic/auth/certificate/http-get.yaml", TestCase: &clientCertificate{}},
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
@@ -11,10 +8,10 @@ import (
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var headlessTestcases = []integrationCase{
|
||||
var headlessTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/headless/headless-basic.yaml", TestCase: &headlessBasic{}},
|
||||
{Path: "protocols/headless/headless-waitevent.yaml", TestCase: &headlessBasic{}},
|
||||
{Path: "protocols/headless/headless-dsl.yaml", TestCase: &headlessBasic{}},
|
||||
@@ -182,8 +179,8 @@ func (h *headlessFileUpload) Execute(filePath string) error {
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
@@ -241,8 +238,8 @@ func (h *headlessFileUploadNegative) Execute(filePath string) error {
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
@@ -10,28 +7,26 @@ import (
|
||||
"net/http/httptest"
|
||||
"net/http/httputil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
logutil "github.com/projectdiscovery/utils/log"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
var httpTestcases = []integrationCase{
|
||||
var httpTestcases = []TestCaseInfo{
|
||||
// TODO: excluded due to parsing errors with console
|
||||
// "http/raw-unsafe-request.yaml": &httpRawUnsafeRequest{},
|
||||
{Path: "protocols/http/get-headers.yaml", TestCase: &httpGetHeaders{}},
|
||||
@@ -67,12 +62,9 @@ var httpTestcases = []integrationCase{
|
||||
{Path: "protocols/http/dsl-functions.yaml", TestCase: &httpDSLFunctions{}},
|
||||
{Path: "protocols/http/race-simple.yaml", TestCase: &httpRaceSimple{}},
|
||||
{Path: "protocols/http/race-multiple.yaml", TestCase: &httpRaceMultiple{}},
|
||||
{Path: "protocols/http/race-condition-with-delay.yaml", TestCase: &httpRaceWithDelay{}},
|
||||
{Path: "protocols/http/race-with-variables.yaml", TestCase: &httpRaceWithVariables{}},
|
||||
{Path: "protocols/http/stop-at-first-match.yaml", TestCase: &httpStopAtFirstMatch{}},
|
||||
{Path: "protocols/http/stop-at-first-match-with-extractors.yaml", TestCase: &httpStopAtFirstMatchWithExtractors{}},
|
||||
{Path: "protocols/http/variables.yaml", TestCase: &httpVariables{}},
|
||||
{Path: "protocols/http/variables-threads-previous.yaml", TestCase: &httpVariablesThreadsPrevious{}},
|
||||
{Path: "protocols/http/variable-dsl-function.yaml", TestCase: &httpVariableDSLFunction{}},
|
||||
{Path: "protocols/http/get-override-sni.yaml", TestCase: &httpSniAnnotation{}},
|
||||
{Path: "protocols/http/get-sni.yaml", TestCase: &customCLISNI{}},
|
||||
@@ -85,14 +77,12 @@ var httpTestcases = []integrationCase{
|
||||
{Path: "protocols/http/cl-body-without-header.yaml", TestCase: &httpCLBodyWithoutHeader{}},
|
||||
{Path: "protocols/http/cl-body-with-header.yaml", TestCase: &httpCLBodyWithHeader{}},
|
||||
{Path: "protocols/http/cli-with-constants.yaml", TestCase: &ConstantWithCliVar{}},
|
||||
{Path: "protocols/http/constants-with-threads.yaml", TestCase: &constantsWithThreads{}},
|
||||
{Path: "protocols/http/matcher-status.yaml", TestCase: &matcherStatusTest{}},
|
||||
{Path: "protocols/http/disable-path-automerge.yaml", TestCase: &httpDisablePathAutomerge{}},
|
||||
{Path: "protocols/http/http-preprocessor.yaml", TestCase: &httpPreprocessor{}},
|
||||
{Path: "protocols/http/multi-request.yaml", TestCase: &httpMultiRequest{}},
|
||||
{Path: "protocols/http/http-matcher-extractor-dy-extractor.yaml", TestCase: &httpMatcherExtractorDynamicExtractor{}},
|
||||
{Path: "protocols/http/multi-http-var-sharing.yaml", TestCase: &httpMultiVarSharing{}},
|
||||
{Path: "protocols/http/response-data-literal-reuse.yaml", TestCase: &httpResponseDataLiteralReuse{}},
|
||||
{Path: "protocols/http/raw-path-single-slash.yaml", TestCase: &httpRawPathSingleSlash{}},
|
||||
{Path: "protocols/http/raw-unsafe-path-single-slash.yaml", TestCase: &httpRawUnsafePathSingleSlash{}},
|
||||
}
|
||||
@@ -107,31 +97,6 @@ func (h *httpMultiVarSharing) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type httpResponseDataLiteralReuse struct{}
|
||||
|
||||
func (h *httpResponseDataLiteralReuse) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprint(w, `{{md5("Hello")}}`)
|
||||
})
|
||||
router.GET("/echo", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if r.URL.Query().Get("x") != `{{md5("Hello")}}` {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type httpMatcherExtractorDynamicExtractor struct{}
|
||||
|
||||
func (h *httpMatcherExtractorDynamicExtractor) Execute(filePath string) error {
|
||||
@@ -193,30 +158,6 @@ func (h *httpInteractshRequest) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 1, 2)
|
||||
}
|
||||
|
||||
type httpInteractshWithPayloadsRequest struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *httpInteractshWithPayloadsRequest) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
value := r.Header.Get("url")
|
||||
if value != "" {
|
||||
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
}
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1, 3)
|
||||
}
|
||||
|
||||
type httpDefaultMatcherCondition struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -255,7 +196,7 @@ func (d *httpDefaultMatcherCondition) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return errkit.Wrap(routerErr, "failed to send http request to interactsh server")
|
||||
return errorutil.NewWithErr(routerErr).Msgf("failed to send http request to interactsh server")
|
||||
}
|
||||
if err := expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
@@ -687,10 +628,10 @@ func (h *httpRawWithParams) Execute(filePath string) error {
|
||||
// we intentionally use params["test"] instead of params.Get("test") to test the case where
|
||||
// there are multiple parameters with the same name
|
||||
if !reflect.DeepEqual(params["key1"], []string{"value1"}) {
|
||||
errx = errkit.Append(errx, errkit.New("key1 not found in params", "expected", []string{"value1"}, "got", params["key1"]))
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value1"}, params["key1"])
|
||||
}
|
||||
if !reflect.DeepEqual(params["key2"], []string{"value2"}) {
|
||||
errx = errkit.Append(errx, errkit.New("key2 not found in params", "expected", []string{"value2"}, "got", params["key2"]))
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value2"}, params["key2"])
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "Test is test raw-params-matcher text")
|
||||
})
|
||||
@@ -801,7 +742,7 @@ func (h *httpRawUnsafePath) Execute(filepath string) error {
|
||||
// testing unsafe paths using router feedback is not possible cause they are `unsafe urls`
|
||||
// hence it is done by parsing and matching paths from nuclei output with `-debug-req` flag
|
||||
// read template files
|
||||
bin, err := os.ReadFile(fixturePath(filepath))
|
||||
bin, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -849,7 +790,7 @@ type httpPaths struct{}
|
||||
|
||||
func (h *httpPaths) Execute(filepath string) error {
|
||||
// covers testcases similar to httpRawUnsafePath but when `unsafe:false`
|
||||
bin, err := os.ReadFile(fixturePath(filepath))
|
||||
bin, err := os.ReadFile(filepath)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -999,17 +940,18 @@ func (h *httpRequestSelfContained) Execute(filePath string) error {
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = w.Write([]byte("This is self-contained response"))
|
||||
})
|
||||
server := httptest.NewServer(router)
|
||||
defer server.Close()
|
||||
|
||||
templatePath, err := tempFixturePath(filePath, map[string]string{
|
||||
"http://127.0.0.1:5431": server.URL,
|
||||
"127.0.0.1:5431": strings.TrimPrefix(server.URL, "http://"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "", debug, "-esc")
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
defer func() {
|
||||
_ = server.Close()
|
||||
}()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1029,24 +971,25 @@ func (h *httpRequestSelfContainedWithParams) Execute(filePath string) error {
|
||||
// we intentionally use params["test"] instead of params.Get("test") to test the case where
|
||||
// there are multiple parameters with the same name
|
||||
if !reflect.DeepEqual(params["something"], []string{"here"}) {
|
||||
errx = errkit.Append(errx, errkit.New("something not found in params", "expected", []string{"here"}, "got", params["something"]))
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"here"}, params["something"])
|
||||
}
|
||||
if !reflect.DeepEqual(params["key"], []string{"value"}) {
|
||||
errx = errkit.Append(errx, errkit.New("key not found in params", "expected", []string{"value"}, "got", params["key"]))
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value"}, params["key"])
|
||||
}
|
||||
_, _ = w.Write([]byte("This is self-contained response"))
|
||||
})
|
||||
server := httptest.NewServer(router)
|
||||
defer server.Close()
|
||||
|
||||
templatePath, err := tempFixturePath(filePath, map[string]string{
|
||||
"http://127.0.0.1:5431": server.URL,
|
||||
"127.0.0.1:5431": strings.TrimPrefix(server.URL, "http://"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "", debug, "-esc")
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
defer func() {
|
||||
_ = server.Close()
|
||||
}()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1070,29 +1013,30 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
gotReqToEndpoints = append(gotReqToEndpoints, "/two")
|
||||
_, _ = w.Write([]byte("This is self-contained response"))
|
||||
})
|
||||
server := httptest.NewServer(router)
|
||||
defer server.Close()
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
defer func() {
|
||||
_ = server.Close()
|
||||
}()
|
||||
|
||||
// create temp file
|
||||
FileLoc, err := os.CreateTemp("", "self-contained-payload-*.txt")
|
||||
if err != nil {
|
||||
return errkit.Wrap(err, "failed to create temp file")
|
||||
return errorutil.NewWithErr(err).Msgf("failed to create temp file")
|
||||
}
|
||||
if _, err := FileLoc.Write([]byte("one\ntwo\n")); err != nil {
|
||||
return errkit.Wrap(err, "failed to write payload to temp file")
|
||||
return errorutil.NewWithErr(err).Msgf("failed to write payload to temp file")
|
||||
}
|
||||
defer func() {
|
||||
_ = FileLoc.Close()
|
||||
}()
|
||||
_ = FileLoc.Close()
|
||||
}()
|
||||
|
||||
templatePath, err := tempFixturePath(filePath, map[string]string{
|
||||
"http://127.0.0.1:5431": server.URL,
|
||||
"127.0.0.1:5431": strings.TrimPrefix(server.URL, "http://"),
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "", debug, "-V", "test="+FileLoc.Name(), "-esc")
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-V", "test="+FileLoc.Name(), "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1102,31 +1046,11 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
}
|
||||
|
||||
if !sliceutil.ElementsMatch(gotReqToEndpoints, []string{"/one", "/two", "/one", "/two"}) {
|
||||
return errkit.New("expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints, "filePath", filePath)
|
||||
return errorutil.NewWithTag(filePath, "expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func tempFixturePath(relativePath string, replacements map[string]string) (string, error) {
|
||||
data, err := os.ReadFile(fixturePath(relativePath))
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read fixture %s: %w", relativePath, err)
|
||||
}
|
||||
content := string(data)
|
||||
for oldValue, newValue := range replacements {
|
||||
content = strings.ReplaceAll(content, oldValue, newValue)
|
||||
}
|
||||
tempDir, err := os.MkdirTemp(suite.tempDir, "http-self-contained-*")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create temp fixture dir: %w", err)
|
||||
}
|
||||
tempPath := filepath.Join(tempDir, filepath.Base(relativePath))
|
||||
if err := os.WriteFile(tempPath, []byte(content), 0600); err != nil {
|
||||
return "", fmt.Errorf("failed to write temp fixture %s: %w", relativePath, err)
|
||||
}
|
||||
return tempPath, nil
|
||||
}
|
||||
|
||||
type httpGetCaseInsensitive struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -1229,71 +1153,6 @@ func (h *httpRaceMultiple) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 5)
|
||||
}
|
||||
|
||||
type httpRaceWithDelay struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *httpRaceWithDelay) Execute(filePath string) error {
|
||||
var requestTimes []time.Time
|
||||
var mu sync.Mutex
|
||||
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
mu.Lock()
|
||||
requestTimes = append(requestTimes, time.Now())
|
||||
mu.Unlock()
|
||||
time.Sleep(2 * time.Second)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectResultsCount(results, 3); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
mu.Lock()
|
||||
defer mu.Unlock()
|
||||
if len(requestTimes) != 3 {
|
||||
return fmt.Errorf("expected 3 requests, got %d", len(requestTimes))
|
||||
}
|
||||
|
||||
// Check concurrency of first two requests (should be very close)
|
||||
if diff := requestTimes[1].Sub(requestTimes[0]); diff > 500*time.Millisecond {
|
||||
return fmt.Errorf("expected first 2 requests to be concurrent, diff: %v", diff)
|
||||
}
|
||||
|
||||
// Check delay of third request (should be after ~2s)
|
||||
if diff := requestTimes[2].Sub(requestTimes[0]); diff < 1500*time.Millisecond {
|
||||
return fmt.Errorf("expected 3rd request to be delayed, diff: %v", diff)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpRaceWithVariables struct{}
|
||||
|
||||
// Execute tests that variables and constants are properly resolved in race mode.
|
||||
func (h *httpRaceWithVariables) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/race", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
// Echo back the API key header so we can match on it
|
||||
_, _ = fmt.Fprint(w, r.Header.Get("X-API-Key"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 3)
|
||||
}
|
||||
|
||||
type httpStopAtFirstMatch struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -1361,30 +1220,6 @@ func (h *httpVariables) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
|
||||
type httpVariablesThreadsPrevious struct{}
|
||||
|
||||
// Execute tests that variables can reference data extracted from previous requests
|
||||
// when using threads mode (parallel execution).
|
||||
func (h *httpVariablesThreadsPrevious) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/login", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprint(w, "token=secret123")
|
||||
})
|
||||
router.GET("/api", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
// Echo back the Authorization header so we can match on it
|
||||
_, _ = fmt.Fprint(w, r.Header.Get("Authorization"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type httpVariableDSLFunction struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -1631,26 +1466,6 @@ func (h *ConstantWithCliVar) Execute(filePath string) error {
|
||||
return expectResultsCount(got, 1)
|
||||
}
|
||||
|
||||
type constantsWithThreads struct{}
|
||||
|
||||
// Execute tests that constants are properly resolved when using threads mode.
|
||||
func (h *constantsWithThreads) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/api/:version", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
|
||||
// Echo back the API key header and version so we can match on them
|
||||
_, _ = fmt.Fprintf(w, "%s %s", r.Header.Get("X-API-Key"), p.ByName("version"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type matcherStatusTest struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -0,0 +1,239 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
"github.com/logrusorgru/aurora"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils/fuzzplayground"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
type TestCaseInfo struct {
|
||||
Path string
|
||||
TestCase testutils.TestCase
|
||||
DisableOn func() bool
|
||||
}
|
||||
|
||||
var (
|
||||
debug = os.Getenv("DEBUG") == "true"
|
||||
customTests = os.Getenv("TESTS")
|
||||
protocol = os.Getenv("PROTO")
|
||||
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
|
||||
protocolTests = map[string][]TestCaseInfo{
|
||||
"http": httpTestcases,
|
||||
"interactsh": interactshTestCases,
|
||||
"network": networkTestcases,
|
||||
"dns": dnsTestCases,
|
||||
"workflow": workflowTestcases,
|
||||
"loader": loaderTestcases,
|
||||
"profile-loader": profileLoaderTestcases,
|
||||
"websocket": websocketTestCases,
|
||||
"headless": headlessTestcases,
|
||||
"whois": whoisTestCases,
|
||||
"ssl": sslTestcases,
|
||||
"library": libraryTestcases,
|
||||
"templatesPath": templatesPathTestCases,
|
||||
"templatesDir": templatesDirTestCases,
|
||||
"file": fileTestcases,
|
||||
"offlineHttp": offlineHttpTestcases,
|
||||
"customConfigDir": customConfigDirTestCases,
|
||||
"fuzzing": fuzzingTestCases,
|
||||
"code": codeTestCases,
|
||||
"multi": multiProtoTestcases,
|
||||
"generic": genericTestcases,
|
||||
"dsl": dslTestcases,
|
||||
"flow": flowTestcases,
|
||||
"javascript": jsTestcases,
|
||||
"matcher-status": matcherStatusTestcases,
|
||||
}
|
||||
// flakyTests are run with a retry count of 3
|
||||
flakyTests = map[string]bool{
|
||||
"protocols/http/self-contained-file-input.yaml": true,
|
||||
}
|
||||
|
||||
// For debug purposes
|
||||
runProtocol = ""
|
||||
runTemplate = ""
|
||||
extraArgs = []string{}
|
||||
interactshRetryCount = 3
|
||||
)
|
||||
|
||||
func main() {
|
||||
flag.StringVar(&runProtocol, "protocol", "", "run integration tests of given protocol")
|
||||
flag.StringVar(&runTemplate, "template", "", "run integration test of given template")
|
||||
flag.Parse()
|
||||
|
||||
// allows passing extra args to nuclei
|
||||
eargs := os.Getenv("DebugExtraArgs")
|
||||
if eargs != "" {
|
||||
extraArgs = strings.Split(eargs, " ")
|
||||
testutils.ExtraDebugArgs = extraArgs
|
||||
}
|
||||
|
||||
if runProtocol != "" {
|
||||
debugTests()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// start fuzz playground server
|
||||
defer fuzzplayground.Cleanup()
|
||||
server := fuzzplayground.GetPlaygroundServer()
|
||||
defer func() {
|
||||
_ = server.Close()
|
||||
}()
|
||||
go func() {
|
||||
if err := server.Start("localhost:8082"); err != nil {
|
||||
if !strings.Contains(err.Error(), "Server closed") {
|
||||
gologger.Fatal().Msgf("Could not start server: %s\n", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
customTestsList := normalizeSplit(customTests)
|
||||
|
||||
failedTestTemplatePaths := runTests(customTestsList)
|
||||
|
||||
if len(failedTestTemplatePaths) > 0 {
|
||||
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::")
|
||||
} else {
|
||||
fmt.Println("::group::All tests passed")
|
||||
fmt.Println("::endgroup::")
|
||||
os.Exit(0)
|
||||
}
|
||||
}
|
||||
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// execute a testcase with retry and consider best of N
|
||||
// intended for flaky tests like interactsh
|
||||
func executeWithRetry(testCase testutils.TestCase, templatePath string, retryCount int) (string, error) {
|
||||
var err error
|
||||
for i := 0; i < retryCount; i++ {
|
||||
err = testCase.Execute(templatePath)
|
||||
if err == nil {
|
||||
fmt.Printf("%s Test \"%s\" passed!\n", success, templatePath)
|
||||
return "", nil
|
||||
}
|
||||
}
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed after %v attempts : %s\n", failed, templatePath, retryCount, err)
|
||||
return templatePath, err
|
||||
}
|
||||
|
||||
func debugTests() {
|
||||
testCaseInfos := protocolTests[runProtocol]
|
||||
for _, testCaseInfo := range testCaseInfos {
|
||||
if (runTemplate != "" && !strings.Contains(testCaseInfo.Path, runTemplate)) ||
|
||||
(testCaseInfo.DisableOn != nil && testCaseInfo.DisableOn()) {
|
||||
continue
|
||||
}
|
||||
if runProtocol == "interactsh" {
|
||||
if _, err := executeWithRetry(testCaseInfo.TestCase, testCaseInfo.Path, interactshRetryCount); err != nil {
|
||||
fmt.Printf("\n%v", err.Error())
|
||||
}
|
||||
} else {
|
||||
if _, err := execute(testCaseInfo.TestCase, testCaseInfo.Path); err != nil {
|
||||
fmt.Printf("\n%v", err.Error())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func runTests(customTemplatePaths []string) []string {
|
||||
var failedTestTemplatePaths []string
|
||||
|
||||
for proto, testCaseInfos := range protocolTests {
|
||||
if protocol != "" {
|
||||
if !strings.EqualFold(proto, protocol) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if len(customTemplatePaths) == 0 {
|
||||
fmt.Printf("Running test cases for %q protocol\n", aurora.Blue(proto))
|
||||
}
|
||||
for _, testCaseInfo := range testCaseInfos {
|
||||
if testCaseInfo.DisableOn != nil && testCaseInfo.DisableOn() {
|
||||
fmt.Printf("skipping test case %v. disabled on %v.\n", aurora.Blue(testCaseInfo.Path), runtime.GOOS)
|
||||
continue
|
||||
}
|
||||
if len(customTemplatePaths) == 0 || sliceutil.Contains(customTemplatePaths, testCaseInfo.Path) {
|
||||
var failedTemplatePath string
|
||||
var err error
|
||||
if proto == "interactsh" || strings.Contains(testCaseInfo.Path, "interactsh") {
|
||||
failedTemplatePath, err = executeWithRetry(testCaseInfo.TestCase, testCaseInfo.Path, interactshRetryCount)
|
||||
} else if flakyTests[testCaseInfo.Path] {
|
||||
failedTemplatePath, err = executeWithRetry(testCaseInfo.TestCase, testCaseInfo.Path, interactshRetryCount)
|
||||
} else {
|
||||
failedTemplatePath, err = execute(testCaseInfo.TestCase, testCaseInfo.Path)
|
||||
}
|
||||
if err != nil {
|
||||
failedTestTemplatePaths = append(failedTestTemplatePaths, failedTemplatePath)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return failedTestTemplatePaths
|
||||
}
|
||||
|
||||
func execute(testCase testutils.TestCase, templatePath string) (string, error) {
|
||||
if err := testCase.Execute(templatePath); err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "%s Test \"%s\" failed: %s\n", failed, templatePath, err)
|
||||
return templatePath, err
|
||||
}
|
||||
|
||||
fmt.Printf("%s Test \"%s\" passed!\n", success, templatePath)
|
||||
return "", nil
|
||||
}
|
||||
|
||||
func expectResultsCount(results []string, expectedNumbers ...int) error {
|
||||
results = filterHeadlessLogs(results)
|
||||
match := sliceutil.Contains(expectedNumbers, len(results))
|
||||
if !match {
|
||||
return fmt.Errorf("incorrect number of results: %d (actual) vs %v (expected) \nResults:\n\t%s\n", len(results), expectedNumbers, strings.Join(results, "\n\t")) // nolint:all
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func normalizeSplit(str string) []string {
|
||||
return strings.FieldsFunc(str, func(r rune) bool {
|
||||
return r == ','
|
||||
})
|
||||
}
|
||||
|
||||
// if chromium is not installed go-rod installs it in .cache directory
|
||||
// this function filters out the logs from download and installation
|
||||
func filterHeadlessLogs(results []string) []string {
|
||||
// [launcher.Browser] 2021/09/23 15:24:05 [launcher] [info] Starting browser
|
||||
filtered := []string{}
|
||||
for _, result := range results {
|
||||
if strings.Contains(result, "[launcher.Browser]") {
|
||||
continue
|
||||
}
|
||||
filtered = append(filtered, result)
|
||||
}
|
||||
return filtered
|
||||
}
|
||||
@@ -1,14 +1,10 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import osutils "github.com/projectdiscovery/utils/os"
|
||||
|
||||
// All Interactsh related testcases
|
||||
var interactshTestCases = []integrationCase{
|
||||
var interactshTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/http/interactsh.yaml", TestCase: &httpInteractshRequest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
|
||||
{Path: "protocols/http/interactsh-with-payloads.yaml", TestCase: &httpInteractshWithPayloadsRequest{}, DisableOn: func() bool { return true }},
|
||||
{Path: "protocols/http/interactsh-stop-at-first-match.yaml", TestCase: &httpInteractshStopAtFirstMatchRequest{}, DisableOn: func() bool { return true }}, // disable this test for now
|
||||
{Path: "protocols/http/default-matcher-condition.yaml", TestCase: &httpDefaultMatcherCondition{}, DisableOn: func() bool { return true }},
|
||||
{Path: "protocols/http/interactsh-requests-mc-and.yaml", TestCase: &httpInteractshRequestsWithMCAnd{}},
|
||||
@@ -0,0 +1,166 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
osutils "github.com/projectdiscovery/utils/os"
|
||||
"go.uber.org/multierr"
|
||||
)
|
||||
|
||||
var jsTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/javascript/redis-pass-brute.yaml", TestCase: &javascriptRedisPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
|
||||
{Path: "protocols/javascript/ssh-server-fingerprint.yaml", TestCase: &javascriptSSHServerFingerprint{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
|
||||
{Path: "protocols/javascript/net-multi-step.yaml", TestCase: &networkMultiStep{}},
|
||||
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
|
||||
}
|
||||
|
||||
var (
|
||||
redisResource *dockertest.Resource
|
||||
sshResource *dockertest.Resource
|
||||
pool *dockertest.Pool
|
||||
defaultRetry = 3
|
||||
)
|
||||
|
||||
type javascriptNetHttps struct{}
|
||||
|
||||
func (j *javascriptNetHttps) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type javascriptRedisPassBrute struct{}
|
||||
|
||||
func (j *javascriptRedisPassBrute) Execute(filePath string) error {
|
||||
if redisResource == nil || pool == nil {
|
||||
// skip test as redis is not running
|
||||
return nil
|
||||
}
|
||||
tempPort := redisResource.GetPort("6379/tcp")
|
||||
finalURL := "localhost:" + tempPort
|
||||
defer purge(redisResource)
|
||||
errs := []error{}
|
||||
for i := 0; i < defaultRetry; i++ {
|
||||
results := []string{}
|
||||
var err error
|
||||
_ = pool.Retry(func() error {
|
||||
//let ssh server start
|
||||
time.Sleep(3 * time.Second)
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectResultsCount(results, 1); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
type javascriptSSHServerFingerprint struct{}
|
||||
|
||||
func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
|
||||
if sshResource == nil || pool == nil {
|
||||
// skip test as redis is not running
|
||||
return nil
|
||||
}
|
||||
tempPort := sshResource.GetPort("2222/tcp")
|
||||
finalURL := "localhost:" + tempPort
|
||||
defer purge(sshResource)
|
||||
errs := []error{}
|
||||
for i := 0; i < defaultRetry; i++ {
|
||||
results := []string{}
|
||||
var err error
|
||||
_ = pool.Retry(func() error {
|
||||
//let ssh server start
|
||||
time.Sleep(3 * time.Second)
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := expectResultsCount(results, 1); err == nil {
|
||||
return nil
|
||||
} else {
|
||||
errs = append(errs, err)
|
||||
}
|
||||
}
|
||||
return multierr.Combine(errs...)
|
||||
}
|
||||
|
||||
// purge any given resource if it is not nil
|
||||
func purge(resource *dockertest.Resource) {
|
||||
if resource != nil && pool != nil {
|
||||
containerName := resource.Container.Name
|
||||
_ = pool.Client.StopContainer(resource.Container.ID, 0)
|
||||
err := pool.Purge(resource)
|
||||
if err != nil {
|
||||
log.Printf("Could not purge resource: %s", err)
|
||||
}
|
||||
_ = pool.RemoveContainerByName(containerName)
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// uses a sensible default on windows (tcp/http) and linux/osx (socket)
|
||||
pool, err := dockertest.NewPool("")
|
||||
if err != nil {
|
||||
log.Printf("something went wrong with dockertest: %s", err)
|
||||
return
|
||||
}
|
||||
|
||||
// uses pool to try to connect to Docker
|
||||
err = pool.Client.Ping()
|
||||
if err != nil {
|
||||
log.Printf("Could not connect to Docker: %s", err)
|
||||
}
|
||||
|
||||
// setup a temporary redis instance
|
||||
redisResource, err = pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: "redis",
|
||||
Tag: "latest",
|
||||
Cmd: []string{"redis-server", "--requirepass", "iamadmin"},
|
||||
Platform: "linux/amd64",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Could not start resource: %s", err)
|
||||
return
|
||||
}
|
||||
// by default expire after 30 sec
|
||||
if err := redisResource.Expire(30); err != nil {
|
||||
log.Printf("Could not expire resource: %s", err)
|
||||
}
|
||||
|
||||
// setup a temporary ssh server
|
||||
sshResource, err = pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Repository: "lscr.io/linuxserver/openssh-server",
|
||||
Tag: "latest",
|
||||
Env: []string{
|
||||
"PUID=1000",
|
||||
"PGID=1000",
|
||||
"TZ=Etc/UTC",
|
||||
"PASSWORD_ACCESS=true",
|
||||
"USER_NAME=admin",
|
||||
"USER_PASSWORD=admin",
|
||||
},
|
||||
Platform: "linux/amd64",
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Could not start resource: %s", err)
|
||||
return
|
||||
}
|
||||
// by default expire after 30 sec
|
||||
if err := sshResource.Expire(30); err != nil {
|
||||
log.Printf("Could not expire resource: %s", err)
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,4 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
@@ -9,7 +6,8 @@ import (
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"path/filepath"
|
||||
"os"
|
||||
"path"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -17,8 +15,6 @@ import (
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/goflags"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
|
||||
@@ -33,11 +29,12 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
)
|
||||
|
||||
var libraryTestcases = []integrationCase{
|
||||
var libraryTestcases = []TestCaseInfo{
|
||||
{Path: "library/test.yaml", TestCase: &goIntegrationTest{}},
|
||||
{Path: "library/test.json", TestCase: &goIntegrationTest{}},
|
||||
}
|
||||
@@ -71,27 +68,18 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
cache := hosterrorscache.New(30, hosterrorscache.DefaultMaxHostsCount, nil)
|
||||
defer cache.Close()
|
||||
|
||||
defaultOpts := types.DefaultOptions()
|
||||
defaultOpts.ExecutionId = "test"
|
||||
defaultOpts.Logger = gologger.DefaultLogger
|
||||
|
||||
mockProgress := &testutils.MockProgressClient{}
|
||||
reportingClient, err := reporting.New(&reporting.Options{ExecutionId: defaultOpts.ExecutionId}, "", false)
|
||||
reportingClient, err := reporting.New(&reporting.Options{}, "", false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer reportingClient.Close()
|
||||
|
||||
defaultOpts := types.DefaultOptions()
|
||||
_ = protocolstate.Init(defaultOpts)
|
||||
_ = protocolinit.Init(defaultOpts)
|
||||
|
||||
defer protocolstate.Close(defaultOpts.ExecutionId)
|
||||
|
||||
actualTemplatePath := templatePath
|
||||
if !filepath.IsAbs(actualTemplatePath) {
|
||||
actualTemplatePath = fixturePath(actualTemplatePath)
|
||||
}
|
||||
defaultOpts.Templates = goflags.StringSlice{actualTemplatePath}
|
||||
defaultOpts.Templates = goflags.StringSlice{templatePath}
|
||||
defaultOpts.ExcludeTags = config.ReadIgnoreFile().Tags
|
||||
|
||||
outputWriter := testutils.NewMockOutputWriter(defaultOpts.OmitTemplate)
|
||||
@@ -107,11 +95,12 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
}
|
||||
defer interactClient.Close()
|
||||
|
||||
catalog := disk.NewCatalog(suite.fixturesDir)
|
||||
home, _ := os.UserHomeDir()
|
||||
catalog := disk.NewCatalog(path.Join(home, "nuclei-templates"))
|
||||
ratelimiter := ratelimit.New(context.Background(), 150, time.Second)
|
||||
defer ratelimiter.Stop()
|
||||
|
||||
executerOpts := &protocols.ExecutorOptions{
|
||||
executerOpts := protocols.ExecutorOptions{
|
||||
Output: outputWriter,
|
||||
Options: defaultOpts,
|
||||
Progress: mockProgress,
|
||||
@@ -127,7 +116,7 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
engine := core.New(defaultOpts)
|
||||
engine.SetExecuterOptions(executerOpts)
|
||||
|
||||
workflowLoader, err := parsers.NewLoader(executerOpts)
|
||||
workflowLoader, err := parsers.NewLoader(&executerOpts)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create workflow loader: %s\n", err)
|
||||
}
|
||||
@@ -137,11 +126,9 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create loader")
|
||||
}
|
||||
if err := store.Load(); err != nil {
|
||||
return nil, errors.Wrap(err, "could not load templates")
|
||||
}
|
||||
store.Load()
|
||||
|
||||
_ = engine.Execute(context.Background(), store.Templates(), provider.NewSimpleInputProviderWithUrls(defaultOpts.ExecutionId, templateURL))
|
||||
_ = engine.Execute(context.Background(), store.Templates(), provider.NewSimpleInputProviderWithUrls(templateURL))
|
||||
engine.WorkPool().Wait() // Wait for the scan to finish
|
||||
|
||||
return results, nil
|
||||
@@ -0,0 +1,229 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
permissionutil "github.com/projectdiscovery/utils/permission"
|
||||
)
|
||||
|
||||
var loaderTestcases = []TestCaseInfo{
|
||||
{Path: "loader/template-list.yaml", TestCase: &remoteTemplateList{}},
|
||||
{Path: "loader/workflow-list.yaml", TestCase: &remoteWorkflowList{}},
|
||||
{Path: "loader/excluded-template.yaml", TestCase: &excludedTemplate{}},
|
||||
{Path: "loader/nonexistent-template-list.yaml", TestCase: &nonExistentTemplateList{}},
|
||||
{Path: "loader/nonexistent-workflow-list.yaml", TestCase: &nonExistentWorkflowList{}},
|
||||
{Path: "loader/template-list-not-allowed.yaml", TestCase: &remoteTemplateListNotAllowed{}},
|
||||
{Path: "loader/load-template-with-id", TestCase: &loadTemplateWithID{}},
|
||||
}
|
||||
|
||||
type remoteTemplateList struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *remoteTemplateList) Execute(templateList string) error {
|
||||
router := httprouter.New()
|
||||
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/template_list", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
file, err := os.ReadFile(templateList)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
_, err = w.Write(file)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]`
|
||||
err := os.WriteFile("test-config.yaml", []byte(configFileData), permissionutil.ConfigFilePermission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove("test-config.yaml")
|
||||
}()
|
||||
|
||||
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-template-url", ts.URL+"/template_list", "-config", "test-config.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
|
||||
type excludedTemplate struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *excludedTemplate) Execute(templateList string) error {
|
||||
router := httprouter.New()
|
||||
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-t", templateList, "-include-templates", templateList)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type remoteTemplateListNotAllowed struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *remoteTemplateListNotAllowed) Execute(templateList string) error {
|
||||
router := httprouter.New()
|
||||
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/template_list", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
file, err := os.ReadFile(templateList)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
_, err = w.Write(file)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
_, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-template-url", ts.URL+"/template_list")
|
||||
if err == nil {
|
||||
return fmt.Errorf("expected error for not allowed remote template list url")
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
||||
type remoteWorkflowList struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *remoteWorkflowList) Execute(workflowList string) error {
|
||||
router := httprouter.New()
|
||||
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
|
||||
router.GET("/workflow_list", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
file, err := os.ReadFile(workflowList)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
_, err = w.Write(file)
|
||||
if err != nil {
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]`
|
||||
err := os.WriteFile("test-config.yaml", []byte(configFileData), permissionutil.ConfigFilePermission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove("test-config.yaml")
|
||||
}()
|
||||
|
||||
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-workflow-url", ts.URL+"/workflow_list", "-config", "test-config.yaml")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 3)
|
||||
}
|
||||
|
||||
type nonExistentTemplateList struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *nonExistentTemplateList) Execute(nonExistingTemplateList string) error {
|
||||
router := httprouter.New()
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]`
|
||||
err := os.WriteFile("test-config.yaml", []byte(configFileData), permissionutil.ConfigFilePermission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove("test-config.yaml")
|
||||
}()
|
||||
|
||||
_, err = testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-template-url", ts.URL+"/404", "-config", "test-config.yaml")
|
||||
if err == nil {
|
||||
return fmt.Errorf("expected error for nonexisting workflow url")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type nonExistentWorkflowList struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *nonExistentWorkflowList) Execute(nonExistingWorkflowList string) error {
|
||||
router := httprouter.New()
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
configFileData := `remote-template-domain: [ "` + ts.Listener.Addr().String() + `" ]`
|
||||
err := os.WriteFile("test-config.yaml", []byte(configFileData), permissionutil.ConfigFilePermission)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove("test-config.yaml")
|
||||
}()
|
||||
|
||||
_, err = testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-workflow-url", ts.URL+"/404", "-config", "test-config.yaml")
|
||||
if err == nil {
|
||||
return fmt.Errorf("expected error for nonexisting workflow url")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type loadTemplateWithID struct{}
|
||||
|
||||
func (h *loadTemplateWithID) Execute(nooop string) error {
|
||||
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", "scanme.sh", "-id", "self-signed-ssl")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
+3
-33
@@ -1,28 +1,21 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
)
|
||||
|
||||
var matcherStatusTestcases = []integrationCase{
|
||||
var matcherStatusTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/http/get.yaml", TestCase: &httpNoAccess{}},
|
||||
{Path: "protocols/network/net-https.yaml", TestCase: &networkNoAccess{}},
|
||||
{Path: "protocols/headless/headless-basic.yaml", TestCase: &headlessNoAccess{}},
|
||||
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNoAccess{}},
|
||||
{Path: "protocols/websocket/basic.yaml", TestCase: &websocketNoAccess{}},
|
||||
{Path: "protocols/dns/a.yaml", TestCase: &dnsNoAccess{}},
|
||||
{Path: "protocols/http/matcher-status-and.yaml,protocols/http/matcher-status-and-cluster.yaml", TestCase: &httpMatcherStatusAnd{}},
|
||||
}
|
||||
|
||||
type httpNoAccess struct{}
|
||||
@@ -125,26 +118,3 @@ func (h *dnsNoAccess) Execute(filePath string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type httpMatcherStatusAnd struct{}
|
||||
|
||||
// Execute verifies that clustered templates with matchers-condition: and
|
||||
// produce failure events when -matcher-status is enabled.
|
||||
func (h *httpMatcherStatusAnd) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = w.Write([]byte("ok"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
files := strings.Split(filePath, ",")
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(files[0], ts.URL, debug, "-t", files[1], "-ms", "-j")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(results) != 2 {
|
||||
return fmt.Errorf("unexpected number of results: %d (expected 2)", len(results))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var multiProtoTestcases = []integrationCase{
|
||||
var multiProtoTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/multi/dynamic-values.yaml", TestCase: &multiProtoDynamicExtractor{}},
|
||||
{Path: "protocols/multi/evaluate-variables.yaml", TestCase: &multiProtoDynamicExtractor{}},
|
||||
{Path: "protocols/multi/exported-response-vars.yaml", TestCase: &multiProtoDynamicExtractor{}},
|
||||
@@ -0,0 +1,241 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
osutils "github.com/projectdiscovery/utils/os"
|
||||
"github.com/projectdiscovery/utils/reader"
|
||||
)
|
||||
|
||||
var networkTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/network/basic.yaml", TestCase: &networkBasic{}, DisableOn: func() bool { return osutils.IsWindows() }},
|
||||
{Path: "protocols/network/hex.yaml", TestCase: &networkBasic{}, DisableOn: func() bool { return osutils.IsWindows() }},
|
||||
{Path: "protocols/network/multi-step.yaml", TestCase: &networkMultiStep{}},
|
||||
{Path: "protocols/network/self-contained.yaml", TestCase: &networkRequestSelContained{}},
|
||||
{Path: "protocols/network/variables.yaml", TestCase: &networkVariables{}},
|
||||
{Path: "protocols/network/same-address.yaml", TestCase: &networkBasic{}},
|
||||
{Path: "protocols/network/network-port.yaml", TestCase: &networkPort{}},
|
||||
{Path: "protocols/network/net-https.yaml", TestCase: &networkhttps{}},
|
||||
{Path: "protocols/network/net-https-timeout.yaml", TestCase: &networkhttps{}},
|
||||
}
|
||||
|
||||
const defaultStaticPort = 5431
|
||||
|
||||
type networkBasic struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *networkBasic) Execute(filePath string) error {
|
||||
var routerErr error
|
||||
|
||||
ts := testutils.NewTCPServer(nil, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
data, err := reader.ConnReadNWithTimeout(conn, 4, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if string(data) == "PING" {
|
||||
_, _ = conn.Write([]byte("PONG"))
|
||||
} else {
|
||||
routerErr = fmt.Errorf("invalid data received: %s", string(data))
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "Could not run nuclei: %s\n", err)
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
_, _ = fmt.Fprintf(os.Stderr, "routerErr: %s\n", routerErr)
|
||||
return routerErr
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type networkMultiStep struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *networkMultiStep) Execute(filePath string) error {
|
||||
var routerErr error
|
||||
|
||||
ts := testutils.NewTCPServer(nil, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
data, err := reader.ConnReadNWithTimeout(conn, 5, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if string(data) == "FIRST" {
|
||||
_, _ = conn.Write([]byte("PING"))
|
||||
}
|
||||
|
||||
data, err = reader.ConnReadNWithTimeout(conn, 6, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if string(data) == "SECOND" {
|
||||
_, _ = conn.Write([]byte("PONG"))
|
||||
}
|
||||
_, _ = conn.Write([]byte("NUCLEI"))
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
|
||||
var expectedResultsSize int
|
||||
if debug {
|
||||
expectedResultsSize = 3
|
||||
} else {
|
||||
expectedResultsSize = 1
|
||||
}
|
||||
|
||||
return expectResultsCount(results, expectedResultsSize)
|
||||
}
|
||||
|
||||
type networkRequestSelContained struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *networkRequestSelContained) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(nil, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
_, _ = conn.Write([]byte("Authentication successful"))
|
||||
})
|
||||
defer ts.Close()
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug, "-esc")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type networkVariables struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *networkVariables) Execute(filePath string) error {
|
||||
var routerErr error
|
||||
|
||||
ts := testutils.NewTCPServer(nil, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
data, err := reader.ConnReadNWithTimeout(conn, 4, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if string(data) == "PING" {
|
||||
_, _ = conn.Write([]byte("aGVsbG8="))
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return routerErr
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type networkPort struct{}
|
||||
|
||||
func (n *networkPort) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(nil, 23846, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
data, err := reader.ConnReadNWithTimeout(conn, 4, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if string(data) == "PING" {
|
||||
_, _ = conn.Write([]byte("PONG"))
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// even though we passed port 443 in url it is ignored and port 23846 is used
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "23846", "443"), debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// this is positive test case where we expect port to be overridden and 34567 to be used
|
||||
ts2 := testutils.NewTCPServer(nil, 34567, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
|
||||
data, err := reader.ConnReadNWithTimeout(conn, 4, time.Duration(5)*time.Second)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if string(data) == "PING" {
|
||||
_, _ = conn.Write([]byte("PONG"))
|
||||
}
|
||||
})
|
||||
defer ts2.Close()
|
||||
|
||||
// even though we passed port 443 in url it is ignored and port 23846 is used
|
||||
// instead of hardcoded port 23846 in template
|
||||
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, ts2.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type networkhttps struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *networkhttps) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
+3
-6
@@ -1,15 +1,12 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var offlineHttpTestcases = []integrationCase{
|
||||
var offlineHttpTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/offlinehttp/rfc-req-resp.yaml", TestCase: &RfcRequestResponse{}},
|
||||
{Path: "protocols/offlinehttp/offline-allowed-paths.yaml", TestCase: &RequestResponseWithAllowedPaths{}},
|
||||
{Path: "protocols/offlinehttp/offline-raw.yaml", TestCase: &RawRequestResponse{}},
|
||||
+10
-13
@@ -1,16 +1,13 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
)
|
||||
|
||||
var profileLoaderTestcases = []integrationCase{
|
||||
var profileLoaderTestcases = []TestCaseInfo{
|
||||
{Path: "profile-loader/load-with-filename", TestCase: &profileLoaderByRelFile{}},
|
||||
{Path: "profile-loader/load-with-id", TestCase: &profileLoaderById{}},
|
||||
{Path: "profile-loader/basic.yml", TestCase: &customProfileLoader{}},
|
||||
@@ -19,9 +16,9 @@ var profileLoaderTestcases = []integrationCase{
|
||||
type profileLoaderByRelFile struct{}
|
||||
|
||||
func (h *profileLoaderByRelFile) Execute(testName string) error {
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-tl", "-tp", "cloud.yml")
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "cloud.yml")
|
||||
if err != nil {
|
||||
return errkit.Wrap(err, "failed to load template with id")
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) <= 10 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 10, len(results))
|
||||
@@ -32,9 +29,9 @@ func (h *profileLoaderByRelFile) Execute(testName string) error {
|
||||
type profileLoaderById struct{}
|
||||
|
||||
func (h *profileLoaderById) Execute(testName string) error {
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-tl", "-tp", "cloud")
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "cloud")
|
||||
if err != nil {
|
||||
return errkit.Wrap(err, "failed to load template with id")
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) <= 10 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 10, len(results))
|
||||
@@ -46,9 +43,9 @@ func (h *profileLoaderById) Execute(testName string) error {
|
||||
type customProfileLoader struct{}
|
||||
|
||||
func (h *customProfileLoader) Execute(filepath string) error {
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-tl", "-tp", filepath)
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", filepath)
|
||||
if err != nil {
|
||||
return errkit.Wrap(err, "failed to load template with id")
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) < 1 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 1, len(results))
|
||||
@@ -0,0 +1,157 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"net"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var sslTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/ssl/basic.yaml", TestCase: &sslBasic{}},
|
||||
{Path: "protocols/ssl/basic-ztls.yaml", TestCase: &sslBasicZtls{}},
|
||||
{Path: "protocols/ssl/custom-cipher.yaml", TestCase: &sslCustomCipher{}},
|
||||
{Path: "protocols/ssl/custom-version.yaml", TestCase: &sslCustomVersion{}},
|
||||
{Path: "protocols/ssl/ssl-with-vars.yaml", TestCase: &sslWithVars{}},
|
||||
{Path: "protocols/ssl/multi-req.yaml", TestCase: &sslMultiReq{}},
|
||||
}
|
||||
|
||||
type sslBasic struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *sslBasic) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslBasicZtls struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *sslBasicZtls) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-ztls")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslCustomCipher struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *sslCustomCipher) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{CipherSuites: []uint16{tls.TLS_AES_128_GCM_SHA256}}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslCustomVersion struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *sslCustomVersion) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{MinVersion: tls.VersionTLS12, MaxVersion: tls.VersionTLS12}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslWithVars struct{}
|
||||
|
||||
func (h *sslWithVars) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-V", "test=asdasdas")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslMultiReq struct{}
|
||||
|
||||
func (h *sslMultiReq) Execute(filePath string) error {
|
||||
//nolint:staticcheck // SSLv3 is intentionally used for testing purposes
|
||||
ts := testutils.NewTCPServer(&tls.Config{
|
||||
MinVersion: tls.VersionSSL30,
|
||||
MaxVersion: tls.VersionTLS11,
|
||||
}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer func() {
|
||||
_ = conn.Close()
|
||||
}()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-V")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
)
|
||||
|
||||
var templatesDirTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templateDirWithTargetTest{}},
|
||||
}
|
||||
|
||||
type templateDirWithTargetTest struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *templateDirWithTargetTest) Execute(filePath string) error {
|
||||
tempdir, err := os.MkdirTemp("", "nuclei-update-dir-*")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to create temp dir")
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tempdir)
|
||||
}()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "8x8exch02.8x8.com", debug, "-ud", tempdir)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
+3
-6
@@ -1,21 +1,18 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
func getTemplatePath() string {
|
||||
return config.DefaultConfig.TemplatesDirectory
|
||||
}
|
||||
|
||||
var templatesPathTestCases = []integrationCase{
|
||||
var templatesPathTestCases = []TestCaseInfo{
|
||||
//template folder path issue
|
||||
{Path: "protocols/http/get.yaml", TestCase: &folderPathTemplateTest{}},
|
||||
//cwd
|
||||
@@ -0,0 +1,107 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/gobwas/ws/wsutil"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var websocketTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/websocket/basic.yaml", TestCase: &websocketBasic{}},
|
||||
{Path: "protocols/websocket/cswsh.yaml", TestCase: &websocketCswsh{}},
|
||||
{Path: "protocols/websocket/no-cswsh.yaml", TestCase: &websocketNoCswsh{}},
|
||||
{Path: "protocols/websocket/path.yaml", TestCase: &websocketWithPath{}},
|
||||
}
|
||||
|
||||
type websocketBasic struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *websocketBasic) Execute(filePath string) error {
|
||||
connHandler := func(conn net.Conn) {
|
||||
for {
|
||||
msg, op, _ := wsutil.ReadClientData(conn)
|
||||
if string(msg) != "hello" {
|
||||
return
|
||||
}
|
||||
_ = wsutil.WriteServerMessage(conn, op, []byte("world"))
|
||||
}
|
||||
}
|
||||
originValidate := func(origin string) bool {
|
||||
return true
|
||||
}
|
||||
ts := testutils.NewWebsocketServer("", connHandler, originValidate)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "http", "ws"), debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type websocketCswsh struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *websocketCswsh) Execute(filePath string) error {
|
||||
connHandler := func(conn net.Conn) {
|
||||
|
||||
}
|
||||
originValidate := func(origin string) bool {
|
||||
return true
|
||||
}
|
||||
ts := testutils.NewWebsocketServer("", connHandler, originValidate)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "http", "ws"), debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type websocketNoCswsh struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *websocketNoCswsh) Execute(filePath string) error {
|
||||
connHandler := func(conn net.Conn) {
|
||||
|
||||
}
|
||||
originValidate := func(origin string) bool {
|
||||
return origin == "https://google.com"
|
||||
}
|
||||
ts := testutils.NewWebsocketServer("", connHandler, originValidate)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "http", "ws"), debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
|
||||
type websocketWithPath struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *websocketWithPath) Execute(filePath string) error {
|
||||
connHandler := func(conn net.Conn) {
|
||||
|
||||
}
|
||||
originValidate := func(origin string) bool {
|
||||
return origin == "https://google.com"
|
||||
}
|
||||
ts := testutils.NewWebsocketServer("/test", connHandler, originValidate)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, strings.ReplaceAll(ts.URL, "http", "ws"), debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 0)
|
||||
}
|
||||
@@ -1,13 +1,10 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var whoisTestCases = []integrationCase{
|
||||
var whoisTestCases = []TestCaseInfo{
|
||||
{Path: "protocols/whois/basic.yaml", TestCase: &whoisBasic{}},
|
||||
}
|
||||
|
||||
@@ -1,23 +1,22 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/tests/testutils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
var workflowTestcases = []integrationCase{
|
||||
var workflowTestcases = []TestCaseInfo{
|
||||
{Path: "workflow/basic.yaml", TestCase: &workflowBasic{}},
|
||||
{Path: "workflow/condition-matched.yaml", TestCase: &workflowConditionMatched{}},
|
||||
{Path: "workflow/condition-unmatched.yaml", TestCase: &workflowConditionUnmatch{}},
|
||||
@@ -31,6 +30,32 @@ var workflowTestcases = []integrationCase{
|
||||
{Path: "workflow/shared-cookie.yaml", TestCase: &workflowSharedCookies{}},
|
||||
}
|
||||
|
||||
func init() {
|
||||
// sign code templates (unless they are disabled)
|
||||
if !isCodeDisabled() {
|
||||
// allow local file access to load content of file references in template
|
||||
// in order to sign them for testing purposes
|
||||
templates.TemplateSignerLFA()
|
||||
|
||||
// testCertFile and testKeyFile are declared in code.go
|
||||
tsigner, err := signer.NewTemplateSignerFromFiles(testCertFile, testKeyFile)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// only the code templates are necessary to be signed
|
||||
var templatesToSign = []string{
|
||||
"workflow/code-template-1.yaml",
|
||||
"workflow/code-template-2.yaml",
|
||||
}
|
||||
for _, templatePath := range templatesToSign {
|
||||
if err := templates.SignTemplate(tsigner, templatePath); err != nil {
|
||||
log.Fatalf("Could not sign template %v got: %s\n", templatePath, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type workflowBasic struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
@@ -11,7 +11,7 @@ import (
|
||||
|
||||
{{range .Functions}}
|
||||
{{ .SignatureWithPrefix "memoized" }} {
|
||||
hash := "{{ .Name }}" {{range .Params}}{{if ne .Type "&{context Context}"}} + ":" + fmt.Sprint({{.Name}}){{end}}{{end}}
|
||||
hash := "{{ .Name }}" {{range .Params}} + ":" + fmt.Sprint({{.Name}}) {{end}}
|
||||
|
||||
v, err, _ := protocolstate.Memoizer.Do(hash, func() (interface{}, error) {
|
||||
return {{.Name}}({{.ParamsNames}})
|
||||
@@ -25,4 +25,4 @@ import (
|
||||
|
||||
return {{.ResultFirstFieldDefaultValue}}, errors.New("could not convert cached result")
|
||||
}
|
||||
{{end}}
|
||||
{{end}}
|
||||
@@ -55,12 +55,6 @@
|
||||
# # these severity labels or tags (does not affect exporters. set those globally)
|
||||
# deny-list:
|
||||
# severity: low
|
||||
# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check
|
||||
# duplicate-issue-check: false
|
||||
# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates
|
||||
# duplicate-issue-page-size: 100
|
||||
# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit)
|
||||
# duplicate-issue-max-pages: 0
|
||||
#
|
||||
# Gitea contains configuration options for a gitea issue tracker
|
||||
#gitea:
|
||||
@@ -87,10 +81,6 @@
|
||||
# severity: low
|
||||
# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check
|
||||
# duplicate-issue-check: false
|
||||
# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates
|
||||
# duplicate-issue-page-size: 100
|
||||
# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit)
|
||||
# duplicate-issue-max-pages: 0
|
||||
#
|
||||
# Jira contains configuration options for Jira issue tracker
|
||||
#jira:
|
||||
@@ -100,9 +90,6 @@
|
||||
# update-existing: false
|
||||
# # URL is the jira application url
|
||||
# url: https://localhost/jira
|
||||
# # site-url is the browsable URL for the Jira instance (optional)
|
||||
# # If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com
|
||||
# site-url: https://your-company.atlassian.net
|
||||
# # account-id is the account-id of the Jira user or username in case of on-prem Jira
|
||||
# account-id: test-account-id
|
||||
# # email is the email of the user for Jira instance
|
||||
@@ -131,10 +118,10 @@
|
||||
# status-not: Closed
|
||||
# # Customfield supports name, id and freeform. name and id are to be used when the custom field is a dropdown.
|
||||
# # freeform can be used if the custom field is just a text entry
|
||||
# # Variables can be used to pull various pieces of data from the finding itself.
|
||||
# # Variables can be used to pull various pieces of data from the finding itself.
|
||||
# # Supported variables: $CVSSMetrics, $CVEID, $CWEID, $Host, $Severity, $CVSSScore, $Name
|
||||
# custom-fields:
|
||||
# customfield_00001:
|
||||
# customfield_00001:
|
||||
# name: "Nuclei"
|
||||
# customfield_00002:
|
||||
# freeform: $CVSSMetrics
|
||||
@@ -185,4 +172,4 @@
|
||||
# omit-raw: false
|
||||
# # determines the number of results to be kept in memory before writing it to the database or 0 to
|
||||
# # persist all in memory and write all results at the end (default)
|
||||
# batch-size: 0
|
||||
# batch-size: 0
|
||||
+85
-237
@@ -13,16 +13,14 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
_pdcp "github.com/projectdiscovery/nuclei/v3/internal/pdcp"
|
||||
"github.com/projectdiscovery/utils/auth/pdcp"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
_ "github.com/projectdiscovery/utils/pprof"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
"github.com/rs/xid"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/projectdiscovery/goflags"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/gologger/levels"
|
||||
"github.com/projectdiscovery/interactsh/pkg/client"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/runner"
|
||||
@@ -40,39 +38,30 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/scanstrategy"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/monitor"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
updateutils "github.com/projectdiscovery/utils/update"
|
||||
)
|
||||
|
||||
var (
|
||||
cfgFile string
|
||||
templateProfile string
|
||||
memProfile string // optional profile file path
|
||||
options = &types.Options{}
|
||||
inlineSecretsTempFiles []string
|
||||
cfgFile string
|
||||
templateProfile string
|
||||
memProfile string // optional profile file path
|
||||
options = &types.Options{}
|
||||
)
|
||||
|
||||
func main() {
|
||||
options.Logger = gologger.DefaultLogger
|
||||
|
||||
defer func() {
|
||||
for _, f := range inlineSecretsTempFiles {
|
||||
_ = os.Remove(f)
|
||||
}
|
||||
}()
|
||||
|
||||
// enables CLI specific configs mostly interactive behavior
|
||||
config.CurrentAppMode = config.AppModeCLI
|
||||
|
||||
if err := runner.ConfigureOptions(); err != nil {
|
||||
options.Logger.Fatal().Msgf("Could not initialize options: %s\n", err)
|
||||
gologger.Fatal().Msgf("Could not initialize options: %s\n", err)
|
||||
}
|
||||
_ = readConfig()
|
||||
|
||||
if options.ListDslSignatures {
|
||||
options.Logger.Info().Msgf("The available custom DSL functions are:")
|
||||
gologger.Info().Msgf("The available custom DSL functions are:")
|
||||
fmt.Println(dsl.GetPrintableDslFunctionSignatures(options.NoColor))
|
||||
return
|
||||
}
|
||||
@@ -83,7 +72,7 @@ func main() {
|
||||
templates.UseOptionsForSigner(options)
|
||||
tsigner, err := signer.NewTemplateSigner(nil, nil) // will read from env , config or generate new keys
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("couldn't initialize signer crypto engine: %s\n", err)
|
||||
gologger.Fatal().Msgf("couldn't initialize signer crypto engine: %s\n", err)
|
||||
}
|
||||
|
||||
successCounter := 0
|
||||
@@ -99,7 +88,7 @@ func main() {
|
||||
if err != templates.ErrNotATemplate {
|
||||
// skip warnings and errors as given items are not templates
|
||||
errorCounter++
|
||||
options.Logger.Error().Msgf("could not sign '%s': %s\n", iterItem, err)
|
||||
gologger.Error().Msgf("could not sign '%s': %s\n", iterItem, err)
|
||||
}
|
||||
} else {
|
||||
successCounter++
|
||||
@@ -108,10 +97,10 @@ func main() {
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
options.Logger.Error().Msgf("%s\n", err)
|
||||
gologger.Error().Msgf("%s\n", err)
|
||||
}
|
||||
}
|
||||
options.Logger.Info().Msgf("All templates signatures were elaborated success=%d failed=%d\n", successCounter, errorCounter)
|
||||
gologger.Info().Msgf("All templates signatures were elaborated success=%d failed=%d\n", successCounter, errorCounter)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -122,7 +111,7 @@ func main() {
|
||||
createProfileFile := func(ext, profileType string) *os.File {
|
||||
f, err := os.Create(memProfile + ext)
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("profile: could not create %s profile %q file: %v", profileType, f.Name(), err)
|
||||
gologger.Fatal().Msgf("profile: could not create %s profile %q file: %v", profileType, f.Name(), err)
|
||||
}
|
||||
return f
|
||||
}
|
||||
@@ -136,18 +125,18 @@ func main() {
|
||||
|
||||
// Start tracing
|
||||
if err := trace.Start(traceFile); err != nil {
|
||||
options.Logger.Fatal().Msgf("profile: could not start trace: %v", err)
|
||||
gologger.Fatal().Msgf("profile: could not start trace: %v", err)
|
||||
}
|
||||
|
||||
// Start CPU profiling
|
||||
if err := pprof.StartCPUProfile(cpuProfileFile); err != nil {
|
||||
options.Logger.Fatal().Msgf("profile: could not start CPU profile: %v", err)
|
||||
gologger.Fatal().Msgf("profile: could not start CPU profile: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
// Start heap memory snapshot
|
||||
if err := pprof.WriteHeapProfile(memProfileFile); err != nil {
|
||||
options.Logger.Fatal().Msgf("profile: could not write memory profile: %v", err)
|
||||
gologger.Fatal().Msgf("profile: could not write memory profile: %v", err)
|
||||
}
|
||||
|
||||
pprof.StopCPUProfile()
|
||||
@@ -157,26 +146,24 @@ func main() {
|
||||
|
||||
runtime.MemProfileRate = oldMemProfileRate
|
||||
|
||||
options.Logger.Info().Msgf("CPU profile saved at %q", cpuProfileFile.Name())
|
||||
options.Logger.Info().Msgf("Memory usage snapshot saved at %q", memProfileFile.Name())
|
||||
options.Logger.Info().Msgf("Traced at %q", traceFile.Name())
|
||||
gologger.Info().Msgf("CPU profile saved at %q", cpuProfileFile.Name())
|
||||
gologger.Info().Msgf("Memory usage snapshot saved at %q", memProfileFile.Name())
|
||||
gologger.Info().Msgf("Traced at %q", traceFile.Name())
|
||||
}()
|
||||
}
|
||||
|
||||
options.ExecutionId = xid.New().String()
|
||||
|
||||
runner.ParseOptions(options)
|
||||
|
||||
if options.ScanUploadFile != "" {
|
||||
if err := runner.UploadResultsToCloud(options); err != nil {
|
||||
options.Logger.Fatal().Msgf("could not upload scan results to cloud dashboard: %s\n", err)
|
||||
gologger.Fatal().Msgf("could not upload scan results to cloud dashboard: %s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
nucleiRunner, err := runner.New(options)
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
}
|
||||
if nucleiRunner == nil {
|
||||
return
|
||||
@@ -189,56 +176,52 @@ func main() {
|
||||
stackMonitor.RegisterCallback(func(dumpID string) error {
|
||||
resumeFileName := fmt.Sprintf("crash-resume-file-%s.dump", dumpID)
|
||||
if options.EnableCloudUpload {
|
||||
options.Logger.Info().Msgf("Uploading scan results to cloud...")
|
||||
gologger.Info().Msgf("Uploading scan results to cloud...")
|
||||
}
|
||||
nucleiRunner.Close()
|
||||
options.Logger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
gologger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
||||
if err != nil {
|
||||
return errkit.Wrap(err, "couldn't create crash resume file")
|
||||
return errorutil.NewWithErr(err).Msgf("couldn't create crash resume file")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Setup filename for graceful exits
|
||||
// Setup graceful exits
|
||||
resumeFileName := types.DefaultResumeFilePath()
|
||||
if options.Resume != "" {
|
||||
resumeFileName = options.Resume
|
||||
}
|
||||
c := make(chan os.Signal, 1)
|
||||
defer close(c)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
<-c
|
||||
options.Logger.Info().Msgf("CTRL+C pressed: Exiting\n")
|
||||
if options.DASTServer {
|
||||
for range c {
|
||||
gologger.Info().Msgf("CTRL+C pressed: Exiting\n")
|
||||
if options.DASTServer {
|
||||
nucleiRunner.Close()
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
gologger.Info().Msgf("Attempting graceful shutdown...")
|
||||
if options.EnableCloudUpload {
|
||||
gologger.Info().Msgf("Uploading scan results to cloud...")
|
||||
}
|
||||
nucleiRunner.Close()
|
||||
if options.ShouldSaveResume() {
|
||||
gologger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Couldn't create resume file: %s\n", err)
|
||||
}
|
||||
}
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
options.Logger.Info().Msgf("Attempting graceful shutdown...")
|
||||
if options.EnableCloudUpload {
|
||||
options.Logger.Info().Msgf("Uploading scan results to cloud...")
|
||||
}
|
||||
nucleiRunner.Close()
|
||||
if options.ShouldSaveResume() {
|
||||
options.Logger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
||||
if err != nil {
|
||||
options.Logger.Error().Msgf("Couldn't create resume file: %s\n", err)
|
||||
}
|
||||
}
|
||||
for _, f := range inlineSecretsTempFiles {
|
||||
_ = os.Remove(f)
|
||||
}
|
||||
os.Exit(1)
|
||||
}()
|
||||
|
||||
if err := nucleiRunner.RunEnumeration(); err != nil {
|
||||
if options.Validate {
|
||||
options.Logger.Fatal().Msgf("Could not validate templates: %s\n", err)
|
||||
gologger.Fatal().Msgf("Could not validate templates: %s\n", err)
|
||||
} else {
|
||||
options.Logger.Fatal().Msgf("Could not run nuclei: %s\n", err)
|
||||
gologger.Fatal().Msgf("Could not run nuclei: %s\n", err)
|
||||
}
|
||||
}
|
||||
nucleiRunner.Close()
|
||||
@@ -267,9 +250,8 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CreateGroup("input", "Target",
|
||||
flagSet.StringSliceVarP(&options.Targets, "target", "u", nil, "target URLs/hosts to scan", goflags.CommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVarP(&options.TargetsFilePath, "list", "l", "", "path to file containing a list of target URLs/hosts to scan (one per line)"),
|
||||
flagSet.StringVarP(&options.InlineTargetsList, "targets-inline", "", "", "inline multiline target list (for use in template profiles)"),
|
||||
flagSet.StringSliceVarP(&options.ExcludeTargets, "exclude-hosts", "eh", nil, "hosts to exclude to scan from the input list (ip, cidr, hostname)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "resume scan from and save to specified file (clustering will be disabled)"),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "resume scan using resume.cfg (clustering will be disabled)"),
|
||||
flagSet.BoolVarP(&options.ScanAllIPs, "scan-all-ips", "sa", false, "scan all the IP's associated with dns record"),
|
||||
flagSet.StringSliceVarP(&options.IPVersion, "ip-version", "iv", nil, "IP version to scan of hostname (4,6) - (default 4)", goflags.CommaSeparatedStringSliceOptions),
|
||||
)
|
||||
@@ -278,8 +260,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.InputFileMode, "input-mode", "im", "list", fmt.Sprintf("mode of input file (%v)", provider.SupportedInputFormats())),
|
||||
flagSet.BoolVarP(&options.FormatUseRequiredOnly, "required-only", "ro", false, "use only required fields in input format when generating requests"),
|
||||
flagSet.BoolVarP(&options.SkipFormatValidation, "skip-format-validation", "sfv", false, "skip format validation (like missing vars) when parsing input file"),
|
||||
flagSet.BoolVarP(&options.VarsTextTemplating, "vars-text-templating", "vtt", false, "enable text templating for vars in input file (only for yaml input mode)"),
|
||||
flagSet.StringSliceVarP(&options.VarsFilePaths, "var-file-paths", "vfp", nil, "list of yaml file contained vars to inject into yaml input", goflags.CommaSeparatedStringSliceOptions),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("templates", "Templates",
|
||||
@@ -294,7 +274,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVar(&options.Validate, "validate", false, "validate the passed templates to nuclei"),
|
||||
flagSet.BoolVarP(&options.NoStrictSyntax, "no-strict-syntax", "nss", false, "disable strict syntax check on templates"),
|
||||
flagSet.BoolVarP(&options.TemplateDisplay, "template-display", "td", false, "displays the templates content"),
|
||||
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all templates matching current filters"),
|
||||
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all available templates"),
|
||||
flagSet.BoolVar(&options.TagList, "tgl", false, "list all available tags"),
|
||||
flagSet.StringSliceVarConfigOnly(&options.RemoteTemplateDomainList, "remote-template-domain", []string{"cloud.projectdiscovery.io"}, "allowed domain list to load remote templates from"),
|
||||
flagSet.BoolVar(&options.SignTemplates, "sign", false, "signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable"),
|
||||
@@ -330,7 +310,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.NoColor, "no-color", "nc", false, "disable output content coloring (ANSI escape codes)"),
|
||||
flagSet.BoolVarP(&options.JSONL, "jsonl", "j", false, "write output in JSONL(ines) format"),
|
||||
flagSet.BoolVarP(&options.JSONRequests, "include-rr", "irr", true, "include request/response pairs in the JSON, JSONL, and Markdown outputs (for findings only) [DEPRECATED use `-omit-raw`]"),
|
||||
flagSet.BoolVarP(&options.OmitRawRequests, "omit-raw", "or", false, "omit request/response pairs in the JSON, JSONL, Markdown, and PDF outputs (for findings only)"),
|
||||
flagSet.BoolVarP(&options.OmitRawRequests, "omit-raw", "or", false, "omit request/response pairs in the JSON, JSONL, and Markdown outputs (for findings only)"),
|
||||
flagSet.BoolVarP(&options.OmitTemplate, "omit-template", "ot", false, "omit encoded template in the JSON, JSONL output"),
|
||||
flagSet.BoolVarP(&options.NoMeta, "no-meta", "nm", false, "disable printing result metadata in cli output"),
|
||||
flagSet.BoolVarP(&options.Timestamp, "timestamp", "ts", false, "enables printing timestamp in cli output"),
|
||||
@@ -340,7 +320,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.SarifExport, "sarif-export", "se", "", "file to export results in SARIF format"),
|
||||
flagSet.StringVarP(&options.JSONExport, "json-export", "je", "", "file to export results in JSON format"),
|
||||
flagSet.StringVarP(&options.JSONLExport, "jsonl-export", "jle", "", "file to export results in JSONL(ine) format"),
|
||||
flagSet.StringVarP(&options.PDFExport, "pdf-export", "pe", "", "file to export results in PDF format"),
|
||||
flagSet.StringSliceVarP(&options.Redact, "redact", "rd", nil, "redact given list of keys from query parameter, request header and body", goflags.CommaSeparatedStringSliceOptions),
|
||||
)
|
||||
|
||||
@@ -426,7 +405,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.IntVarP(&options.JsConcurrency, "js-concurrency", "jsc", 120, "maximum number of javascript runtimes to be executed in parallel"),
|
||||
flagSet.IntVarP(&options.PayloadConcurrency, "payload-concurrency", "pc", 25, "max payload concurrency for each template"),
|
||||
flagSet.IntVarP(&options.ProbeConcurrency, "probe-concurrency", "prc", 50, "http probe concurrency with httpx"),
|
||||
flagSet.IntVarP(&options.TemplateLoadingConcurrency, "template-loading-concurrency", "tlc", types.DefaultTemplateLoadingConcurrency, "maximum number of concurrent template loading operations"),
|
||||
)
|
||||
flagSet.CreateGroup("optimization", "Optimizations",
|
||||
flagSet.IntVar(&options.Timeout, "timeout", 10, "time to wait in seconds before timeout"),
|
||||
@@ -455,7 +433,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.ShowBrowser, "show-browser", "sb", false, "show the browser on the screen when running templates with headless mode"),
|
||||
flagSet.StringSliceVarP(&options.HeadlessOptionalArguments, "headless-options", "ho", nil, "start headless chrome with additional options", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.BoolVarP(&options.UseInstalledChrome, "system-chrome", "sc", false, "use local installed Chrome browser instead of nuclei installed"),
|
||||
flagSet.StringVarP(&options.CDPEndpoint, "cdp-endpoint", "cdpe", "", "use remote browser via Chrome DevTools Protocol (CDP) endpoint"),
|
||||
flagSet.BoolVarP(&options.ShowActions, "list-headless-action", "lha", false, "list available headless actions"),
|
||||
)
|
||||
|
||||
@@ -487,12 +464,6 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CallbackVarP(disableUpdatesCallback, "disable-update-check", "duc", "disable automatic nuclei/templates update check"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("Honeypot", "Honeypot",
|
||||
flagSet.BoolVarP(&options.HoneypotDetection, "honeypot-detect", "hpd", false, "detect potential honeypot hosts based on match concentration"),
|
||||
flagSet.IntVarP(&options.HoneypotThreshold, "honeypot-threshold", "hpt", 15, "number of distinct template IDs required to flag a honeypot host"),
|
||||
flagSet.BoolVarP(&options.SuppressHoneypotResults, "suppress-honeypot", "shp", false, "suppress output for flagged honeypot hosts"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("stats", "Statistics",
|
||||
flagSet.BoolVar(&options.EnableProgressBar, "stats", false, "display statistics about the running scan"),
|
||||
flagSet.BoolVarP(&options.StatsJSON, "stats-json", "sj", false, "display statistics in JSONL(ines) format"),
|
||||
@@ -571,11 +542,11 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
h := &pdcp.PDCPCredHandler{}
|
||||
_, err := h.GetCreds()
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msg("To utilize the `-ai` flag, please configure your API key with the `-auth` flag or set the `PDCP_API_KEY` environment variable")
|
||||
gologger.Fatal().Msg("To utilize the `-ai` flag, please configure your API key with the `-auth` flag or set the `PDCP_API_KEY` environment variable")
|
||||
}
|
||||
}
|
||||
|
||||
options.Logger.SetTimestamp(options.Timestamp, levels.LevelDebug)
|
||||
gologger.DefaultLogger.SetTimestamp(options.Timestamp, levels.LevelDebug)
|
||||
|
||||
if options.VerboseVerbose {
|
||||
// hide release notes if silent mode is enabled
|
||||
@@ -597,58 +568,17 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
config.DefaultConfig.SetConfigDir(customConfigDir)
|
||||
readFlagsConfig(flagSet)
|
||||
}
|
||||
|
||||
if cfgFile != "" {
|
||||
if !fileutil.FileExists(cfgFile) {
|
||||
options.Logger.Fatal().Msgf("given config file '%s' does not exist", cfgFile)
|
||||
gologger.Fatal().Msgf("given config file '%s' does not exist", cfgFile)
|
||||
}
|
||||
// merge config file with flags
|
||||
if err := flagSet.MergeConfigFile(cfgFile); err != nil {
|
||||
options.Logger.Fatal().Msgf("Could not read config: %s\n", err)
|
||||
}
|
||||
|
||||
if !options.Vars.IsEmpty() {
|
||||
// Maybe we should add vars to the config file as well even if they are set via flags?
|
||||
file, err := os.Open(cfgFile)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not open config file: %s\n", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
data := make(map[string]interface{})
|
||||
err = yaml.NewDecoder(file).Decode(&data)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not decode config file: %s\n", err)
|
||||
}
|
||||
|
||||
variables := data["var"]
|
||||
if variables != nil {
|
||||
if varSlice, ok := variables.([]interface{}); ok {
|
||||
for _, value := range varSlice {
|
||||
if strVal, ok := value.(string); ok {
|
||||
err = options.Vars.Set(strVal)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("Could not set variable from config file: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
gologger.Warning().Msgf("Skipping non-string variable in config: %#v", value)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
gologger.Warning().Msgf("No 'var' section found in config file: %s", cfgFile)
|
||||
}
|
||||
}
|
||||
|
||||
gologger.Fatal().Msgf("Could not read config: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
templatesDir := options.NewTemplatesDirectory
|
||||
if templatesDir == "" {
|
||||
templatesDir = os.Getenv(config.NucleiTemplatesDirEnv)
|
||||
}
|
||||
if templatesDir != "" {
|
||||
config.DefaultConfig.SetTemplatesDir(templatesDir)
|
||||
if options.NewTemplatesDirectory != "" {
|
||||
config.DefaultConfig.SetTemplatesDir(options.NewTemplatesDirectory)
|
||||
}
|
||||
|
||||
defaultProfilesPath := filepath.Join(config.DefaultConfig.GetTemplateDir(), "profiles")
|
||||
@@ -657,7 +587,7 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
if tp := findProfilePathById(templateProfile, defaultProfilesPath); tp != "" {
|
||||
templateProfile = tp
|
||||
} else {
|
||||
options.Logger.Fatal().Msgf("'%s' is not a profile-id or profile path", templateProfile)
|
||||
gologger.Fatal().Msgf("'%s' is not a profile-id or profile path", templateProfile)
|
||||
}
|
||||
}
|
||||
if !filepath.IsAbs(templateProfile) {
|
||||
@@ -672,50 +602,17 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
}
|
||||
}
|
||||
if !fileutil.FileExists(templateProfile) {
|
||||
options.Logger.Fatal().Msgf("given template profile file '%s' does not exist", templateProfile)
|
||||
gologger.Fatal().Msgf("given template profile file '%s' does not exist", templateProfile)
|
||||
}
|
||||
if err := flagSet.MergeConfigFile(templateProfile); err != nil {
|
||||
options.Logger.Fatal().Msgf("Could not read template profile: %s\n", err)
|
||||
}
|
||||
|
||||
// Process inline target list from profile.
|
||||
// Supports both the dedicated targets-inline key and multiline
|
||||
// content in the list key (which normally holds a file path).
|
||||
if options.InlineTargetsList != "" {
|
||||
inlineTargets := strings.Split(strings.TrimSpace(options.InlineTargetsList), "\n")
|
||||
for _, target := range inlineTargets {
|
||||
target = strings.TrimSpace(target)
|
||||
if target != "" && !strings.HasPrefix(target, "#") {
|
||||
options.Targets = append(options.Targets, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
if strings.Contains(options.TargetsFilePath, "\n") {
|
||||
// list key has multiline content, treat as inline targets
|
||||
inlineTargets := strings.Split(strings.TrimSpace(options.TargetsFilePath), "\n")
|
||||
for _, target := range inlineTargets {
|
||||
target = strings.TrimSpace(target)
|
||||
if target != "" && !strings.HasPrefix(target, "#") {
|
||||
options.Targets = append(options.Targets, target)
|
||||
}
|
||||
}
|
||||
options.TargetsFilePath = ""
|
||||
}
|
||||
|
||||
// Process inline secrets from profile YAML
|
||||
tempSecretsFile, err := processInlineSecretsFromProfile(templateProfile, options)
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("Could not process inline secrets: %s\n", err)
|
||||
}
|
||||
if tempSecretsFile != "" {
|
||||
inlineSecretsTempFiles = append(inlineSecretsTempFiles, tempSecretsFile)
|
||||
gologger.Fatal().Msgf("Could not read template profile: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(options.SecretsFile) > 0 {
|
||||
for _, secretFile := range options.SecretsFile {
|
||||
if !fileutil.FileExists(secretFile) {
|
||||
options.Logger.Fatal().Msgf("given secrets file '%s' does not exist", secretFile)
|
||||
gologger.Fatal().Msgf("given secrets file '%s' does not exist", options.SecretsFile)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -741,25 +638,25 @@ func readFlagsConfig(flagset *goflags.FlagSet) {
|
||||
if err != nil {
|
||||
// something went wrong either dir is not readable or something else went wrong upstream in `goflags`
|
||||
// warn and exit in this case
|
||||
options.Logger.Warning().Msgf("Could not read config file: %s\n", err)
|
||||
gologger.Warning().Msgf("Could not read config file: %s\n", err)
|
||||
return
|
||||
}
|
||||
cfgFile := config.DefaultConfig.GetFlagsConfigFilePath()
|
||||
if !fileutil.FileExists(cfgFile) {
|
||||
if !fileutil.FileExists(defaultCfgFile) {
|
||||
// if default config does not exist, warn and exit
|
||||
options.Logger.Warning().Msgf("missing default config file : %s", defaultCfgFile)
|
||||
gologger.Warning().Msgf("missing default config file : %s", defaultCfgFile)
|
||||
return
|
||||
}
|
||||
// if does not exist copy it from the default config
|
||||
if err = fileutil.CopyFile(defaultCfgFile, cfgFile); err != nil {
|
||||
options.Logger.Warning().Msgf("Could not copy config file: %s\n", err)
|
||||
gologger.Warning().Msgf("Could not copy config file: %s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
// if config file exists, merge it with the default config
|
||||
if err = flagset.MergeConfigFile(cfgFile); err != nil {
|
||||
options.Logger.Warning().Msgf("failed to merge configfile with flags got: %s\n", err)
|
||||
gologger.Warning().Msgf("failed to merge configfile with flags got: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -770,29 +667,29 @@ func disableUpdatesCallback() {
|
||||
|
||||
// printVersion prints the nuclei version and exits.
|
||||
func printVersion() {
|
||||
options.Logger.Info().Msgf("Nuclei Engine Version: %s", config.Version)
|
||||
options.Logger.Info().Msgf("Nuclei Config Directory: %s", config.DefaultConfig.GetConfigDir())
|
||||
options.Logger.Info().Msgf("Nuclei Cache Directory: %s", config.DefaultConfig.GetCacheDir()) // cache dir contains resume files
|
||||
options.Logger.Info().Msgf("PDCP Directory: %s", pdcp.PDCPDir)
|
||||
gologger.Info().Msgf("Nuclei Engine Version: %s", config.Version)
|
||||
gologger.Info().Msgf("Nuclei Config Directory: %s", config.DefaultConfig.GetConfigDir())
|
||||
gologger.Info().Msgf("Nuclei Cache Directory: %s", config.DefaultConfig.GetCacheDir()) // cache dir contains resume files
|
||||
gologger.Info().Msgf("PDCP Directory: %s", pdcp.PDCPDir)
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
// printTemplateVersion prints the nuclei template version and exits.
|
||||
func printTemplateVersion() {
|
||||
cfg := config.DefaultConfig
|
||||
options.Logger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", cfg.TemplateVersion, cfg.TemplatesDirectory)
|
||||
gologger.Info().Msgf("Public nuclei-templates version: %s (%s)\n", cfg.TemplateVersion, cfg.TemplatesDirectory)
|
||||
|
||||
if fileutil.FolderExists(cfg.CustomS3TemplatesDirectory) {
|
||||
options.Logger.Info().Msgf("Custom S3 templates location: %s\n", cfg.CustomS3TemplatesDirectory)
|
||||
gologger.Info().Msgf("Custom S3 templates location: %s\n", cfg.CustomS3TemplatesDirectory)
|
||||
}
|
||||
if fileutil.FolderExists(cfg.CustomGitHubTemplatesDirectory) {
|
||||
options.Logger.Info().Msgf("Custom GitHub templates location: %s ", cfg.CustomGitHubTemplatesDirectory)
|
||||
gologger.Info().Msgf("Custom GitHub templates location: %s ", cfg.CustomGitHubTemplatesDirectory)
|
||||
}
|
||||
if fileutil.FolderExists(cfg.CustomGitLabTemplatesDirectory) {
|
||||
options.Logger.Info().Msgf("Custom GitLab templates location: %s ", cfg.CustomGitLabTemplatesDirectory)
|
||||
gologger.Info().Msgf("Custom GitLab templates location: %s ", cfg.CustomGitLabTemplatesDirectory)
|
||||
}
|
||||
if fileutil.FolderExists(cfg.CustomAzureTemplatesDirectory) {
|
||||
options.Logger.Info().Msgf("Custom Azure templates location: %s ", cfg.CustomAzureTemplatesDirectory)
|
||||
gologger.Info().Msgf("Custom Azure templates location: %s ", cfg.CustomAzureTemplatesDirectory)
|
||||
}
|
||||
os.Exit(0)
|
||||
}
|
||||
@@ -802,20 +699,19 @@ func resetCallback() {
|
||||
Using '-reset' will delete all nuclei configurations files and all nuclei-templates
|
||||
|
||||
Following files will be deleted:
|
||||
1. All config files at %v
|
||||
2. All cache files (including resume state) at %v
|
||||
3. All nuclei-templates at %v
|
||||
1. All Config + Resumes files at %v
|
||||
2. All nuclei-templates at %v
|
||||
|
||||
Note: Make sure you have backup of your custom nuclei-templates before proceeding
|
||||
|
||||
`, config.DefaultConfig.GetConfigDir(), config.DefaultConfig.GetCacheDir(), config.DefaultConfig.TemplatesDirectory)
|
||||
options.Logger.Print().Msg(warning)
|
||||
`, config.DefaultConfig.GetConfigDir(), config.DefaultConfig.TemplatesDirectory)
|
||||
gologger.Print().Msg(warning)
|
||||
reader := bufio.NewReader(os.Stdin)
|
||||
for {
|
||||
fmt.Print("Are you sure you want to continue? [y/n]: ")
|
||||
resp, err := reader.ReadString('\n')
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("could not read response: %s", err)
|
||||
gologger.Fatal().Msgf("could not read response: %s", err)
|
||||
}
|
||||
resp = strings.TrimSpace(resp)
|
||||
if stringsutil.EqualFoldAny(resp, "y", "yes") {
|
||||
@@ -828,17 +724,13 @@ Note: Make sure you have backup of your custom nuclei-templates before proceedin
|
||||
}
|
||||
err := os.RemoveAll(config.DefaultConfig.GetConfigDir())
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("could not delete config dir: %s", err)
|
||||
}
|
||||
err = os.RemoveAll(config.DefaultConfig.GetCacheDir())
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("could not delete cache dir: %s", err)
|
||||
gologger.Fatal().Msgf("could not delete config dir: %s", err)
|
||||
}
|
||||
err = os.RemoveAll(config.DefaultConfig.TemplatesDirectory)
|
||||
if err != nil {
|
||||
options.Logger.Fatal().Msgf("could not delete templates dir: %s", err)
|
||||
gologger.Fatal().Msgf("could not delete templates dir: %s", err)
|
||||
}
|
||||
options.Logger.Info().Msgf("Successfully deleted all nuclei configurations files and nuclei-templates")
|
||||
gologger.Info().Msgf("Successfully deleted all nuclei configurations files and nuclei-templates")
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
@@ -858,58 +750,14 @@ func findProfilePathById(profileId, templatesDir string) string {
|
||||
return nil
|
||||
})
|
||||
if err != nil && err.Error() != "FOUND" {
|
||||
options.Logger.Error().Msgf("%s\n", err)
|
||||
gologger.Error().Msgf("%s\n", err)
|
||||
}
|
||||
return profilePath
|
||||
}
|
||||
|
||||
// profileSecrets is a helper struct to extract secrets section from a template profile YAML
|
||||
type profileSecrets struct {
|
||||
Secrets interface{} `yaml:"secrets"`
|
||||
}
|
||||
|
||||
// processInlineSecretsFromProfile parses the profile YAML file for inline secrets
|
||||
// and creates a temporary secrets file compatible with nuclei's auth provider.
|
||||
// Returns the path to the temp file or empty string if no secrets found.
|
||||
func processInlineSecretsFromProfile(profilePath string, options *types.Options) (string, error) {
|
||||
data, err := os.ReadFile(profilePath)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not read profile file: %w", err)
|
||||
func init() {
|
||||
// print stacktrace of errors in debug mode
|
||||
if strings.EqualFold(os.Getenv("DEBUG"), "true") {
|
||||
errorutil.ShowStackTrace = true
|
||||
}
|
||||
|
||||
var profile profileSecrets
|
||||
if err := yaml.Unmarshal(data, &profile); err != nil {
|
||||
return "", fmt.Errorf("could not parse profile YAML: %w", err)
|
||||
}
|
||||
|
||||
if profile.Secrets == nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
secretsData, err := yaml.Marshal(profile.Secrets)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not marshal inline secrets: %w", err)
|
||||
}
|
||||
|
||||
tempDir := filepath.Join(os.TempDir(), "nuclei-secrets")
|
||||
if err := os.MkdirAll(tempDir, 0700); err != nil {
|
||||
return "", fmt.Errorf("could not create temp directory: %w", err)
|
||||
}
|
||||
|
||||
tempFile, err := os.CreateTemp(tempDir, "inline-secrets-*.yaml")
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("could not create temp secrets file: %w", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = tempFile.Close()
|
||||
}()
|
||||
|
||||
if _, err := tempFile.Write(secretsData); err != nil {
|
||||
_ = tempFile.Close()
|
||||
_ = os.Remove(tempFile.Name())
|
||||
return "", fmt.Errorf("could not write to temp secrets file: %w", err)
|
||||
}
|
||||
|
||||
options.SecretsFile = append(options.SecretsFile, tempFile.Name())
|
||||
return tempFile.Name(), nil
|
||||
}
|
||||
@@ -1,13 +1,9 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -24,6 +20,7 @@ var (
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set up
|
||||
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent)
|
||||
_ = os.Setenv("DISABLE_STDOUT", "true")
|
||||
|
||||
@@ -52,31 +49,6 @@ func TestMain(m *testing.M) {
|
||||
os.Exit(exitCode)
|
||||
}
|
||||
|
||||
// getUniqFilename generates a unique filename by appending .N if file exists
|
||||
// Similar to wget's behavior: file.cpu.prof, file.cpu.1.prof, file.cpu.2.prof, etc.
|
||||
func getUniqFilename(basePath string) string {
|
||||
if _, err := os.Stat(basePath); os.IsNotExist(err) {
|
||||
return basePath
|
||||
}
|
||||
|
||||
lastDot := strings.LastIndex(basePath, ".")
|
||||
var name, ext string
|
||||
if lastDot != -1 {
|
||||
name = basePath[:lastDot]
|
||||
ext = basePath[lastDot:]
|
||||
} else {
|
||||
name = basePath
|
||||
ext = ""
|
||||
}
|
||||
|
||||
for i := 1; ; i++ {
|
||||
newPath := fmt.Sprintf("%s.%d%s", name, i, ext)
|
||||
if _, err := os.Stat(newPath); os.IsNotExist(err) {
|
||||
return newPath
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func getDefaultOptions() *types.Options {
|
||||
return &types.Options{
|
||||
RemoteTemplateDomainList: []string{"cloud.projectdiscovery.io"},
|
||||
@@ -121,8 +93,6 @@ func getDefaultOptions() *types.Options {
|
||||
LoadHelperFileFunction: types.DefaultOptions().LoadHelperFileFunction,
|
||||
// DialerKeepAlive: time.Duration(0),
|
||||
// DASTServerAddress: "localhost:9055",
|
||||
ExecutionId: "test",
|
||||
Logger: gologger.DefaultLogger,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,55 +105,24 @@ func runEnumBenchmark(b *testing.B, options *types.Options) {
|
||||
}
|
||||
defer nucleiRunner.Close()
|
||||
|
||||
benchNameSlug := strings.ReplaceAll(b.Name(), "/", "-")
|
||||
|
||||
// Start CPU profiling
|
||||
cpuProfileBase := fmt.Sprintf("%s.cpu.prof", benchNameSlug)
|
||||
cpuProfilePath := getUniqFilename(cpuProfileBase)
|
||||
cpuProfile, err := os.Create(cpuProfilePath)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create CPU profile: %s", err)
|
||||
}
|
||||
defer func() { _ = cpuProfile.Close() }()
|
||||
|
||||
if err := pprof.StartCPUProfile(cpuProfile); err != nil {
|
||||
b.Fatalf("failed to start CPU profile: %s", err)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
|
||||
b.ResetTimer()
|
||||
b.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
for i := 0; i < b.N; i++ {
|
||||
if err := nucleiRunner.RunEnumeration(); err != nil {
|
||||
b.Fatalf("%s failed: %s", b.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
|
||||
// Write heap profile
|
||||
heapProfileBase := fmt.Sprintf("%s.heap.prof", benchNameSlug)
|
||||
heapProfilePath := getUniqFilename(heapProfileBase)
|
||||
heapProfile, err := os.Create(heapProfilePath)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create heap profile: %s", err)
|
||||
}
|
||||
defer func() { _ = heapProfile.Close() }()
|
||||
|
||||
runtime.GC() // Force GC before heap profile
|
||||
if err := pprof.WriteHeapProfile(heapProfile); err != nil {
|
||||
b.Fatalf("failed to write heap profile: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRunEnumeration(b *testing.B) {
|
||||
// Default case: run enumeration with default options == all nuclei-templates
|
||||
b.Run("Default", func(b *testing.B) {
|
||||
options := getDefaultOptions()
|
||||
options.Targets = []string{targetURL}
|
||||
// b.Run("Default", func(b *testing.B) {
|
||||
// options := getDefaultOptions()
|
||||
// options.Targets = []string{targetURL}
|
||||
|
||||
runEnumBenchmark(b, options)
|
||||
})
|
||||
// runEnumBenchmark(b, options)
|
||||
// })
|
||||
|
||||
// Case: https://github.com/projectdiscovery/nuclei/pull/6258
|
||||
b.Run("Multiproto", func(b *testing.B) {
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
func TestProcessInlineSecretsFromProfile(t *testing.T) {
|
||||
t.Run("profile with inline secrets", func(t *testing.T) {
|
||||
profileContent := `
|
||||
secrets:
|
||||
static:
|
||||
- type: header
|
||||
domains:
|
||||
- api.example.com
|
||||
headers:
|
||||
- key: x-api-key
|
||||
value: test-key-123
|
||||
`
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "test-profile-*.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create temp file: %v", err)
|
||||
}
|
||||
if _, err := tmpFile.WriteString(profileContent); err != nil {
|
||||
t.Fatalf("could not write profile: %v", err)
|
||||
}
|
||||
_ = tmpFile.Close()
|
||||
|
||||
opts := &types.Options{}
|
||||
tempPath, err := processInlineSecretsFromProfile(tmpFile.Name(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("processInlineSecretsFromProfile failed: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(tempPath)
|
||||
}()
|
||||
|
||||
if len(opts.SecretsFile) != 1 {
|
||||
t.Fatalf("expected 1 secrets file, got %d", len(opts.SecretsFile))
|
||||
}
|
||||
|
||||
secretsPath := opts.SecretsFile[0]
|
||||
if !strings.Contains(secretsPath, "inline-secrets-") {
|
||||
t.Errorf("secrets file path should contain 'inline-secrets-', got %s", secretsPath)
|
||||
}
|
||||
|
||||
data, err := os.ReadFile(secretsPath)
|
||||
if err != nil {
|
||||
t.Fatalf("could not read generated secrets file: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
if !strings.Contains(content, "x-api-key") {
|
||||
t.Errorf("secrets file should contain header key, got:\n%s", content)
|
||||
}
|
||||
if !strings.Contains(content, "test-key-123") {
|
||||
t.Errorf("secrets file should contain header value, got:\n%s", content)
|
||||
}
|
||||
if !strings.Contains(content, "api.example.com") {
|
||||
t.Errorf("secrets file should contain domain, got:\n%s", content)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("profile without secrets", func(t *testing.T) {
|
||||
profileContent := `
|
||||
severity:
|
||||
- critical
|
||||
- high
|
||||
`
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "test-profile-*.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create temp file: %v", err)
|
||||
}
|
||||
if _, err := tmpFile.WriteString(profileContent); err != nil {
|
||||
t.Fatalf("could not write profile: %v", err)
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
t.Fatalf("could not close temp file: %v", err)
|
||||
}
|
||||
|
||||
opts := &types.Options{}
|
||||
tempPath, err := processInlineSecretsFromProfile(tmpFile.Name(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("processInlineSecretsFromProfile should not fail: %v", err)
|
||||
}
|
||||
|
||||
if tempPath != "" {
|
||||
t.Errorf("expected empty temp path for profile without secrets, got %s", tempPath)
|
||||
}
|
||||
|
||||
if len(opts.SecretsFile) != 0 {
|
||||
t.Errorf("expected 0 secrets files for profile without secrets, got %d", len(opts.SecretsFile))
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("nonexistent profile", func(t *testing.T) {
|
||||
opts := &types.Options{}
|
||||
_, err := processInlineSecretsFromProfile(filepath.Join(t.TempDir(), "nonexistent.yaml"), opts)
|
||||
if err == nil {
|
||||
t.Error("expected error for nonexistent file")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInlineTargetsParsing(t *testing.T) {
|
||||
t.Run("multiline list key treated as inline targets", func(t *testing.T) {
|
||||
opts := &types.Options{
|
||||
TargetsFilePath: "example.com\ntest.com\nscanme.sh\n",
|
||||
}
|
||||
|
||||
if strings.Contains(opts.TargetsFilePath, "\n") {
|
||||
inlineTargets := strings.Split(strings.TrimSpace(opts.TargetsFilePath), "\n")
|
||||
for _, target := range inlineTargets {
|
||||
target = strings.TrimSpace(target)
|
||||
if target != "" && !strings.HasPrefix(target, "#") {
|
||||
opts.Targets = append(opts.Targets, target)
|
||||
}
|
||||
}
|
||||
opts.TargetsFilePath = ""
|
||||
}
|
||||
|
||||
if len(opts.Targets) != 3 {
|
||||
t.Fatalf("expected 3 targets, got %d: %v", len(opts.Targets), opts.Targets)
|
||||
}
|
||||
if opts.Targets[0] != "example.com" {
|
||||
t.Errorf("expected first target 'example.com', got '%s'", opts.Targets[0])
|
||||
}
|
||||
if opts.Targets[1] != "test.com" {
|
||||
t.Errorf("expected second target 'test.com', got '%s'", opts.Targets[1])
|
||||
}
|
||||
if opts.Targets[2] != "scanme.sh" {
|
||||
t.Errorf("expected third target 'scanme.sh', got '%s'", opts.Targets[2])
|
||||
}
|
||||
if opts.TargetsFilePath != "" {
|
||||
t.Errorf("TargetsFilePath should be cleared, got '%s'", opts.TargetsFilePath)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("single line list key remains as file path", func(t *testing.T) {
|
||||
opts := &types.Options{
|
||||
TargetsFilePath: "/path/to/targets.txt",
|
||||
}
|
||||
|
||||
if strings.Contains(opts.TargetsFilePath, "\n") {
|
||||
t.Error("single-line path should not be treated as inline targets")
|
||||
}
|
||||
|
||||
if opts.TargetsFilePath != "/path/to/targets.txt" {
|
||||
t.Errorf("file path should remain unchanged, got '%s'", opts.TargetsFilePath)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("inline targets with comments and blank lines", func(t *testing.T) {
|
||||
opts := &types.Options{
|
||||
TargetsFilePath: "example.com\n# this is a comment\n\ntest.com\n",
|
||||
}
|
||||
|
||||
if strings.Contains(opts.TargetsFilePath, "\n") {
|
||||
inlineTargets := strings.Split(strings.TrimSpace(opts.TargetsFilePath), "\n")
|
||||
for _, target := range inlineTargets {
|
||||
target = strings.TrimSpace(target)
|
||||
if target != "" && !strings.HasPrefix(target, "#") {
|
||||
opts.Targets = append(opts.Targets, target)
|
||||
}
|
||||
}
|
||||
opts.TargetsFilePath = ""
|
||||
}
|
||||
|
||||
if len(opts.Targets) != 2 {
|
||||
t.Fatalf("expected 2 targets (comments/blanks filtered), got %d: %v", len(opts.Targets), opts.Targets)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("targets-inline key", func(t *testing.T) {
|
||||
opts := &types.Options{
|
||||
InlineTargetsList: "host1.com\nhost2.com\nhost3.com",
|
||||
}
|
||||
|
||||
if opts.InlineTargetsList != "" {
|
||||
inlineTargets := strings.Split(strings.TrimSpace(opts.InlineTargetsList), "\n")
|
||||
for _, target := range inlineTargets {
|
||||
target = strings.TrimSpace(target)
|
||||
if target != "" && !strings.HasPrefix(target, "#") {
|
||||
opts.Targets = append(opts.Targets, target)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(opts.Targets) != 3 {
|
||||
t.Fatalf("expected 3 targets from targets-inline, got %d", len(opts.Targets))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestInlineSecretsFileFormat(t *testing.T) {
|
||||
// Verify the generated secrets file has the correct YAML structure
|
||||
// that matches what authx.GetAuthDataFromYAML expects
|
||||
profileContent := `
|
||||
secrets:
|
||||
static:
|
||||
- type: header
|
||||
domains:
|
||||
- api.example.com
|
||||
headers:
|
||||
- key: Authorization
|
||||
value: Bearer test-token
|
||||
dynamic:
|
||||
- template: oauth-flow.yaml
|
||||
variables:
|
||||
- key: username
|
||||
value: testuser
|
||||
type: cookie
|
||||
domains:
|
||||
- auth.example.com
|
||||
`
|
||||
tmpFile, err := os.CreateTemp(t.TempDir(), "test-secrets-*.yaml")
|
||||
if err != nil {
|
||||
t.Fatalf("could not create temp file: %v", err)
|
||||
}
|
||||
if _, err := tmpFile.WriteString(profileContent); err != nil {
|
||||
t.Fatalf("could not write profile: %v", err)
|
||||
}
|
||||
if err := tmpFile.Close(); err != nil {
|
||||
t.Fatalf("could not close temp file: %v", err)
|
||||
}
|
||||
|
||||
opts := &types.Options{}
|
||||
tempPath, err := processInlineSecretsFromProfile(tmpFile.Name(), opts)
|
||||
if err != nil {
|
||||
t.Fatalf("processInlineSecretsFromProfile failed: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = os.Remove(tempPath)
|
||||
}()
|
||||
|
||||
data, err := os.ReadFile(opts.SecretsFile[0])
|
||||
if err != nil {
|
||||
t.Fatalf("could not read secrets file: %v", err)
|
||||
}
|
||||
|
||||
content := string(data)
|
||||
// The secrets section should contain static and dynamic at root level,
|
||||
// matching the Authx struct yaml tags
|
||||
if !strings.Contains(content, "static:") {
|
||||
t.Errorf("secrets file should have 'static:' key for Authx compatibility, got:\n%s", content)
|
||||
}
|
||||
if !strings.Contains(content, "dynamic:") {
|
||||
t.Errorf("secrets file should have 'dynamic:' key for Authx compatibility, got:\n%s", content)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
id: basic-dns-a-example
|
||||
|
||||
info:
|
||||
name: Test DNS A Query Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
dns:
|
||||
- name: "{{FQDN}}"
|
||||
type: SRV
|
||||
class: inet
|
||||
recursion: true
|
||||
retries: 3
|
||||
matchers:
|
||||
- type: word
|
||||
part: all
|
||||
words:
|
||||
- "SRV"
|
||||
+31
-31
@@ -23,7 +23,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
@@ -146,8 +146,8 @@ func process(opts options) error {
|
||||
gologger.Fatal().Msgf("could not open error log file: %s\n", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = errFile.Close()
|
||||
}()
|
||||
_ = errFile.Close()
|
||||
}()
|
||||
}
|
||||
|
||||
templateCatalog := disk.NewCatalog(filepath.Dir(opts.input))
|
||||
@@ -196,14 +196,14 @@ func process(opts options) error {
|
||||
}
|
||||
|
||||
if opts.format {
|
||||
formattedTemplateData, isFormatted, err := formatTemplate(dataString)
|
||||
formatedTemplateData, isFormated, err := formatTemplate(dataString)
|
||||
if err != nil {
|
||||
gologger.Info().Label("format").Msg(logErrMsg(path, err, opts.debug, errFile))
|
||||
} else {
|
||||
if isFormatted {
|
||||
_ = os.WriteFile(path, []byte(formattedTemplateData), 0644)
|
||||
dataString = formattedTemplateData
|
||||
gologger.Info().Label("format").Msgf("✅ formatted template: %s\n", path)
|
||||
if isFormated {
|
||||
_ = os.WriteFile(path, []byte(formatedTemplateData), 0644)
|
||||
dataString = formatedTemplateData
|
||||
gologger.Info().Label("format").Msgf("✅ formated template: %s\n", path)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -243,7 +243,7 @@ func enhanceTemplate(data string) (string, bool, error) {
|
||||
return data, false, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return data, false, errkit.New("unexpected status code: %v", resp.Status)
|
||||
return data, false, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
}
|
||||
var templateResp TemplateResp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&templateResp); err != nil {
|
||||
@@ -254,20 +254,20 @@ func enhanceTemplate(data string) (string, bool, error) {
|
||||
}
|
||||
if templateResp.ValidateErrorCount > 0 {
|
||||
if len(templateResp.ValidateError) > 0 {
|
||||
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
return data, false, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("validation failed", "tag", "validate")
|
||||
return data, false, errorutil.New("validation failed").WithTag("validate")
|
||||
}
|
||||
if templateResp.Error.Name != "" {
|
||||
return data, false, errkit.New("%s", templateResp.Error.Name)
|
||||
return data, false, errorutil.New("%s", templateResp.Error.Name)
|
||||
}
|
||||
if strings.TrimSpace(templateResp.Enhanced) == "" && !templateResp.Lint {
|
||||
if templateResp.LintError.Reason != "" {
|
||||
return data, false, errkit.New(templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
return data, false, errorutil.NewWithTag("lint", "at line: %v", templateResp.LintError.Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("template enhance failed")
|
||||
return data, false, errorutil.New("template enhance failed")
|
||||
}
|
||||
|
||||
// formatTemplate formats template data using templateman format api
|
||||
@@ -277,7 +277,7 @@ func formatTemplate(data string) (string, bool, error) {
|
||||
return data, false, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return data, false, errkit.New("unexpected status code: %v", resp.Status)
|
||||
return data, false, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
}
|
||||
var templateResp TemplateResp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&templateResp); err != nil {
|
||||
@@ -288,20 +288,20 @@ func formatTemplate(data string) (string, bool, error) {
|
||||
}
|
||||
if templateResp.ValidateErrorCount > 0 {
|
||||
if len(templateResp.ValidateError) > 0 {
|
||||
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
return data, false, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("validation failed", "tag", "validate")
|
||||
return data, false, errorutil.New("validation failed").WithTag("validate")
|
||||
}
|
||||
if templateResp.Error.Name != "" {
|
||||
return data, false, errkit.New("%s", templateResp.Error.Name)
|
||||
return data, false, errorutil.New("%s", templateResp.Error.Name)
|
||||
}
|
||||
if strings.TrimSpace(templateResp.Updated) == "" && !templateResp.Lint {
|
||||
if templateResp.LintError.Reason != "" {
|
||||
return data, false, errkit.New(templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
return data, false, errorutil.NewWithTag("lint", "at line: %v", templateResp.LintError.Mark.Line)
|
||||
}
|
||||
return data, false, errkit.New("template format failed")
|
||||
return data, false, errorutil.New("template format failed")
|
||||
}
|
||||
|
||||
// lintTemplate lints template data using templateman lint api
|
||||
@@ -311,7 +311,7 @@ func lintTemplate(data string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return false, errkit.New("unexpected status code: %v", resp.Status)
|
||||
return false, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
}
|
||||
var lintResp TemplateLintResp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&lintResp); err != nil {
|
||||
@@ -321,9 +321,9 @@ func lintTemplate(data string) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
if lintResp.LintError.Reason != "" {
|
||||
return false, errkit.New(lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line, "tag", "lint")
|
||||
return false, errorutil.NewWithTag("lint", lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line)
|
||||
}
|
||||
return false, errkit.New("at line: %v", lintResp.LintError.Mark.Line, "tag", "lint")
|
||||
return false, errorutil.NewWithTag("lint", "at line: %v", lintResp.LintError.Mark.Line)
|
||||
}
|
||||
|
||||
// validateTemplate validates template data using templateman validate api
|
||||
@@ -333,7 +333,7 @@ func validateTemplate(data string) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
if resp.StatusCode != 200 {
|
||||
return false, errkit.New("unexpected status code: %v", resp.Status)
|
||||
return false, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
}
|
||||
var validateResp TemplateResp
|
||||
if err := json.NewDecoder(resp.Body).Decode(&validateResp); err != nil {
|
||||
@@ -344,14 +344,14 @@ func validateTemplate(data string) (bool, error) {
|
||||
}
|
||||
if validateResp.ValidateErrorCount > 0 {
|
||||
if len(validateResp.ValidateError) > 0 {
|
||||
return false, errkit.New(validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
return false, errorutil.NewWithTag("validate", validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line)
|
||||
}
|
||||
return false, errkit.New("validation failed", "tag", "validate")
|
||||
return false, errorutil.New("validation failed").WithTag("validate")
|
||||
}
|
||||
if validateResp.Error.Name != "" {
|
||||
return false, errkit.New("%s", validateResp.Error.Name)
|
||||
return false, errorutil.New("%s", validateResp.Error.Name)
|
||||
}
|
||||
return false, errkit.New("template validation failed")
|
||||
return false, errorutil.New("template validation failed")
|
||||
}
|
||||
|
||||
// parseAndAddMaxRequests parses and adds max requests to templates
|
||||
@@ -401,7 +401,7 @@ func parseAndAddMaxRequests(catalog catalog.Catalog, path, data string) (string,
|
||||
|
||||
// parseTemplate parses a template and returns the template object
|
||||
func parseTemplate(catalog catalog.Catalog, templatePath string) (*templates.Template, error) {
|
||||
executorOpts := &protocols.ExecutorOptions{
|
||||
executorOpts := protocols.ExecutorOptions{
|
||||
Catalog: catalog,
|
||||
Options: defaultOpts,
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
|
||||
_ "github.com/mattn/go-sqlite3"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/fuzzplayground"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils/fuzzplayground"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
@@ -52,17 +52,6 @@ func main() {
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to create signer: %s", err)
|
||||
}
|
||||
certData, err := os.ReadFile(cert)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to read certificate file %s: %s", cert, err)
|
||||
}
|
||||
tmplVerifier, err := signer.NewTemplateSigVerifier(certData)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to create verifier: %s", err)
|
||||
}
|
||||
if err := signer.AddSignerToDefault(tmplVerifier); err != nil {
|
||||
gologger.Fatal().Msgf("failed to register verifier: %s", err)
|
||||
}
|
||||
gologger.Info().Msgf("Template Signer: %v\n", tmplSigner.Identifier())
|
||||
|
||||
// read file
|
||||
@@ -106,24 +95,16 @@ func main() {
|
||||
gologger.Info().Msgf("------------------------")
|
||||
gologger.Info().Msgf("Signature: %s", sig2)
|
||||
gologger.Info().Msgf("Content Hash (SHA256): %s\n", hex.EncodeToString(hash2[:]))
|
||||
|
||||
tmpl, err = templates.Parse(template, nil, execOpts)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("failed to parse signed template: %s", err)
|
||||
}
|
||||
}
|
||||
if !tmpl.Verified {
|
||||
gologger.Fatal().Msg("template signature could not be verified with the provided certificate")
|
||||
}
|
||||
gologger.Info().Msgf("✓ Template signed & verified successfully")
|
||||
}
|
||||
|
||||
func defaultExecutorOpts(templatePath string) *protocols.ExecutorOptions {
|
||||
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{
|
||||
executerOpts := protocols.ExecutorOptions{
|
||||
Catalog: catalog,
|
||||
Options: options,
|
||||
TemplatePath: templatePath,
|
||||
|
||||
Executable
+105
@@ -0,0 +1,105 @@
|
||||
#!/bin/bash
|
||||
|
||||
# This script is used to retry failed workflows in github actions.
|
||||
# It uses gh cli to fetch the failed workflows and then rerun them.
|
||||
# It also checks the logs of the failed workflows to see if it is a flaky test.
|
||||
# If it is a flaky test, it will rerun the failed jobs in the workflow.
|
||||
# eg:
|
||||
# ./gh_retry.sh -h to see the help.
|
||||
# ./gh_retry.sh will run the script with default values.
|
||||
|
||||
# You can also pass the following arguments:
|
||||
# ./gh_retry.sh -b master -l 30 -t "30 mins ago" -w "Build Test"
|
||||
|
||||
#Initialize variables to default values.
|
||||
BRANCH=$(git symbolic-ref --short HEAD)
|
||||
LIMIT=30
|
||||
BEFORE="30 mins ago"
|
||||
WORKFLOW="Build Test"
|
||||
|
||||
# You can add multiple patterns separated by |
|
||||
GREP_ERROR_PATTERN='Test "http/interactsh.yaml" failed'
|
||||
|
||||
#Set fonts for Help.
|
||||
NORM=$(tput sgr0)
|
||||
BOLD=$(tput bold)
|
||||
REV=$(tput smso)
|
||||
|
||||
HELP()
|
||||
{
|
||||
# Display Help
|
||||
echo "Script to retry failed workflows in github actions."
|
||||
echo
|
||||
echo "Syntax: scriptTemplate [-b]"
|
||||
echo "options:"
|
||||
echo "${REV}-b${NORM} Branch to check failed workflows/jobs. Default is ${BOLD}$BRANCH${NORM}."
|
||||
echo "${REV}-l${NORM} Maximum number of runs to fetch. Default is ${BOLD}$LIMIT${NORM}."
|
||||
echo "${REV}-t${NORM} Time to filter the failed jobs. Default is ${BOLD}$BEFORE${NORM}."
|
||||
echo "${REV}-w${NORM} Workflow to filter the failed jobs. Default is ${BOLD}$WORKFLOW${NORM}."
|
||||
echo
|
||||
}
|
||||
|
||||
while getopts :b:l:t:w:h FLAG; do
|
||||
case $FLAG in
|
||||
b)
|
||||
BRANCH=$OPTARG
|
||||
;;
|
||||
l)
|
||||
LIMIT=$OPTARG
|
||||
;;
|
||||
t)
|
||||
BEFORE=$OPTARG
|
||||
;;
|
||||
w)
|
||||
WORKFLOW=$OPTARG
|
||||
;;
|
||||
h) #show help
|
||||
HELP
|
||||
exit 0
|
||||
;;
|
||||
\?) #unrecognized option - show help
|
||||
echo -e \\n"Option -${BOLD}$OPTARG${NORM} not allowed."
|
||||
HELP
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
shift $((OPTIND-1))
|
||||
|
||||
function print_bold() {
|
||||
echo "${BOLD}$1${NORM}"
|
||||
}
|
||||
|
||||
function retry_failed_jobs() {
|
||||
print_bold "Checking failed workflows for branch $BRANCH before $BEFORE"
|
||||
|
||||
date=$(date +%Y-%m-%d'T'%H:%M'Z' -d "$BEFORE")
|
||||
|
||||
workflowIds=$(gh run list --limit "$LIMIT" --json headBranch,status,name,conclusion,databaseId,updatedAt | jq -c '.[] |
|
||||
select ( .headBranch==$branch ) |
|
||||
select ( .name | contains($workflow) ) |
|
||||
select ( .conclusion=="failure" ) |
|
||||
select ( .updatedAt > $date) ' --arg date "$date" --arg branch "$BRANCH" --arg workflow "$WORKFLOW" | jq .databaseId)
|
||||
|
||||
# convert line separated by space to array
|
||||
eval "arr=($workflowIds)"
|
||||
|
||||
if [[ -z $arr ]]
|
||||
then
|
||||
print_bold "Could not find any failed workflows in the last $BEFORE"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
for s in "${arr[@]}"; do
|
||||
print_bold "Checking logs of failed workflow $s to see if it is a flaky test"
|
||||
gh run view "$s" --log-failed | grep -E "$GREP_ERROR_PATTERN" > /dev/null
|
||||
if [ $? == 0 ] ; then
|
||||
print_bold "Retrying failed jobs $s"
|
||||
gh run rerun "$s" --failed
|
||||
sleep 10s
|
||||
gh run view "$s"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
retry_failed_jobs
|
||||
@@ -1,11 +1,11 @@
|
||||
module github.com/projectdiscovery/nuclei/v3
|
||||
|
||||
go 1.25.7
|
||||
go 1.24.1
|
||||
|
||||
require (
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
|
||||
github.com/andygrunwald/go-jira v1.16.1
|
||||
github.com/antchfx/htmlquery v1.3.5
|
||||
github.com/andygrunwald/go-jira v1.16.0
|
||||
github.com/antchfx/htmlquery v1.3.4
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-rod/rod v0.116.2
|
||||
@@ -16,16 +16,16 @@ require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/olekukonko/tablewriter v1.0.8
|
||||
github.com/miekg/dns v1.1.66
|
||||
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.5.6
|
||||
github.com/projectdiscovery/hmap v0.0.100
|
||||
github.com/projectdiscovery/interactsh v1.3.1
|
||||
github.com/projectdiscovery/fastdialer v0.4.1
|
||||
github.com/projectdiscovery/hmap v0.0.91
|
||||
github.com/projectdiscovery/interactsh v1.2.4
|
||||
github.com/projectdiscovery/rawhttp v0.1.90
|
||||
github.com/projectdiscovery/retryabledns v1.0.114
|
||||
github.com/projectdiscovery/retryablehttp-go v1.3.8
|
||||
github.com/projectdiscovery/retryabledns v1.0.103
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.116
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.6
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.6.0
|
||||
@@ -35,48 +35,45 @@ require (
|
||||
github.com/spf13/cast v1.9.2
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
github.com/weppos/publicsuffix-go v0.50.3
|
||||
github.com/weppos/publicsuffix-go v0.40.3-0.20250311103038-7794c8c0723b
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/net v0.52.0
|
||||
golang.org/x/oauth2 v0.34.0
|
||||
golang.org/x/text v0.35.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/text v0.26.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
carvel.dev/ytt v0.52.0
|
||||
code.gitea.io/sdk/gitea v0.17.0
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
|
||||
github.com/Azure/go-ntlmssp v0.1.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
|
||||
github.com/DataDog/gostackparse v0.7.0
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057
|
||||
github.com/Mzack9999/go-rsync v0.0.0-20250821180103-81ffa574ef4d
|
||||
github.com/Mzack9999/goja v0.0.0-20250507184235-e46100e9c697
|
||||
github.com/Mzack9999/goja_nodejs v0.0.0-20250507184139-66bcbf65c883
|
||||
github.com/alexsnet/go-vnc v0.1.0
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/alitto/pond v1.9.2
|
||||
github.com/antchfx/xmlquery v1.4.4
|
||||
github.com/antchfx/xpath v1.3.6
|
||||
github.com/antchfx/xpath v1.3.4
|
||||
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
|
||||
github.com/aws/aws-sdk-go-v2 v1.41.5
|
||||
github.com/aws/aws-sdk-go-v2 v1.36.5
|
||||
github.com/aws/aws-sdk-go-v2/config v1.29.17
|
||||
github.com/aws/aws-sdk-go-v2/credentials v1.17.70
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.82
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.99.0
|
||||
github.com/bytedance/sonic v1.15.0
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.82.0
|
||||
github.com/bytedance/sonic v1.13.3
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/charmbracelet/glamour v0.10.0
|
||||
github.com/clbanning/mxj/v2 v2.7.0
|
||||
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c
|
||||
github.com/docker/go-units v0.5.0
|
||||
github.com/dop251/goja v0.0.0-20250624190929-4d26883d182a
|
||||
github.com/dop251/goja_nodejs v0.0.0-20250409162600-f7acab6894b0
|
||||
github.com/fatih/structs v1.1.0
|
||||
github.com/getkin/kin-openapi v0.132.0
|
||||
github.com/go-git/go-git/v5 v5.17.1
|
||||
github.com/go-echarts/go-echarts/v2 v2.6.0
|
||||
github.com/go-git/go-git/v5 v5.16.2
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-pdf/fpdf v0.9.0
|
||||
github.com/go-pg/pg/v10 v10.15.0
|
||||
github.com/go-pg/pg v8.0.7+incompatible
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -86,182 +83,169 @@ require (
|
||||
github.com/kitabisa/go-ci v1.0.3
|
||||
github.com/labstack/echo/v4 v4.13.4
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa
|
||||
github.com/lib/pq v1.11.2
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.28
|
||||
github.com/maypok86/otter/v2 v2.2.1
|
||||
github.com/mholt/archives v0.1.5
|
||||
github.com/mholt/archives v0.1.3
|
||||
github.com/microsoft/go-mssqldb v1.9.2
|
||||
github.com/ory/dockertest/v3 v3.12.0
|
||||
github.com/praetorian-inc/fingerprintx v1.1.15
|
||||
github.com/projectdiscovery/dsl v0.8.14
|
||||
github.com/projectdiscovery/dsl v0.5.0
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/gcache v0.0.0-20241015120333-12546c6e3f4c
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
|
||||
github.com/projectdiscovery/goflags v0.1.74
|
||||
github.com/projectdiscovery/gologger v1.1.68
|
||||
github.com/projectdiscovery/gologger v1.1.54
|
||||
github.com/projectdiscovery/gostruct v0.0.2
|
||||
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81
|
||||
github.com/projectdiscovery/httpx v1.9.0
|
||||
github.com/projectdiscovery/mapcidr v1.1.97
|
||||
github.com/projectdiscovery/gozero v0.0.3
|
||||
github.com/projectdiscovery/httpx v1.7.0
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/networkpolicy v0.1.36
|
||||
github.com/projectdiscovery/ratelimit v0.0.85
|
||||
github.com/projectdiscovery/networkpolicy v0.1.17
|
||||
github.com/projectdiscovery/ratelimit v0.0.81
|
||||
github.com/projectdiscovery/rdap v0.9.0
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.2.2
|
||||
github.com/projectdiscovery/uncover v1.2.0
|
||||
github.com/projectdiscovery/useragent v0.0.107
|
||||
github.com/projectdiscovery/utils v0.10.1
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.76
|
||||
github.com/projectdiscovery/tlsx v1.1.9
|
||||
github.com/projectdiscovery/uncover v1.1.0
|
||||
github.com/projectdiscovery/useragent v0.0.101
|
||||
github.com/projectdiscovery/utils v0.4.21
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.35
|
||||
github.com/redis/go-redis/v9 v9.11.0
|
||||
github.com/seh-msft/burpxml v1.0.1
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
|
||||
github.com/sijms/go-ora/v2 v2.9.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
github.com/stretchr/testify v1.10.0
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
|
||||
github.com/testcontainers/testcontainers-go v0.38.0
|
||||
github.com/testcontainers/testcontainers-go/modules/mongodb v0.37.0
|
||||
github.com/trivago/tgo v1.0.7
|
||||
github.com/yassinebenaid/godump v0.11.1
|
||||
github.com/zmap/zgrab2 v0.1.8
|
||||
gitlab.com/gitlab-org/api/client-go v0.130.1
|
||||
go.mongodb.org/mongo-driver v1.17.9
|
||||
golang.org/x/term v0.41.0
|
||||
go.mongodb.org/mongo-driver v1.17.4
|
||||
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
|
||||
golang.org/x/term v0.32.0
|
||||
golang.org/x/tools v0.34.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
|
||||
require (
|
||||
aead.dev/minisign v0.3.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // indirect
|
||||
filippo.io/edwards25519 v1.1.1 // indirect
|
||||
aead.dev/minisign v0.2.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
filippo.io/edwards25519 v1.1.0 // indirect
|
||||
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a // indirect
|
||||
github.com/42wim/httpsig v1.2.2 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
|
||||
github.com/BurntSushi/toml v1.3.2 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/ProtonMail/go-crypto v1.1.6 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.10.3 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/akrylysov/pogreb v0.10.2 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.20.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
|
||||
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
|
||||
github.com/andybalholm/cascadia v1.3.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.8 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.22 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.7 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.9.13 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.21 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.36 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.7.4 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.18.17 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sso v1.25.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.30.3 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.34.0 // indirect
|
||||
github.com/aws/smithy-go v1.24.2 // indirect
|
||||
github.com/aws/smithy-go v1.22.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/aymerick/douceur v0.2.0 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.22.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.5.0 // indirect
|
||||
github.com/bodgit/plumbing v1.3.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.0 // indirect
|
||||
github.com/bodgit/windows v1.0.1 // indirect
|
||||
github.com/brianvoe/gofakeit/v7 v7.2.1 // indirect
|
||||
github.com/buger/jsonparser v1.1.2 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic/loader v0.5.0 // indirect
|
||||
github.com/caddyserver/certmagic v0.25.0 // indirect
|
||||
github.com/caddyserver/zerossl v0.1.3 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic/loader v0.2.4 // indirect
|
||||
github.com/caddyserver/certmagic v0.19.2 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
|
||||
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||
github.com/censys/censys-sdk-go v0.19.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.3.2 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250908092851-c2208eb08494 // indirect
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
|
||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.1.7 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.1.4 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
github.com/cloudflare/circl v1.6.3 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/cnf/structhash v0.0.0-20250313080605-df4c6cc74a9a // indirect
|
||||
github.com/cloudflare/circl v1.6.1 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
github.com/containerd/platforms v0.2.1 // indirect
|
||||
github.com/cpuguy83/dockercfg v0.3.2 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.5.1 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/distribution/reference v0.6.0 // indirect
|
||||
github.com/djherbis/times v1.6.0 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/docker/cli v29.2.0+incompatible // indirect
|
||||
github.com/docker/docker v28.3.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.0 // indirect
|
||||
github.com/docker/cli v27.4.1+incompatible // indirect
|
||||
github.com/docker/docker v27.1.1+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/ebitengine/purego v0.10.0 // indirect
|
||||
github.com/emirpasic/gods v1.18.1 // indirect
|
||||
github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 // indirect
|
||||
github.com/fatih/color v1.18.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/felixge/fgprof v0.9.5 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/free5gc/util v1.0.5-0.20230511064842-2e120956883b // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
|
||||
github.com/gaissmai/bart v0.26.1 // indirect
|
||||
github.com/gaissmai/bart v0.20.4 // indirect
|
||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-fed/httpsig v1.1.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-ole/go-ole v1.3.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.6.2 // indirect
|
||||
github.com/go-ole/go-ole v1.2.6 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-pg/zerochecker v0.2.0 // indirect
|
||||
github.com/go-playground/locales v0.14.1 // indirect
|
||||
github.com/go-playground/universal-translator v0.18.1 // indirect
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.5.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
|
||||
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/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
|
||||
github.com/golang/snappy v1.0.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.3.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.4 // indirect
|
||||
github.com/google/go-github/v30 v30.1.0 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/gosimple/slug v1.15.0 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
|
||||
github.com/hashicorp/go-multierror v1.1.1 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hashicorp/go-uuid v1.0.3 // indirect
|
||||
github.com/hashicorp/go-version v1.8.0 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf // indirect
|
||||
github.com/hdm/jarm-go v0.0.7 // indirect
|
||||
github.com/iangcarroll/cookiemonster v1.6.0 // indirect
|
||||
github.com/imdario/mergo v0.3.16 // indirect
|
||||
github.com/imdario/mergo v0.3.13 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.6 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jcmturner/aescts/v2 v2.0.0 // indirect
|
||||
@@ -270,102 +254,84 @@ require (
|
||||
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
|
||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect
|
||||
github.com/kaiakz/ubuffer v0.0.0-20200803053910-dd1083087166 // indirect
|
||||
github.com/kataras/jwt v0.1.10 // indirect
|
||||
github.com/kevinburke/ssh_config v1.2.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
|
||||
github.com/klauspost/compress v1.18.0 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/labstack/gommon v0.4.2 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/libdns/libdns v1.1.1 // indirect
|
||||
github.com/libdns/libdns v0.2.1 // indirect
|
||||
github.com/logrusorgru/aurora/v4 v4.0.0 // indirect
|
||||
github.com/lor00x/goldap v0.0.0-20240304151906-8d785c64d1c8 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20250827001030-24949be3fa54 // indirect
|
||||
github.com/mackerelio/go-osstat v0.2.6 // indirect
|
||||
github.com/magiconair/properties v1.8.10 // indirect
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mackerelio/go-osstat v0.2.4 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-colorable v0.1.14 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mholt/acmez/v3 v3.1.3 // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||
github.com/mikelolasagasti/xz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.1 // indirect
|
||||
github.com/minio/minlz v1.0.0 // indirect
|
||||
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/go-archive v0.1.0 // indirect
|
||||
github.com/moby/moby/api v1.53.0 // indirect
|
||||
github.com/moby/moby/client v0.2.2 // indirect
|
||||
github.com/moby/patternmatcher v0.6.0 // indirect
|
||||
github.com/moby/sys/sequential v0.6.0 // indirect
|
||||
github.com/moby/sys/user v0.4.0 // indirect
|
||||
github.com/moby/sys/userns v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
github.com/moby/sys/user v0.3.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/morikuni/aec v1.0.0 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.2 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
|
||||
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
|
||||
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
|
||||
github.com/olekukonko/errors v1.1.0 // indirect
|
||||
github.com/olekukonko/ll v0.0.9 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opencontainers/runc v1.2.8 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.0 // indirect
|
||||
github.com/opencontainers/runc v1.2.3 // indirect
|
||||
github.com/openrdap/rdap v0.9.1 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.23 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.2 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.1 // indirect
|
||||
github.com/projectdiscovery/blackrock v0.0.1 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.2.31 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.1.15 // 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-20250715113114-c77eb3567582 // indirect
|
||||
github.com/refraction-networking/utls v1.8.2 // indirect
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
|
||||
github.com/refraction-networking/utls v1.7.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/sashabaranov/go-openai v1.37.0 // indirect
|
||||
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/shirou/gopsutil/v4 v4.26.3 // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.3.1 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // indirect
|
||||
github.com/tidwall/btree v1.8.1 // indirect
|
||||
github.com/tidwall/buntdb v1.3.2 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.5 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/tidwall/buntdb v1.3.1 // indirect
|
||||
github.com/tidwall/gjson v1.18.0 // indirect
|
||||
github.com/tidwall/grect v0.1.4 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tidwall/rtred v0.1.2 // indirect
|
||||
github.com/tidwall/tinyqueue v0.1.1 // indirect
|
||||
github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.16 // indirect
|
||||
github.com/tklauser/numcpus v0.11.0 // indirect
|
||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/vmihailenco/bufpool v0.1.11 // indirect
|
||||
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
|
||||
github.com/vmihailenco/tagparser v0.1.2 // indirect
|
||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||
github.com/vulncheck-oss/go-exploit v1.51.0 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.3 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
@@ -377,58 +343,33 @@ require (
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/ysmood/fetchup v0.2.3 // indirect
|
||||
github.com/ysmood/got v0.40.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.13 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.6 // indirect
|
||||
github.com/zcalusic/sysinfo v1.1.3 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/zap/exp v0.3.0 // indirect
|
||||
go.yaml.in/yaml/v3 v3.0.4 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
mellium.im/sasl v0.3.2 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
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.2 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/trivago/tgo v1.0.7
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
github.com/ysmood/got v0.40.0 // indirect
|
||||
github.com/ysmood/gson v0.7.3 // indirect
|
||||
github.com/ysmood/leakless v0.9.0 // indirect
|
||||
github.com/yuin/goldmark v1.7.8 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.5 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zcalusic/sysinfo v1.0.2 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20240803002437-3a861682ac77 // indirect
|
||||
go.etcd.io/bbolt v1.4.3 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20240512203510-0fef58d9a9db // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
|
||||
goftp.io/server/v2 v2.0.1 // indirect
|
||||
golang.org/x/crypto v0.49.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621
|
||||
golang.org/x/mod v0.33.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.42.0
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/crypto v0.39.0 // indirect
|
||||
golang.org/x/mod v0.25.0 // indirect
|
||||
golang.org/x/sync v0.15.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
|
||||
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/alecthomas/chroma v0.10.0
|
||||
github.com/go-echarts/go-echarts/v2 v2.6.0
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
mellium.im/sasl v0.3.2 // indirect
|
||||
)
|
||||
|
||||
// https://go.dev/ref/mod#go-mod-file-retract
|
||||
|
||||
-21
@@ -1,21 +0,0 @@
|
||||
module nuclei-tools
|
||||
|
||||
go 1.26.1
|
||||
|
||||
tool (
|
||||
github.com/dvyukov/go-fuzz/go-fuzz
|
||||
github.com/dvyukov/go-fuzz/go-fuzz-build
|
||||
golang.org/x/vuln/cmd/govulncheck
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c // indirect
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
|
||||
github.com/stephens2424/writerset v1.0.2 // indirect
|
||||
golang.org/x/mod v0.34.0 // indirect
|
||||
golang.org/x/sync v0.20.0 // indirect
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c // indirect
|
||||
golang.org/x/tools v0.43.0 // indirect
|
||||
golang.org/x/vuln v1.1.4 // indirect
|
||||
)
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
github.com/Julusian/godocdown v0.0.0-20170816220326-6d19f8ff2df8/go.mod h1:INZr5t32rG59/5xeltqoCJoNY7e5x/3xoY9WSWVWg74=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c h1:oLpHpHwNuAPvw3bBviEZNrJbigNNi5dRadfZnagGgZI=
|
||||
github.com/dvyukov/go-fuzz v0.0.0-20240924070022-e577bee5275c/go.mod h1:11Gm+ccJnvAhCNLlf5+cS9KjtbaD5I5zaZpFMsTHWTw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1 h1:m0kkaHRKEu7tUIUFVwhGGGYClXvyl4RE03qmvRTNfbw=
|
||||
github.com/elazarl/go-bindata-assetfs v1.0.1/go.mod h1:v+YaWX3bdea5J/mo8dSETolEo7R71Vk1u8bnjau5yw4=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/robertkrimen/godocdown v0.0.0-20130622164427-0bfa04905481/go.mod h1:C9WhFzY47SzYBIvzFqSvHIR6ROgDo4TtdTuRaOMjF/s=
|
||||
github.com/stephens2424/writerset v1.0.2 h1:znRLgU6g8RS5euYRcy004XeE4W+Tu44kALzy7ghPif8=
|
||||
github.com/stephens2424/writerset v1.0.2/go.mod h1:aS2JhsMn6eA7e82oNmW4rfsgAOp9COBTTl8mzkwADnc=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
|
||||
golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
|
||||
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c h1:6a8FdnNk6bTXBjR4AGKFgUKuo+7GnR3FX5L7CbveeZc=
|
||||
golang.org/x/telemetry v0.0.0-20260311193753-579e4da9a98c/go.mod h1:TpUTTEp9frx7rTdLpC9gFG9kdI7zVLFTFFlqaH2Cncw=
|
||||
golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
|
||||
golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
|
||||
golang.org/x/vuln v1.1.4 h1:Ju8QsuyhX3Hk8ma3CesTbO8vfJD9EvUBgHvkxHBzj0I=
|
||||
golang.org/x/vuln v1.1.4/go.mod h1:F+45wmU18ym/ca5PLTPLsSzr2KppzswxPP603ldA67s=
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [ $1 = "-h" ]; then
|
||||
echo "Help for ./debug.sh"
|
||||
printf "\n1. To run all integration tests of 'x' protocol:\n"
|
||||
printf " \$ ./debug.sh http\n\n"
|
||||
printf "2. To run all integration tests of 'x' protocol that contains 'y' in template name:\n"
|
||||
printf " \$ ./debug.sh http self\n\n"
|
||||
printf "3. To run all integration tests of 'x' protocol that contains 'y' in template name and pass extra args to nuclei:\n"
|
||||
printf " \$ ./debug.sh http self -svd -debug-req\n\n"
|
||||
printf "nuclei binary is created every time script is run but integration-test binary is not"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# Stop execution if race condition is found
|
||||
export GORACE="halt_on_error=1"
|
||||
|
||||
echo "::group::Build nuclei"
|
||||
rm nuclei 2>/dev/null
|
||||
cd ../cmd/nuclei
|
||||
go build -race .
|
||||
mv nuclei ../../integration_tests/nuclei
|
||||
echo -e "::endgroup::\n"
|
||||
cd ../../integration_tests
|
||||
cmdstring=""
|
||||
|
||||
if [ -n "$1" ]; then
|
||||
cmdstring+=" -protocol $1 "
|
||||
fi
|
||||
|
||||
if [ -n "$2" ]; then
|
||||
cmdstring+=" -template $2 "
|
||||
fi
|
||||
|
||||
# Parse any extra args that are directly passed to nuclei
|
||||
if [ -n $debugArgs ]; then
|
||||
export DebugExtraArgs="${@:3}"
|
||||
fi
|
||||
|
||||
echo "$DebugExtraArgs"
|
||||
|
||||
echo -e "::group::Run Integration Test\n"
|
||||
./integration-test $cmdstring
|
||||
|
||||
if [ $? -eq 0 ]
|
||||
then
|
||||
echo -e "::endgroup::\n"
|
||||
exit 0
|
||||
else
|
||||
exit 1
|
||||
fi
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 781 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user