mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 02:08:33 -10:00
Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13cb549351 |
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
|
||||
@@ -1,85 +0,0 @@
|
||||
name: Bug Report
|
||||
description: Create a report to help us to improve the Nuclei.
|
||||
title: "[BUG] ..."
|
||||
labels: ["Type: Bug"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this bug report!
|
||||
|
||||
For support requests, FAQs or "How to" questions, please use the [GitHub Discussions](https://github.com/projectdiscovery/nuclei/discussions) section instead or join our [Discord server](https://discord.gg/projectdiscovery) to discuss the idea on the **#nuclei** channel.
|
||||
|
||||
:warning: **Issues missing important information may be closed without further investigation.**
|
||||
- type: checkboxes
|
||||
attributes:
|
||||
label: Is there an existing issue for this?
|
||||
description: Please search to see if an issue already exists for the bug you encountered.
|
||||
options:
|
||||
- label: I have searched the existing issues.
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Current Behavior
|
||||
description: A concise description of what you're experiencing.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Expected Behavior
|
||||
description: A concise description of what you expected to happen.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Steps To Reproduce
|
||||
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.
|
||||
|
||||
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
|
||||
placeholder: |
|
||||
1. Run `nuclei -t ...`
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Relevant log output
|
||||
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.
|
||||
|
||||
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
|
||||
render: shell
|
||||
- type: textarea
|
||||
attributes:
|
||||
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)_
|
||||
value: |
|
||||
- OS:
|
||||
- Nuclei:
|
||||
- Go:
|
||||
render: markdown
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
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!
|
||||
|
||||
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
|
||||
validations:
|
||||
required: false
|
||||
@@ -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
|
||||
@@ -0,0 +1,21 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Request feature to implement in this project
|
||||
title: ""
|
||||
labels: 'Type: Enhancement'
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
1. Please make sure to provide a detailed description with all the relevant information that might be required to start working on this feature.
|
||||
2. In case you are not sure about your request or whether the particular feature is already supported or not, please start a discussion instead.
|
||||
3. GitHub Discussion: https://github.com/projectdiscovery/nuclei/discussions/categories/ideas
|
||||
4. Join our discord server at https://discord.gg/projectdiscovery to discuss the idea on the #nuclei channel.
|
||||
-->
|
||||
|
||||
### Please describe your feature request:
|
||||
<!-- A clear and concise description of feature to implement -->
|
||||
|
||||
### Describe the use case of this feature:
|
||||
<!-- A clear and concise description of the feature request's motivation and the use-cases in which it could be useful. -->
|
||||
@@ -0,0 +1,36 @@
|
||||
---
|
||||
name: Issue report
|
||||
about: Create a report to help us to improve the project
|
||||
labels: 'Type: Bug'
|
||||
|
||||
---
|
||||
|
||||
<!--
|
||||
1. Please search to see if an issue already exists for the bug you encountered.
|
||||
2. For support requests, FAQs or "How to" questions, please use the GitHub Discussions section instead - https://github.com/projectdiscovery/nuclei/discussions or
|
||||
3. Join our discord server at https://discord.gg/projectdiscovery and post the question on the #nuclei channel.
|
||||
-->
|
||||
|
||||
<!-- ISSUES MISSING IMPORTANT INFORMATION MAY BE CLOSED WITHOUT INVESTIGATION. -->
|
||||
|
||||
### Nuclei version:
|
||||
<!-- You can find current version of nuclei with "nuclei -version" -->
|
||||
<!-- We only accept issues that are reproducible on the latest version of nuclei. -->
|
||||
<!-- You can find the latest version of project at https://github.com/projectdiscovery/nuclei/releases/ -->
|
||||
|
||||
### Current Behavior:
|
||||
<!-- A concise description of what you're experiencing. -->
|
||||
|
||||
### Expected Behavior:
|
||||
<!-- A concise description of what you expected to happen. -->
|
||||
|
||||
### Steps To Reproduce:
|
||||
<!--
|
||||
Example: steps to reproduce the behavior:
|
||||
1. Run 'nuclei -t ... -u ..'
|
||||
2. See error...
|
||||
-->
|
||||
|
||||
|
||||
### Anything else:
|
||||
<!-- Links? References? Screenshots? Anything that will give us more context about the issue that you are encountering! -->
|
||||
@@ -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
|
||||
@@ -1,37 +0,0 @@
|
||||
name: Feature Request
|
||||
description: Request feature to implement in the Nuclei.
|
||||
title: "[FEATURE] ..."
|
||||
labels: ["Type: Enhancement"]
|
||||
body:
|
||||
- type: markdown
|
||||
attributes:
|
||||
value: |
|
||||
Thanks for taking the time to fill out this feature request!
|
||||
|
||||
Please make sure to provide a detailed description with all the relevant information that might be required to start working on this feature. In case you are not sure about your request or whether the particular feature is already supported or not, please [start a discussion](https://github.com/projectdiscovery/nuclei/discussions/categories/ideas) instead.
|
||||
|
||||
Join our [Discord server](https://discord.gg/projectdiscovery) to discuss the idea on the **#nuclei** channel.
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe your feature request
|
||||
description: A clear and concise description of feature to implement.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe the use case of the feature
|
||||
description: A clear and concise description of the feature request's motivation and the use-cases in which it could be useful.
|
||||
validations:
|
||||
required: true
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Describe alternatives you've considered
|
||||
description: A clear and concise description of any alternative solutions or features you've considered.
|
||||
validations:
|
||||
required: false
|
||||
- type: textarea
|
||||
attributes:
|
||||
label: Additional context
|
||||
description: Add any other context about the feature request here.
|
||||
validations:
|
||||
required: false
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
addReviewers: true
|
||||
reviewers:
|
||||
- dogancanbakir
|
||||
- dwisiswant0
|
||||
- mzack9999
|
||||
|
||||
numberOfReviewers: 1
|
||||
skipKeywords:
|
||||
- '@dependabot'
|
||||
+21
-24
@@ -1,5 +1,12 @@
|
||||
# To get started with Dependabot version updates, you'll need to specify which
|
||||
# package ecosystems to update and where the package manifests are located.
|
||||
# Please see the documentation for all configuration options:
|
||||
# https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates
|
||||
|
||||
version: 2
|
||||
updates:
|
||||
|
||||
# Maintain dependencies for go modules
|
||||
- package-ecosystem: "gomod"
|
||||
directory: "/"
|
||||
schedule:
|
||||
@@ -8,33 +15,23 @@ updates:
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
labels:
|
||||
- "Type: Maintenance"
|
||||
allow:
|
||||
- dependency-name: "github.com/projectdiscovery/*"
|
||||
groups:
|
||||
modules:
|
||||
patterns: ["github.com/projectdiscovery/*"]
|
||||
security:
|
||||
applies-to: "security-updates"
|
||||
patterns: ["*"]
|
||||
exclude-patterns: ["github.com/projectdiscovery/*"]
|
||||
labels:
|
||||
- "Type: Maintenance"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
target-branch: "dev"
|
||||
commit-message:
|
||||
prefix: "chore"
|
||||
include: "scope"
|
||||
groups:
|
||||
workflows:
|
||||
patterns: ["*"]
|
||||
exclude-patterns: ["projectdiscovery/actions/*"]
|
||||
labels:
|
||||
- "Type: Maintenance"
|
||||
|
||||
# # Maintain dependencies for GitHub Actions
|
||||
# - package-ecosystem: "github-actions"
|
||||
# directory: "/"
|
||||
# schedule:
|
||||
# interval: "weekly"
|
||||
# target-branch: "dev"
|
||||
# commit-message:
|
||||
# prefix: "chore"
|
||||
# include: "scope"
|
||||
# labels:
|
||||
# - "Type: Maintenance"
|
||||
#
|
||||
# # Maintain dependencies for docker
|
||||
# - package-ecosystem: "docker"
|
||||
# directory: "/"
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# Number of days of inactivity before an issue becomes stale
|
||||
daysUntilStale: 7
|
||||
|
||||
# Number of days of inactivity before a stale issue is closed
|
||||
daysUntilClose: 7
|
||||
|
||||
# Issues with these labels will never be considered stale
|
||||
# exemptLabels:
|
||||
# - pinned
|
||||
# - security
|
||||
|
||||
# Only issues or pull requests with all of these labels are check if stale.
|
||||
onlyLabels:
|
||||
- "Status: Abandoned"
|
||||
- "Type: Question"
|
||||
|
||||
# Label to use when marking as stale
|
||||
staleLabel: stale
|
||||
|
||||
# Comment to post when marking an issue as stale. Set to `false` to disable
|
||||
markComment: >
|
||||
This issue has been automatically marked as stale because it has not had
|
||||
recent activity. It will be closed if no further activity occurs. Thank you
|
||||
for your contributions.
|
||||
|
||||
# Comment to post when closing a stale issue. Set to `false` to disable
|
||||
closeComment: false
|
||||
@@ -1,24 +0,0 @@
|
||||
name: 🔀 Auto merge PR
|
||||
|
||||
on:
|
||||
# pull_request:
|
||||
# types: [opened, synchronize, reopened, ready_for_review]
|
||||
# pull_request_review:
|
||||
# types: [submitted]
|
||||
workflow_run:
|
||||
workflows: ["♾️ Compatibility Checks"]
|
||||
types: [completed]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
auto-merge-dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.event.workflow_run.conclusion == 'success' }}
|
||||
steps:
|
||||
- uses: projectdiscovery/actions/pr/approve@v1
|
||||
- uses: projectdiscovery/actions/pr/merge@v1
|
||||
with:
|
||||
auto: "true"
|
||||
@@ -0,0 +1,74 @@
|
||||
name: 🔨 Build Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Builds
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x]
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Set up Python # required for running python code in py-snippet.yaml integration test
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.10'
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Go Mod hygiene
|
||||
run: |
|
||||
go clean -modcache
|
||||
go mod tidy
|
||||
|
||||
- name: Build
|
||||
run: go build .
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
- name: Test
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
run: go test ./...
|
||||
|
||||
- name: Integration Tests
|
||||
timeout-minutes: 50
|
||||
env:
|
||||
GH_ACTION: true
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
run: |
|
||||
chmod +x run.sh
|
||||
bash run.sh ${{ matrix.os }}
|
||||
working-directory: integration_tests/
|
||||
|
||||
- name: Race Condition Tests
|
||||
if: ${{ matrix.os != 'windows-latest' }} # known issue: https://github.com/golang/go/issues/46099
|
||||
run: go run -race . -l ../functional-test/targets.txt -id tech-detect,tls-version
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
- name: Example SDK Simple
|
||||
run: go run .
|
||||
working-directory: examples/simple/
|
||||
|
||||
- name: Example SDK Advanced
|
||||
run: go run .
|
||||
working-directory: examples/advanced/
|
||||
|
||||
- name: Example SDK with speed control
|
||||
run: go run .
|
||||
working-directory: examples/with_speed_control/
|
||||
@@ -0,0 +1,39 @@
|
||||
name: 🚨 CodeQL Analysis
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
analyze:
|
||||
name: Analyze
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
language: [ 'go' ]
|
||||
# CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ]
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@v2
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@v2
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@v2
|
||||
@@ -1,20 +0,0 @@
|
||||
name: ♾️ Compatibility Checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize]
|
||||
branches:
|
||||
- dev
|
||||
|
||||
jobs:
|
||||
compat-checks:
|
||||
if: ${{ github.actor == 'dependabot[bot]' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go/compat-checks@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
release-test: true
|
||||
@@ -0,0 +1,26 @@
|
||||
name: 🤖 dep auto merge
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
repository-projects: write
|
||||
|
||||
jobs:
|
||||
automerge:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
|
||||
- uses: ahmadnassri/action-dependabot-auto-merge@v2
|
||||
with:
|
||||
github-token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
target: all
|
||||
@@ -0,0 +1,47 @@
|
||||
name: 🌥 Docker Push
|
||||
|
||||
on:
|
||||
workflow_run:
|
||||
workflows: ["🎉 Release Binary"]
|
||||
types:
|
||||
- completed
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docker:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- name: Git Checkout
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Get GitHub tag
|
||||
id: meta
|
||||
run: |
|
||||
curl --silent "https://api.github.com/repos/projectdiscovery/nuclei/releases/latest" | jq -r .tag_name | xargs -I {} echo TAG={} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v2
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v2
|
||||
|
||||
- name: Login to DockerHub
|
||||
uses: docker/login-action@v2
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
|
||||
- name: Build and push
|
||||
uses: docker/build-push-action@v4
|
||||
with:
|
||||
context: .
|
||||
platforms: linux/amd64,linux/arm64
|
||||
push: true
|
||||
tags: projectdiscovery/nuclei:latest,projectdiscovery/nuclei:${{ steps.meta.outputs.TAG }}
|
||||
|
||||
- name: Update DockerHub Description
|
||||
uses: peter-evans/dockerhub-description@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
repository: projectdiscovery/nuclei
|
||||
@@ -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 }}"
|
||||
@@ -0,0 +1,33 @@
|
||||
name: 🧪 Functional Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
|
||||
jobs:
|
||||
functional:
|
||||
name: Functional Test
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Functional Tests
|
||||
env:
|
||||
GH_ACTION: true
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
run: |
|
||||
chmod +x run.sh
|
||||
bash run.sh ${{ matrix.os }}
|
||||
working-directory: cmd/functional-test
|
||||
@@ -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
|
||||
@@ -1,27 +0,0 @@
|
||||
name: ⏰ Generate Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
publish-docs:
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- run: make syntax-docs
|
||||
- run: git status -s | wc -l | xargs -I {} echo CHANGES={} >> $GITHUB_OUTPUT
|
||||
id: status
|
||||
- uses: projectdiscovery/actions/commit@v1
|
||||
if: steps.status.outputs.CHANGES > 0
|
||||
with:
|
||||
files: |
|
||||
SYNTAX-REFERENCE.md
|
||||
nuclei-jsonschema.json
|
||||
message: 'docs: update syntax & JSON schema 🤖'
|
||||
- run: git push origin $GITHUB_REF
|
||||
@@ -1,50 +0,0 @@
|
||||
name: 👤 Generate PGO
|
||||
|
||||
on:
|
||||
workflow_dispatch: {}
|
||||
workflow_call: {}
|
||||
|
||||
jobs:
|
||||
pgo:
|
||||
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"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
with:
|
||||
name: "pgo"
|
||||
path: "${{ env.PGO_FILE }}"
|
||||
overwrite: true
|
||||
@@ -1,30 +0,0 @@
|
||||
name: 🐛 govulncheck
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Weekly
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
govulncheck:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
env:
|
||||
OUTPUT: "/tmp/results.sarif"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
with:
|
||||
sarif_file: "${{ env.OUTPUT }}"
|
||||
category: "govulncheck"
|
||||
@@ -0,0 +1,27 @@
|
||||
name: 🙏🏻 Lint Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Lint Test
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Run golangci-lint
|
||||
uses: golangci/[email protected]
|
||||
with:
|
||||
version: latest
|
||||
args: --timeout 5m
|
||||
@@ -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,39 +0,0 @@
|
||||
name: 🔨 Performance Regression
|
||||
|
||||
on:
|
||||
workflow_call: {}
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
perf-regression:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
env:
|
||||
BENCH_OUT: "/tmp/bench.out"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
with:
|
||||
path: ./cache
|
||||
key: ${{ runner.os }}-benchmark
|
||||
- uses: benchmark-action/github-action-benchmark@v1
|
||||
with:
|
||||
name: 'RunEnumeration Benchmark'
|
||||
tool: 'go'
|
||||
output-file-path: ${{ env.BENCH_OUT }}
|
||||
external-data-json-path: ./cache/benchmark-data.json
|
||||
fail-on-alert: false
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-on-alert: true
|
||||
summary-always: true
|
||||
- uses: actions/cache/save@v5
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
path: ./cache
|
||||
key: ${{ runner.os }}-benchmark
|
||||
@@ -0,0 +1,36 @@
|
||||
name: 🔨 Performance Test
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
# Weekly
|
||||
- cron: '0 0 * * 0'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Test Performance
|
||||
strategy:
|
||||
matrix:
|
||||
go-version: [1.21.x]
|
||||
os: [ubuntu-latest, macOS-latest]
|
||||
|
||||
runs-on: ${{ matrix.os }}
|
||||
if: github.repository == 'projectdiscovery/nuclei'
|
||||
steps:
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: ${{ matrix.go-version }}
|
||||
|
||||
- name: Check out code
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Go Mod hygine
|
||||
run: |
|
||||
go clean -modcache
|
||||
go mod tidy
|
||||
|
||||
# Max GH exection time 6H => timeout after that
|
||||
- name: Running performance with big list
|
||||
run: go run -race . -l ../functional-test/targets-150.txt
|
||||
working-directory: cmd/nuclei/
|
||||
@@ -0,0 +1,48 @@
|
||||
name: ⏰ Publish Docs
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
docs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: "Set up Go"
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- name: Generate YAML Syntax Documentation
|
||||
id: generate-docs
|
||||
run: |
|
||||
if ! which dstdocgen > /dev/null; then
|
||||
echo -e "Command dstdocgen not found! Installing\c"
|
||||
go install github.com/projectdiscovery/yamldoc-go/cmd/docgen/dstdocgen@main
|
||||
fi
|
||||
go generate pkg/templates/templates.go
|
||||
go build -o "cmd/docgen/docgen" cmd/docgen/docgen.go
|
||||
./cmd/docgen/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
git status -s | wc -l | xargs -I {} echo CHANGES={} >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit files
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
run: |
|
||||
git config --local user.email "[email protected]"
|
||||
git config --local user.name "GitHub Action"
|
||||
git add SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
git commit -m "Auto Generate Syntax Docs + JSONSchema [$(date)] :robot:" -a
|
||||
|
||||
- name: Push changes
|
||||
if: steps.generate-docs.outputs.CHANGES > 0
|
||||
uses: ad-m/github-push-action@master
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
branch: ${{ github.ref }}
|
||||
@@ -0,0 +1,30 @@
|
||||
name: 🎉 Release Binary
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: "release --clean"
|
||||
version: latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}"
|
||||
DISCORD_WEBHOOK_ID: "${{ secrets.DISCORD_WEBHOOK_ID }}"
|
||||
DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}"
|
||||
@@ -0,0 +1,28 @@
|
||||
name: 🔨 Release Test
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
release-test:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- name: "Check out code"
|
||||
uses: actions/checkout@v3
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set up Go
|
||||
uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- name: release test
|
||||
uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: "release --clean --snapshot"
|
||||
version: latest
|
||||
@@ -1,40 +0,0 @@
|
||||
name: 🎉 Release
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- '*'
|
||||
workflow_dispatch: {}
|
||||
|
||||
jobs:
|
||||
pgo:
|
||||
name: "Generate PGO"
|
||||
uses: ./.github/workflows/generate-pgo.yaml
|
||||
|
||||
release:
|
||||
name: "Release"
|
||||
needs: ["pgo"]
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: actions/download-artifact@v8
|
||||
with:
|
||||
name: "pgo"
|
||||
path: "cmd/nuclei/"
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: docker/login-action@v4
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_TOKEN }}
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
with:
|
||||
release: true
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
SLACK_WEBHOOK: "${{ secrets.RELEASE_SLACK_WEBHOOK }}"
|
||||
DISCORD_WEBHOOK_ID: "${{ secrets.DISCORD_WEBHOOK_ID }}"
|
||||
DISCORD_WEBHOOK_TOKEN: "${{ secrets.DISCORD_WEBHOOK_TOKEN }}"
|
||||
@@ -1,40 +0,0 @@
|
||||
name: 💤 Stale
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 0' # Weekly
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write # only for delete-branch option
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
stale-issue-label: "Status: Stale"
|
||||
stale-pr-label: "Status: Stale"
|
||||
stale-issue-message: >
|
||||
This issue has been automatically marked as stale because it has not
|
||||
had recent activity. It will be closed in 7 days if no further
|
||||
activity occurs. Thank you for your contributions!
|
||||
stale-pr-message: >
|
||||
This pull request has been automatically marked as stale due to
|
||||
inactivity. It will be closed in 7 days if no further activity
|
||||
occurs. Please update if you wish to keep it open.
|
||||
close-issue-message: >
|
||||
This issue has been automatically closed due to inactivity. If you
|
||||
think this is a mistake or would like to continue the discussion,
|
||||
please comment or feel free to reopen it.
|
||||
close-pr-message: >
|
||||
This pull request has been automatically closed due to inactivity.
|
||||
If you think this is a mistake or would like to continue working on
|
||||
it, please comment or feel free to reopen it.
|
||||
close-issue-label: "Status: Abandoned"
|
||||
close-pr-label: "Status: Abandoned"
|
||||
exempt-issue-labels: "Type: Enhancement"
|
||||
@@ -0,0 +1,23 @@
|
||||
name: 🛠 Template Validate
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- uses: actions/setup-go@v4
|
||||
with:
|
||||
go-version: 1.21.x
|
||||
|
||||
- name: Template Validation
|
||||
run: |
|
||||
go run . -ut
|
||||
go run . -validate
|
||||
go run . -validate -w workflows
|
||||
working-directory: cmd/nuclei/
|
||||
@@ -1,168 +0,0 @@
|
||||
name: 🔨 Tests
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: ["dev"]
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.go'
|
||||
- '**.mod'
|
||||
workflow_dispatch:
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: "Lint"
|
||||
if: ${{ !endsWith(github.actor, '[bot]') }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/golangci-lint/v2@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: 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:
|
||||
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
|
||||
if: ${{ matrix.os != 'windows-latest' }} # known issue: https://github.com/golang/go/issues/46099
|
||||
working-directory: cmd/nuclei/
|
||||
|
||||
sdk:
|
||||
name: "Run example SDK"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- 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/
|
||||
|
||||
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: projectdiscovery/actions/setup/go@v1
|
||||
- run: make integration
|
||||
env:
|
||||
PDCP_API_KEY: "${{ secrets.PDCP_API_KEY }}"
|
||||
timeout-minutes: 50
|
||||
|
||||
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: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action@v3
|
||||
with:
|
||||
version: latest
|
||||
install-only: true
|
||||
- run: make functional
|
||||
|
||||
validate:
|
||||
name: "Template validate"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ github.token }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/nuclei-action/cache@v3
|
||||
- run: make template-validate
|
||||
|
||||
codeql:
|
||||
name: "CodeQL analysis"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
actions: read
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: 'go'
|
||||
- uses: github/codeql-action/autobuild@v4
|
||||
- uses: github/codeql-action/analyze@v4
|
||||
|
||||
release:
|
||||
name: "Release test"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
with:
|
||||
go-version: "stable"
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
|
||||
flamegraph:
|
||||
name: "Flamegraph"
|
||||
needs: ["tests"]
|
||||
uses: ./.github/workflows/flamegraph.yaml
|
||||
|
||||
# perf-regression:
|
||||
# name: "Performance 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]
|
||||
+36
-48
@@ -1,55 +1,43 @@
|
||||
# Misc
|
||||
**/*-cache
|
||||
**/*-config
|
||||
**/.cache
|
||||
**/.gofuzz/
|
||||
**/*.DS_Store
|
||||
**/*.exe
|
||||
.devcontainer
|
||||
.gitignore
|
||||
.idea
|
||||
.vscode
|
||||
.claude/settings.local.json
|
||||
# Binaries
|
||||
/bin/*
|
||||
/bindgen
|
||||
/debug-*
|
||||
/docgen
|
||||
/functional-test
|
||||
/fuzzplayground
|
||||
/integration-test
|
||||
/jsdocgen
|
||||
/main
|
||||
/memogen
|
||||
/nuclei
|
||||
/nuclei-stats*
|
||||
/nuclei_dev
|
||||
/nuclei_main
|
||||
/scan-charts
|
||||
/scrapefunc
|
||||
/scrapefuncs
|
||||
/tsgen
|
||||
*.test
|
||||
|
||||
# Templates
|
||||
/*.yaml
|
||||
|
||||
# Generated docs
|
||||
/dsl.md
|
||||
|
||||
# Junk
|
||||
.devcontainer
|
||||
vendor
|
||||
dist
|
||||
integration_tests/nuclei
|
||||
integration_tests/.cache
|
||||
integration_tests/integration-test
|
||||
cmd/nuclei/main
|
||||
cmd/nuclei/nuclei
|
||||
cmd/integration-test/nuclei
|
||||
cmd/functional-test/nuclei_dev
|
||||
cmd/functional-test/nuclei_main
|
||||
cmd/functional-test/functional-test
|
||||
cmd/docgen/docgen
|
||||
pkg/protocols/common/helpers/deserialization/testdata/Deserialize.class
|
||||
pkg/protocols/common/helpers/deserialization/testdata/ValueObject.class
|
||||
pkg/protocols/common/helpers/deserialization/testdata/ValueObject2.ser
|
||||
vendor
|
||||
*.exe
|
||||
.gitignore
|
||||
pkg/js/devtools/bindgen/cmd/bindgen/bindgen
|
||||
pkg/js/devtools/jsdocgen/jsdocgen
|
||||
pkg/js/devtools/tsgen/tsgen
|
||||
pkg/js/devtools/tsgen/cmd/tsgen/tsgen
|
||||
*.DS_Store
|
||||
pkg/protocols/headless/engine/.cache
|
||||
/nuclei
|
||||
/bindgen
|
||||
/jsdocgen
|
||||
/tsgen
|
||||
/scrapefuncs
|
||||
/integration_tests/.cache/
|
||||
/*.yaml
|
||||
**/*-config
|
||||
**/*-cache
|
||||
/fuzzplayground
|
||||
integration_tests/fuzzplayground
|
||||
/dsl.md
|
||||
/nuclei-stats
|
||||
/nuclei-stats-*
|
||||
/scan-charts
|
||||
/**/debug-*
|
||||
|
||||
# Headless `screenshot` action
|
||||
*.png
|
||||
|
||||
# Profiling & tracing
|
||||
*.prof
|
||||
*.pprof
|
||||
*.trace
|
||||
*.mem
|
||||
*.cpu
|
||||
+24
-95
@@ -1,32 +1,27 @@
|
||||
version: 2
|
||||
|
||||
before:
|
||||
hooks:
|
||||
- go mod download
|
||||
- go mod verify
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- main: cmd/nuclei/main.go
|
||||
binary: nuclei
|
||||
id: nuclei-cli
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
- GOEXPERIMENT=greenteagc
|
||||
goos: [windows, linux, darwin]
|
||||
goarch: [amd64, '386', arm, arm64]
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: '386'
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
flags:
|
||||
- -trimpath
|
||||
- -pgo=auto
|
||||
ldflags:
|
||||
- -s
|
||||
- -w
|
||||
- main: cmd/nuclei/main.go
|
||||
binary: nuclei
|
||||
id: nuclei-cli
|
||||
|
||||
env:
|
||||
- CGO_ENABLED=0
|
||||
|
||||
goos: [windows,linux,darwin]
|
||||
goarch: [amd64,386,arm,arm64]
|
||||
ignore:
|
||||
- goos: darwin
|
||||
goarch: 386
|
||||
- goos: windows
|
||||
goarch: arm
|
||||
- goos: windows
|
||||
goarch: arm64
|
||||
|
||||
flags:
|
||||
- -trimpath
|
||||
|
||||
#- main: cmd/tmc/main.go
|
||||
# binary: tmc
|
||||
@@ -39,80 +34,14 @@ builds:
|
||||
# goarch: [amd64]
|
||||
|
||||
archives:
|
||||
- formats: [zip]
|
||||
id: nuclei
|
||||
ids: [nuclei-cli]
|
||||
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ if eq .Os "darwin" }}macOS{{ else }}{{ .Os }}{{ end }}_{{ .Arch }}'
|
||||
- format: zip
|
||||
id: nuclei
|
||||
builds: [nuclei-cli]
|
||||
name_template: '{{ .ProjectName }}_{{ .Version }}_{{ if eq .Os "darwin" }}macOS{{ else }}{{ .Os }}{{ end }}_{{ .Arch }}'
|
||||
|
||||
checksum:
|
||||
algorithm: sha256
|
||||
|
||||
dockers:
|
||||
- image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:latest-amd64"
|
||||
dockerfile: Dockerfile.goreleaser
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--pull"
|
||||
- "--platform=linux/amd64"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.ref.name={{ .Tag }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
goarch: amd64
|
||||
- image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}-arm64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}-arm64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}-arm64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:latest-arm64"
|
||||
dockerfile: Dockerfile.goreleaser
|
||||
use: buildx
|
||||
build_flag_templates:
|
||||
- "--pull"
|
||||
- "--platform=linux/arm64"
|
||||
- "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
- "--label=org.opencontainers.image.ref.name={{ .Tag }}"
|
||||
- "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
- "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
goarch: arm64
|
||||
# # NOTE(dwisiswant0): chromium doesn't support 32-bit on alpine
|
||||
# - image_templates:
|
||||
# - "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}-386"
|
||||
# - "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}-386"
|
||||
# - "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}-386"
|
||||
# - "projectdiscovery/{{ .ProjectName }}:latest-386"
|
||||
# dockerfile: Dockerfile.goreleaser
|
||||
# use: buildx
|
||||
# build_flag_templates:
|
||||
# - "--pull"
|
||||
# - "--platform=linux/386"
|
||||
# - "--label=org.opencontainers.image.created={{ .Date }}"
|
||||
# - "--label=org.opencontainers.image.ref.name={{ .Tag }}"
|
||||
# - "--label=org.opencontainers.image.revision={{ .FullCommit }}"
|
||||
# - "--label=org.opencontainers.image.version={{ .Version }}"
|
||||
# goarch: "386"
|
||||
|
||||
docker_manifests:
|
||||
- name_template: "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}"
|
||||
image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:{{ .Tag }}-arm64"
|
||||
- name_template: "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}"
|
||||
image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}.{{ .Minor }}-arm64"
|
||||
- name_template: "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}"
|
||||
image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:v{{ .Major }}-arm64"
|
||||
- name_template: "projectdiscovery/{{ .ProjectName }}:latest"
|
||||
image_templates:
|
||||
- "projectdiscovery/{{ .ProjectName }}:latest-amd64"
|
||||
- "projectdiscovery/{{ .ProjectName }}:latest-arm64"
|
||||
|
||||
announce:
|
||||
slack:
|
||||
enabled: true
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -381,7 +381,7 @@ func (r *Request) Type() templateTypes.ProtocolType {
|
||||
}
|
||||
```
|
||||
|
||||
Almost all of these protocols have boilerplate functions for which default implementations have been provided in the `providers` package. Examples are the implementation of `Match`, `Extract`, `MakeResultEvent`, `GetCompiledOperators`, etc. which are almost same throughout Nuclei protocols code. It is enough to copy-paste them unless customization is required.
|
||||
Almost all of these protocols have boilerplate functions for which default implementations have been provided in the `providers` package. Examples are the implementation of `Match`, `Extract`, `MakeResultEvent`, GetCompiledOperators`, etc. which are almost same throughout Nuclei protocols code. It is enough to copy-paste them unless customization is required.
|
||||
|
||||
`eventcreator` package offers `CreateEventWithAdditionalOptions` function which can be used to create result events after doing request execution.
|
||||
|
||||
@@ -454,58 +454,28 @@ 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
|
||||
## Profiling Instructions
|
||||
|
||||
To analyze Nuclei's performance and resource usage, you can generate CPU & memory profiles and trace files using the `-profile-mem` flag:
|
||||
To enable dumping of Memory profiling data, `-profile-mem` flag can be used along with path to a file. This writes a pprof formatted file which can be used for investigate resource usage with `pprof` tool.
|
||||
|
||||
```bash
|
||||
nuclei -t nuclei-templates/ -u https://example.com -profile-mem=nuclei-$(git describe --tags)
|
||||
```console
|
||||
$ nuclei -t nuclei-templates/ -u https://example.com -profile-mem mem.pprof
|
||||
```
|
||||
|
||||
This command creates three files:
|
||||
To view profile data in pprof, first install pprof. Then run the below command -
|
||||
|
||||
* `nuclei.cpu`: CPU profile
|
||||
* `nuclei.mem`: Memory (heap) profile
|
||||
* `nuclei.trace`: Execution trace
|
||||
|
||||
### Analyzing the CPU/Memory Profiles
|
||||
|
||||
* View the profile in the terminal:
|
||||
|
||||
```bash
|
||||
go tool pprof nuclei.{cpu,mem}
|
||||
```console
|
||||
$ go tool pprof mem.pprof
|
||||
```
|
||||
|
||||
* Display overall CPU time for processing $$N$$ targets:
|
||||
To open a web UI on a port to visualize debug data, the below command can be used.
|
||||
|
||||
```console
|
||||
$ go tool pprof -http=:8081 mem.pprof
|
||||
```
|
||||
go tool pprof -top nuclei.cpu | grep "Total samples"
|
||||
```
|
||||
|
||||
* Display top memory consumers:
|
||||
|
||||
```bash
|
||||
go tool pprof -top nuclei.mem | grep "$(go list -m)" | head -10
|
||||
```
|
||||
|
||||
* Visualize the profile in a web browser:
|
||||
|
||||
```bash
|
||||
go tool pprof -http=:$(shuf -i 1000-99999 -n 1) nuclei.{cpu,mem}
|
||||
```
|
||||
|
||||
### Analyzing the Trace File
|
||||
|
||||
To examine the execution trace:
|
||||
|
||||
```bash
|
||||
go tool trace nuclei.trace
|
||||
```
|
||||
|
||||
These tools help identify performance bottlenecks and memory leaks, allowing for targeted optimizations of Nuclei's codebase.
|
||||
|
||||
## Project Structure
|
||||
|
||||
|
||||
+9
-9
@@ -1,16 +1,16 @@
|
||||
# Build
|
||||
FROM golang:1.24-alpine AS builder
|
||||
|
||||
FROM golang:1.21-alpine AS build-env
|
||||
RUN apk add build-base
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
RUN make verify
|
||||
RUN make build
|
||||
WORKDIR /app
|
||||
RUN go mod download
|
||||
RUN go build ./cmd/nuclei
|
||||
|
||||
# Release
|
||||
FROM alpine:latest
|
||||
FROM alpine:3.18.6
|
||||
RUN apk -U upgrade --no-cache \
|
||||
&& apk add --no-cache bind-tools chromium ca-certificates
|
||||
COPY --from=build-env /app/nuclei /usr/local/bin/
|
||||
|
||||
RUN apk add --no-cache bind-tools chromium ca-certificates
|
||||
COPY --from=builder /app/bin/nuclei /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["nuclei"]
|
||||
ENTRYPOINT ["nuclei"]
|
||||
@@ -1,12 +0,0 @@
|
||||
FROM alpine:latest
|
||||
|
||||
LABEL org.opencontainers.image.authors="ProjectDiscovery"
|
||||
LABEL org.opencontainers.image.description="Nuclei is a fast, customizable vulnerability scanner powered by the global security community and built on a simple YAML-based DSL, enabling collaboration to tackle trending vulnerabilities on the internet. It helps you find vulnerabilities in your applications, APIs, networks, DNS, and cloud configurations."
|
||||
LABEL org.opencontainers.image.licenses="MIT"
|
||||
LABEL org.opencontainers.image.title="nuclei"
|
||||
LABEL org.opencontainers.image.url="https://github.com/projectdiscovery/nuclei"
|
||||
|
||||
RUN apk add --no-cache bind-tools chromium ca-certificates
|
||||
COPY nuclei /usr/local/bin/
|
||||
|
||||
ENTRYPOINT ["nuclei"]
|
||||
-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.
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 ProjectDiscovery, Inc.
|
||||
Copyright (c) 2022 ProjectDiscovery, Inc.
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,290 +1,62 @@
|
||||
# Go parameters
|
||||
GOCMD := go
|
||||
GOBUILD := $(GOCMD) build
|
||||
GOBUILD_OUTPUT :=
|
||||
GOBUILD_OUTPUT_EXT :=
|
||||
GOBUILD_PACKAGES :=
|
||||
GOBUILD_ADDITIONAL_ARGS :=
|
||||
GOMOD := $(GOCMD) mod
|
||||
GOTEST := $(GOCMD) test
|
||||
GOCMD=go
|
||||
GOBUILD=$(GOCMD) build
|
||||
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"
|
||||
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 scan-charts docs test integration functional tidy devtools jsupdate ts fuzzplayground memogen dsl-docs
|
||||
|
||||
all: build
|
||||
|
||||
clean:
|
||||
rm -f '${GOBUILD_OUTPUT}${GOBUILD_OUTPUT_EXT}' 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)
|
||||
|
||||
build: GOFLAGS = -pgo=auto
|
||||
build: GOBUILD_OUTPUT = ./bin/nuclei
|
||||
build: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
build: go-build
|
||||
|
||||
build-test: GOFLAGS = -v -pgo=auto
|
||||
build-test: GOBUILD_OUTPUT = ./bin/nuclei.test
|
||||
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}
|
||||
|
||||
build-stats: GOBUILD_OUTPUT = ./bin/nuclei-stats
|
||||
build-stats: GOBUILD_PACKAGES = cmd/nuclei/main.go
|
||||
build-stats: GOBUILD_ADDITIONAL_ARGS = -tags=stats
|
||||
build-stats: go-build
|
||||
|
||||
scan-charts: GOBUILD_OUTPUT = ./bin/scan-charts
|
||||
scan-charts: GOBUILD_PACKAGES = cmd/scan-charts/main.go
|
||||
scan-charts: go-build
|
||||
|
||||
template-signer: GOBUILD_OUTPUT = ./bin/template-signer
|
||||
template-signer: GOBUILD_PACKAGES = cmd/tools/signer/main.go
|
||||
template-signer: go-build
|
||||
|
||||
docgen: GOBUILD_OUTPUT = ./bin/docgen
|
||||
docgen: GOBUILD_PACKAGES = cmd/docgen/docgen.go
|
||||
docgen: bin = dstdocgen
|
||||
docgen:
|
||||
@if ! which $(bin) >/dev/null; then \
|
||||
echo "Command $(bin) not found! Installing..."; \
|
||||
go install -v github.com/projectdiscovery/yamldoc-go/cmd/docgen/$(bin)@latest; \
|
||||
fi
|
||||
# TODO: FIX THIS PANIC
|
||||
$(GOCMD) generate pkg/templates/templates.go
|
||||
$(GOBUILD) -o "${GOBUILD_OUTPUT}" $(GOBUILD_PACKAGES)
|
||||
|
||||
docs: docgen
|
||||
build:
|
||||
rm -f nuclei 2>/dev/null
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "nuclei" cmd/nuclei/main.go
|
||||
build-stats:
|
||||
rm -f nuclei-stats 2>/dev/null
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -tags=stats -o "nuclei-stats" cmd/nuclei/main.go
|
||||
scan-charts:
|
||||
rm -f scan-charts 2>/dev/null
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "scan-charts" cmd/scan-charts/main.go
|
||||
docs:
|
||||
./bin/docgen docs.md nuclei-jsonschema.json
|
||||
|
||||
syntax-docs: docgen
|
||||
syntax-docs:
|
||||
./bin/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
|
||||
|
||||
test: GOFLAGS = -race -v -timeout 1h -count 1
|
||||
if ! which dstdocgen > /dev/null; then
|
||||
echo -e "Command not found! Install? (y/n) \c"
|
||||
go get -v github.com/projectdiscovery/yamldoc-go/cmd/docgen/dstdocgen
|
||||
fi
|
||||
$(GOCMD) generate pkg/templates/templates.go
|
||||
$(GOBUILD) -o "cmd/docgen/docgen" cmd/docgen/docgen.go
|
||||
./cmd/docgen/docgen docs.md nuclei-jsonschema.json
|
||||
test:
|
||||
$(GOTEST) $(GOFLAGS) ./...
|
||||
|
||||
integration:
|
||||
$(GOTEST) -tags=integration -timeout 1h ./internal/tests/integration
|
||||
|
||||
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
|
||||
|
||||
cd integration_tests; bash run.sh
|
||||
functional:
|
||||
cd cmd/functional-test; bash run.sh
|
||||
tidy:
|
||||
$(GOMOD) tidy
|
||||
|
||||
download:
|
||||
$(GOMOD) download
|
||||
|
||||
verify: download
|
||||
$(GOMOD) verify
|
||||
|
||||
vet: verify
|
||||
$(GOCMD) vet ./...
|
||||
|
||||
devtools-bindgen: GOBUILD_OUTPUT = ./bin/bindgen
|
||||
devtools-bindgen: GOBUILD_PACKAGES = pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
devtools-bindgen: go-build
|
||||
|
||||
devtools-tsgen: GOBUILD_OUTPUT = ./bin/tsgen
|
||||
devtools-tsgen: GOBUILD_PACKAGES = pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
devtools-tsgen: go-build
|
||||
|
||||
devtools-scrapefuncs: GOBUILD_OUTPUT = ./bin/scrapefuncs
|
||||
devtools-scrapefuncs: GOBUILD_PACKAGES = pkg/js/devtools/scrapefuncs/main.go
|
||||
devtools-scrapefuncs: go-build
|
||||
|
||||
devtools-all: devtools-bindgen devtools-tsgen devtools-scrapefuncs
|
||||
|
||||
jsupdate-bindgen: GOBUILD_OUTPUT = ./bin/bindgen
|
||||
jsupdate-bindgen: GOBUILD_PACKAGES = pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
jsupdate-bindgen: go-build
|
||||
jsupdate-bindgen:
|
||||
./$(GOBUILD_OUTPUT) -dir pkg/js/libs -out pkg/js/generated
|
||||
|
||||
jsupdate-tsgen: GOBUILD_OUTPUT = ./bin/tsgen
|
||||
jsupdate-tsgen: GOBUILD_PACKAGES = pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
jsupdate-tsgen: go-build
|
||||
jsupdate-tsgen:
|
||||
./$(GOBUILD_OUTPUT) -dir pkg/js/libs -out pkg/js/generated/ts
|
||||
|
||||
jsupdate-all: jsupdate-bindgen jsupdate-tsgen
|
||||
|
||||
ts: jsupdate-tsgen
|
||||
|
||||
fuzzplayground: GOBUILD_OUTPUT = ./bin/fuzzplayground
|
||||
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
|
||||
devtools:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "bindgen" pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "tsgen" pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "scrapefuncs" pkg/js/devtools/scrapefuncs/main.go
|
||||
jsupdate:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "bindgen" pkg/js/devtools/bindgen/cmd/bindgen/main.go
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "tsgen" pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
./bindgen -dir pkg/js/libs -out pkg/js/generated
|
||||
./tsgen -dir pkg/js/libs -out pkg/js/generated/ts
|
||||
ts:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "tsgen" pkg/js/devtools/tsgen/cmd/tsgen/main.go
|
||||
./tsgen -dir pkg/js/libs -out pkg/js/generated/ts
|
||||
fuzzplayground:
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "fuzzplayground" cmd/tools/fuzzplayground/main.go
|
||||
memogen:
|
||||
./$(GOBUILD_OUTPUT) -src pkg/js/libs -tpl cmd/memogen/function.tpl
|
||||
|
||||
dsl-docs: GOBUILD_OUTPUT = ./bin/scrapefuncs
|
||||
dsl-docs: GOBUILD_PACKAGES = pkg/js/devtools/scrapefuncs/main.go
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "memogen" cmd/memogen/memogen.go
|
||||
./memogen -src pkg/js/libs -tpl cmd/memogen/function.tpl
|
||||
dsl-docs:
|
||||
./$(GOBUILD_OUTPUT) -out dsl.md
|
||||
|
||||
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
|
||||
rm -f dsl.md scrapefuncs 2>/dev/null
|
||||
$(GOBUILD) $(GOFLAGS) -ldflags '$(LDFLAGS)' -o "scrapefuncs" pkg/js/devtools/scrapefuncs/main.go
|
||||
./scrapefuncs -out dsl.md
|
||||
@@ -1,135 +1,113 @@
|
||||

|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://nuclei.projectdiscovery.io"><img src="static/nuclei-logo.png" width="200px" alt="Nuclei"></a>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Fast and customisable vulnerability scanner based on simple YAML based DSL.</h4>
|
||||
|
||||
<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>
|
||||
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#how-it-works">How</a> •
|
||||
<a href="#install-nuclei">Install</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">Documentation</a> •
|
||||
<a href="#credits">Credits</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">FAQs</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Join Discord</a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<p 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>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
**Nuclei is a modern, high-performance vulnerability scanner that leverages simple YAML-based templates. It empowers you to design custom vulnerability detection scenarios that mimic real-world conditions, leading to zero false positives.**
|
||||
---
|
||||
|
||||
- Simple YAML format for creating and customizing vulnerability templates.
|
||||
- Contributed by thousands of security professionals to tackle trending vulnerabilities.
|
||||
- Reduce false positives by simulating real-world steps to verify a vulnerability.
|
||||
- Ultra-fast parallel scan processing and request clustering.
|
||||
- Integrate into CI/CD pipelines for vulnerability detection and regression testing.
|
||||
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS, JavaScript, Code and more.
|
||||
- Integrate with Jira, Splunk, GitHub, Elastic, GitLab.
|
||||
Nuclei is used to send requests across targets based on a template, leading to zero false positives and providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols, including TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless, Code etc. With powerful and flexible templating, Nuclei can be used to model all kinds of security checks.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
We have a [dedicated repository](https://github.com/projectdiscovery/nuclei-templates) that houses various type of vulnerability templates contributed by **more than 300** security researchers and engineers.
|
||||
|
||||
## Table of Contents
|
||||
## How it works
|
||||
|
||||
- [**`Get Started`**](#get-started)
|
||||
- [_`1. Nuclei CLI`_](#1-nuclei-cli)
|
||||
- [_`2. Pro and Enterprise Editions`_](#2-pro-and-enterprise-editions)
|
||||
- [**`Documentation`**](#documentation)
|
||||
- [_`Command Line Flags`_](#command-line-flags)
|
||||
- [_`Single target scan`_](#single-target-scan)
|
||||
- [_`Scanning multiple targets`_](#scanning-multiple-targets)
|
||||
- [_`Network scan`_](#network-scan)
|
||||
- [_`Scanning with your custom template`_](#scanning-with-your-custom-template)
|
||||
- [_`Connect Nuclei to ProjectDiscovery_`_](#connect-nuclei-to-projectdiscovery)
|
||||
- [**`Nuclei Templates, Community and Rewards`**](#nuclei-templates-community-and-rewards-) 💎
|
||||
- [**`Our Mission`**](#our-mission)
|
||||
- [**`Contributors`**](#contributors-heart) ❤
|
||||
- [**`License`**](#license)
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
|
||||
## Get Started
|
||||
|
||||
### **1. Nuclei CLI**
|
||||
| :exclamation: **Disclaimer** |
|
||||
|---------------------------------|
|
||||
| **This project is in active development**. Expect breaking changes with releases. Review the release changelog before updating. |
|
||||
| This project was primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
|
||||
_Install Nuclei on your machine. Get started by following the installation guide [**`here`**](https://docs.projectdiscovery.io/tools/nuclei/install?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme). Additionally, We provide [**`a free cloud tier`**](https://cloud.projectdiscovery.io/sign-up) and comes with a generous monthly free limits:_
|
||||
# Install Nuclei
|
||||
|
||||
- Store and visualize your vulnerability findings
|
||||
- Write and manage your nuclei templates
|
||||
- Access latest nuclei templates
|
||||
- Discover and store your targets
|
||||
|
||||
> [!Important]
|
||||
> |**This project is in active development**. Expect breaking changes with releases. Review the release changelog before updating.|
|
||||
> |:--------------------------------|
|
||||
> | This project is primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
|
||||
<br>
|
||||
|
||||
### **2. Pro and Enterprise Editions**
|
||||
|
||||
_For security teams and enterprises, we provide a cloud-hosted service built on top of Nuclei OSS, fine-tuned to help you continuously run vulnerability scans at scale with your team and existing workflows:_
|
||||
|
||||
- 50x faster scans
|
||||
- Large scale scanning with high accuracy
|
||||
- Integrations with cloud services (AWS, GCP, Azure, Cloudflare, Fastly, Terraform, Kubernetes)
|
||||
- Jira, Slack, Linear, APIs and Webhooks
|
||||
- Executive and compliance reporting
|
||||
- Plus: Real-time scanning, SAML SSO, SOC 2 compliant platform (with EU and US hosting options), shared team workspaces, and more
|
||||
- We're constantly [**`adding new features`**](https://feedback.projectdiscovery.io/changelog)!
|
||||
- **Ideal for:** Pentesters, security teams, and enterprises
|
||||
|
||||
[**`Sign up to Pro`**](https://projectdiscovery.io/pricing?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme) or [**`Talk to our team`**](https://projectdiscovery.io/request-demo?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme) if you have large organization and complex requirements.
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## 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).
|
||||
|
||||
<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>
|
||||
|
||||
### Installation
|
||||
|
||||
`nuclei` requires **go >= 1.24.2** to install successfully. Run the following command to get the repo:
|
||||
Nuclei requires **go1.21** to install successfully. Run the following command to install the latest version -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
To learn more about installing nuclei, see `https://docs.projectdiscovery.io/tools/nuclei/install`.
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
### Command Line Flags
|
||||
**More installation [methods can be found here](https://docs.projectdiscovery.io/tools/nuclei/install).**
|
||||
|
||||
To display all the flags for the tool:
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
### Nuclei Templates
|
||||
|
||||
Nuclei has built-in support for automatic template download/update as default since version [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2). [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) project provides a community-contributed list of ready-to-use templates that is constantly updated.
|
||||
|
||||
You may still use the `update-templates` flag to update the nuclei templates at any time; You can write your own checks for your individual workflow and needs following Nuclei's [templating guide](https://docs.projectdiscovery.io/templates/).
|
||||
|
||||
The YAML DSL reference syntax is available [here](SYNTAX-REFERENCE.md).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Usage
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Expand full help flags</summary>
|
||||
This will display help for the tool. Here are all the switches it supports.
|
||||
|
||||
```yaml
|
||||
|
||||
```console
|
||||
Nuclei is a fast, template based vulnerability scanner focusing
|
||||
on extensive configurability, massive extensibility and ease of use.
|
||||
|
||||
@@ -141,7 +119,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)
|
||||
|
||||
@@ -156,20 +134,16 @@ TEMPLATES:
|
||||
-as, -automatic-scan automatic web scan using wappalyzer technology detection to tags mapping
|
||||
-t, -templates string[] list of template or template directory to run (comma-separated, file)
|
||||
-turl, -template-url string[] template url or list containing template urls to run (comma-separated, file)
|
||||
-ai, -prompt string generate and run template using ai prompt
|
||||
-w, -workflows string[] list of workflow or workflow directory to run (comma-separated, file)
|
||||
-wurl, -workflow-url string[] workflow url or list containing workflow urls to run (comma-separated, file)
|
||||
-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
|
||||
-dut, -disable-unsigned-templates disable running unsigned templates or templates with mismatched signature
|
||||
-esc, -enable-self-contained enable loading self-contained templates
|
||||
-egm, -enable-global-matchers enable loading global matchers templates
|
||||
-file enable loading file templates
|
||||
|
||||
FILTERING:
|
||||
-a, -author string[] templates to run based on authors (comma-separated, file)
|
||||
@@ -205,7 +179,6 @@ OUTPUT:
|
||||
-se, -sarif-export string file to export results in SARIF format
|
||||
-je, -json-export string file to export results in JSON format
|
||||
-jle, -jsonl-export string file to export results in JSONL(ine) format
|
||||
-rd, -redact string[] redact given list of keys from query parameter, request header and body
|
||||
|
||||
CONFIGURATIONS:
|
||||
-config string path to the nuclei configuration file
|
||||
@@ -230,6 +203,7 @@ CONFIGURATIONS:
|
||||
-sml, -show-match-line show match lines for file templates, works with extractors only
|
||||
-ztls use ztls library with autofallback to standard one for tls13 [Deprecated] autofallback to ztls is enabled by default
|
||||
-sni string tls sni hostname to use (default: input domain name)
|
||||
-dt, -dialer-timeout value timeout for network requests.
|
||||
-dka, -dialer-keep-alive value keep-alive duration for network requests.
|
||||
-lfa, -allow-local-file-access allows file (payload) access anywhere on the system
|
||||
-lna, -restrict-local-network-access blocks connections to the local / private network
|
||||
@@ -238,6 +212,7 @@ CONFIGURATIONS:
|
||||
-sip, -source-ip string source ip address to use for network scan
|
||||
-rsr, -response-size-read int max response size to read in bytes
|
||||
-rss, -response-size-save int max response size to read in bytes (default 1048576)
|
||||
-rrt, -response-read-timeout value response read timeout in seconds (default 5s)
|
||||
-reset reset removes all nuclei configuration and data files (including nuclei-templates)
|
||||
-tlsi, -tls-impersonate enable experimental client hello (ja3) tls randomization
|
||||
-hae, -http-api-endpoint string experimental http api endpoint
|
||||
@@ -252,24 +227,18 @@ INTERACTSH:
|
||||
-ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
|
||||
|
||||
FUZZING:
|
||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||
-fuzz enable loading fuzzing templates (Deprecated: use -dast instead)
|
||||
-dast enable / run dast (fuzz) nuclei templates
|
||||
-dts, -dast-server enable dast server mode (live fuzzing)
|
||||
-dtr, -dast-report write dast scan report to file
|
||||
-dtst, -dast-server-token string dast server token (optional)
|
||||
-dtsa, -dast-server-address string dast server address (default "localhost:9055")
|
||||
-dfp, -display-fuzz-points display fuzz points in the output for debugging
|
||||
-fuzz-param-frequency int frequency of uninteresting parameters for fuzzing before skipping (default 10)
|
||||
-fa, -fuzz-aggression string fuzzing aggression level controls payload count for fuzz (low, medium, high) (default "low")
|
||||
-cs, -fuzz-scope string[] in scope url regex to be followed by fuzzer
|
||||
-cos, -fuzz-out-scope string[] out of scope url regex to be excluded by fuzzer
|
||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||
-fuzz enable loading fuzzing templates (Deprecated: use -dast instead)
|
||||
-dast enable / run dast (fuzz) nuclei templates
|
||||
-dfp, -display-fuzz-points display fuzz points in the output for debugging
|
||||
-fuzz-param-frequency int frequency of uninteresting parameters for fuzzing before skipping (default 10)
|
||||
-fa, -fuzz-aggression string fuzzing aggression level controls payload count for fuzz (low, medium, high) (default "low")
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover enable uncover engine
|
||||
-uq, -uncover-query string[] uncover search query
|
||||
-ue, -uncover-engine string[] uncover search engine (shodan,censys,fofa,shodan-idb,quake,hunter,zoomeye,netlas,criminalip,publicwww,hunterhow,google) (default shodan)
|
||||
-ue, -uncover-engine string[] uncover search engine (shodan,censys,fofa,shodan-idb,quake,hunter,zoomeye,netlas,criminalip,publicwww,hunterhow) (default shodan)
|
||||
-uf, -uncover-field string uncover fields to return (ip,port,host) (default "ip:port")
|
||||
-ul, -uncover-limit int uncover results to return (default 100)
|
||||
-ur, -uncover-ratelimit int override ratelimit of engines with unknown ratelimit (default 60 req/min) (default 60)
|
||||
@@ -285,7 +254,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,28 +277,26 @@ 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:
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string generate memory (heap) profile & trace files
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-vdl, -var-dump-limit int limit the number of characters displayed in var dump (default 255)
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string optional nuclei memory profile dump file
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
|
||||
UPDATE:
|
||||
-up, -update update nuclei engine to the latest released version
|
||||
@@ -338,327 +304,77 @@ 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
|
||||
-si, -stats-interval int number of seconds to wait between showing a statistics update (default 5)
|
||||
-mp, -metrics-port int port to expose nuclei metrics on (default 9092)
|
||||
-hps, -http-stats enable http status capturing (experimental)
|
||||
|
||||
CLOUD:
|
||||
-auth configure projectdiscovery cloud (pdcp) api key (default true)
|
||||
-tid, -team-id string upload scan results to given team id (optional) (default "none")
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard [DEPRECATED use -dashboard]
|
||||
-sid, -scan-id string upload scan results to existing scan id (optional)
|
||||
-sname, -scan-name string scan name to set (optional)
|
||||
-pd, -dashboard upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard
|
||||
-pdu, -dashboard-upload string upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard
|
||||
-auth configure projectdiscovery cloud (pdcp) api key
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard
|
||||
-sid, -scan-id string upload scan results to given scan id
|
||||
|
||||
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:
|
||||
Run nuclei on single host:
|
||||
$ nuclei -target example.com
|
||||
$ nuclei -target example.com
|
||||
|
||||
Run nuclei with specific template directories:
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
|
||||
Run nuclei against a list of hosts:
|
||||
$ nuclei -list hosts.txt
|
||||
$ nuclei -list hosts.txt
|
||||
|
||||
Run nuclei with a JSON output:
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
|
||||
Run nuclei with sorted Markdown outputs (with environment variables):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.projectdiscovery.io/getting-started/running
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
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)
|
||||
### Running Nuclei
|
||||
|
||||
</details>
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/running for details on running Nuclei
|
||||
|
||||
### Single target scan
|
||||
### Using Nuclei From Go Code
|
||||
|
||||
To perform a quick scan on web-application:
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com
|
||||
```
|
||||
|
||||
### Scanning multiple targets
|
||||
|
||||
Nuclei can handle bulk scanning by providing a list of targets. You can use a file containing multiple URLs.
|
||||
|
||||
```sh
|
||||
nuclei -list urls.txt
|
||||
```
|
||||
|
||||
### Network scan
|
||||
|
||||
This will scan the entire subnet for network-related issues, such as open ports or misconfigured services.
|
||||
|
||||
```sh
|
||||
nuclei -target 192.168.1.0/24
|
||||
```
|
||||
|
||||
### Scanning with your custom template
|
||||
|
||||
To write and use your own template, create a `.yaml` file with specific rules, then use it as follows.
|
||||
|
||||
```sh
|
||||
nuclei -u https://example.com -t /path/to/your-template.yaml
|
||||
```
|
||||
|
||||
### Connect Nuclei to ProjectDiscovery
|
||||
|
||||
You can run the scans on your machine and upload the results to the cloud platform for further analysis and remediation.
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com -dashboard
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This feature is absolutely free and does not require any subscription. For a detailed guide, refer to the [**`documentation`**](https://docs.projectdiscovery.io/cloud/scanning/nuclei-scan?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme).
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Nuclei Templates, Community and Rewards 💎
|
||||
[**Nuclei templates**](https://github.com/projectdiscovery/nuclei-templates) are based on the concepts of YAML based template files that define how the requests will be sent and processed. This allows easy extensibility capabilities to nuclei. The templates are written in YAML which specifies a simple human-readable format to quickly define the execution process.
|
||||
|
||||
**Try it online with our free AI powered Nuclei Templates Editor by** [**`clicking here`**](https://cloud.projectdiscovery.io/templates).
|
||||
|
||||
Nuclei Templates offer a streamlined way to identify and communicate vulnerabilities, combining essential details like severity ratings and detection methods. This open-source, community-developed tool accelerates threat response and is widely recognized in the cybersecurity world. Nuclei templates are actively contributed by thousands of security researchers globally. We run two programs for our contributors: [**`Pioneers`**](https://projectdiscovery.io/pioneers) and [**`💎 bounties`**](https://github.com/projectdiscovery/nuclei-templates/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20Bounty%22).
|
||||
Complete guide of using Nuclei as Library/SDK is available at [godoc](https://pkg.go.dev/github.com/projectdiscovery/nuclei/v3/lib#section-readme)
|
||||
|
||||
|
||||
<p align="left">
|
||||
<a href="/static/nuclei-templates-teamcity.png" target="_blank"><img src="/static/nuclei-templates-teamcity.png" width="1200px" alt="Nuclei template example for detecting TeamCity misconfiguration" /></a>
|
||||
### Resources
|
||||
|
||||
You can access the main documentation for Nuclei at https://docs.projectdiscovery.io/tools/nuclei/, and learn more about Nuclei in the cloud with [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io)
|
||||
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/resources for more resources and videos about Nuclei!
|
||||
|
||||
### Credits
|
||||
|
||||
Thanks to all the amazing [community contributors for sending PRs](https://github.com/projectdiscovery/nuclei/graphs/contributors) and keeping this project updated. :heart:
|
||||
|
||||
If you have an idea or some kind of improvement, you are welcome to contribute and participate in the Project, feel free to send your PR.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=projectdiscovery/nuclei&max=500">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
#### Examples
|
||||
|
||||
Visit [**our documentation**](https://docs.projectdiscovery.io/templates/introduction) for use cases and ideas.
|
||||
Do also check out the below similar open-source projects that may fit in your workflow:
|
||||
|
||||
| Use case | Nuclei template |
|
||||
| :----------------------------------- | :------------------------------------------------- |
|
||||
| Detect known CVEs | **[CVE-2021-44228 (Log4Shell)](https://cloud.projectdiscovery.io/public/CVE-2021-45046)** |
|
||||
| Identify Out-of-Band vulnerabilities | **[Blind SQL Injection via OOB](https://cloud.projectdiscovery.io/public/CVE-2024-22120)** |
|
||||
| SQL Injection detection | **[Generic SQL Injection](https://cloud.projectdiscovery.io/public/CVE-2022-34265)** |
|
||||
| Cross-Site Scripting (XSS) | **[Reflected XSS Detection](https://cloud.projectdiscovery.io/public/CVE-2023-4173)** |
|
||||
| Default or weak passwords | **[Default Credentials Check](https://cloud.projectdiscovery.io/public/airflow-default-login)** |
|
||||
| Secret files or data exposure | **[Sensitive File Disclosure](https://cloud.projectdiscovery.io/public/airflow-configuration-exposure)** |
|
||||
| Identify open redirects | **[Open Redirect Detection](https://cloud.projectdiscovery.io/public/open-redirect)** |
|
||||
| Detect subdomain takeovers | **[Subdomain Takeover Templates](https://cloud.projectdiscovery.io/public/azure-takeover-detection)** |
|
||||
| Security misconfigurations | **[Unprotected Jenkins Console](https://cloud.projectdiscovery.io/public/unauthenticated-jenkins)** |
|
||||
| Weak SSL/TLS configurations | **[SSL Certificate Expiry](https://cloud.projectdiscovery.io/public/expired-ssl)** |
|
||||
| Misconfigured cloud services | **[Open S3 Bucket Detection](https://cloud.projectdiscovery.io/public/s3-public-read-acp)** |
|
||||
| Remote code execution vulnerabilities| **[RCE Detection Templates](https://cloud.projectdiscovery.io/public/CVE-2024-29824)** |
|
||||
| Directory traversal attacks | **[Path Traversal Detection](https://cloud.projectdiscovery.io/public/oracle-fatwire-lfi)** |
|
||||
| File inclusion vulnerabilities | **[Local/Remote File Inclusion](https://cloud.projectdiscovery.io/public/CVE-2023-6977)** |
|
||||
[FFuF](https://github.com/ffuf/ffuf), [Qsfuzz](https://github.com/ameenmaali/qsfuzz), [Inception](https://github.com/proabiral/inception), [Snallygaster](https://github.com/hannob/snallygaster), [Gofingerprint](https://github.com/Static-Flow/gofingerprint), [Sn1per](https://github.com/1N3/Sn1per/tree/master/templates), [Google tsunami](https://github.com/google/tsunami-security-scanner), [Jaeles](https://github.com/jaeles-project/jaeles), [ChopChop](https://github.com/michelin/ChopChop)
|
||||
|
||||
### License
|
||||
|
||||
<br>
|
||||
<br>
|
||||
Nuclei is distributed under [MIT License](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)
|
||||
|
||||
## Our Mission
|
||||
|
||||
Traditional vulnerability scanners were built decades ago. They are closed-source, incredibly slow, and vendor-driven. Today's attackers are mass exploiting newly released CVEs across the internet within days, unlike the years it used to take. This shift requires a completely different approach to tackling trending exploits on the internet.
|
||||
|
||||
We built Nuclei to solve this challenge. We made the entire scanning engine framework open and customizable—allowing the global security community to collaborate and tackle the trending attack vectors and vulnerabilities on the internet. Nuclei is now used and contributed by Fortune 500 enterprises, government agencies, universities.
|
||||
|
||||
You can participate by contributing to our code, [**`templates library`**](https://github.com/projectdiscovery/nuclei-templates), or [**`joining our team`**](https://projectdiscovery.io/).
|
||||
|
||||
<br>
|
||||
<br>
|
||||
|
||||
## Contributors :heart:
|
||||
|
||||
Thanks to all the amazing [**`community contributors for sending PRs`**](https://github.com/projectdiscovery/nuclei/graphs/contributors) and keeping this project updated. :heart:
|
||||
|
||||
<p align="left">
|
||||
<a href="https://github.com/Ice3man543"><img src="https://avatars.githubusercontent.com/u/22318055?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehsandeep"><img src="https://avatars.githubusercontent.com/u/8293321?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Mzack9999"><img src="https://avatars.githubusercontent.com/u/13421144?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/forgedhallpass"><img src="https://avatars.githubusercontent.com/u/13679401?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tarunKoyalwar"><img src="https://avatars.githubusercontent.com/u/45962551?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/manuelbua"><img src="https://avatars.githubusercontent.com/u/819314?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dogancanbakir"><img src="https://avatars.githubusercontent.com/u/65292895?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parrasajad"><img src="https://avatars.githubusercontent.com/u/16835787?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vzamanillo"><img src="https://avatars.githubusercontent.com/u/10209695?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ShubhamRasal"><img src="https://avatars.githubusercontent.com/u/45902122?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/RamanaReddy0M"><img src="https://avatars.githubusercontent.com/u/90540245?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/LuitelSamikshya"><img src="https://avatars.githubusercontent.com/u/85764322?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kchason"><img src="https://avatars.githubusercontent.com/u/1111099?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/pmareke"><img src="https://avatars.githubusercontent.com/u/3502075?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dwisiswant0"><img src="https://avatars.githubusercontent.com/u/25837540?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/xm1k3"><img src="https://avatars.githubusercontent.com/u/73166077?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/5amu"><img src="https://avatars.githubusercontent.com/u/39925709?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehrishirajsharma"><img src="https://avatars.githubusercontent.com/u/35542790?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zerodivisi0n"><img src="https://avatars.githubusercontent.com/u/687694?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/geeknik"><img src="https://avatars.githubusercontent.com/u/466878?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/TerminalFi"><img src="https://avatars.githubusercontent.com/u/32599364?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KaulSe"><img src="https://avatars.githubusercontent.com/u/45340011?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/sullo"><img src="https://avatars.githubusercontent.com/u/1474884?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/wdahlenburg"><img src="https://avatars.githubusercontent.com/u/4451504?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ghost"><img src="https://avatars.githubusercontent.com/u/10137?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Nishan8583"><img src="https://avatars.githubusercontent.com/u/20457968?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jdk2588"><img src="https://avatars.githubusercontent.com/u/985054?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/nothinux"><img src="https://avatars.githubusercontent.com/u/17433202?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/CodFrm"><img src="https://avatars.githubusercontent.com/u/22783163?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/CasperGN"><img src="https://avatars.githubusercontent.com/u/5549643?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ankh2054"><img src="https://avatars.githubusercontent.com/u/6784287?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/revblock"><img src="https://avatars.githubusercontent.com/u/72813848?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/cn-kali-team"><img src="https://avatars.githubusercontent.com/u/30642514?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/EndPositive"><img src="https://avatars.githubusercontent.com/u/25148195?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jimen0"><img src="https://avatars.githubusercontent.com/u/6826244?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/xstevens"><img src="https://avatars.githubusercontent.com/u/69216?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mjkim610"><img src="https://avatars.githubusercontent.com/u/17107206?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/organiccrap"><img src="https://avatars.githubusercontent.com/u/376317?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/lu4nx"><img src="https://avatars.githubusercontent.com/u/3006875?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/souvikhazra1"><img src="https://avatars.githubusercontent.com/u/13842393?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tovask"><img src="https://avatars.githubusercontent.com/u/22732484?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Marmelatze"><img src="https://avatars.githubusercontent.com/u/199681?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/doug-threatmate"><img src="https://avatars.githubusercontent.com/u/127235272?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/yabeow"><img src="https://avatars.githubusercontent.com/u/21117771?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/olearycrew"><img src="https://avatars.githubusercontent.com/u/6044920?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/gano3s"><img src="https://avatars.githubusercontent.com/u/2551605?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/alizmhdi"><img src="https://avatars.githubusercontent.com/u/79321261?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/hackerpain"><img src="https://avatars.githubusercontent.com/u/61242234?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/lc"><img src="https://avatars.githubusercontent.com/u/19563282?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/savushkin-yauheni"><img src="https://avatars.githubusercontent.com/u/5173352?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/74616e696d"><img src="https://avatars.githubusercontent.com/u/97121933?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/edoardottt"><img src="https://avatars.githubusercontent.com/u/35783570?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zt2"><img src="https://avatars.githubusercontent.com/u/7644862?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/moonD4rk"><img src="https://avatars.githubusercontent.com/u/24284231?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/wk8"><img src="https://avatars.githubusercontent.com/u/2536231?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mikerott"><img src="https://avatars.githubusercontent.com/u/857712?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/toufik-airane"><img src="https://avatars.githubusercontent.com/u/5610269?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/hktalent"><img src="https://avatars.githubusercontent.com/u/18223385?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jturner"><img src="https://avatars.githubusercontent.com/u/1825202?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/gaby"><img src="https://avatars.githubusercontent.com/u/835733?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vavkamil"><img src="https://avatars.githubusercontent.com/u/47953210?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/leonjza"><img src="https://avatars.githubusercontent.com/u/1148127?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mionskowski-form3"><img src="https://avatars.githubusercontent.com/u/91873652?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/chenrui333"><img src="https://avatars.githubusercontent.com/u/1580956?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/iamargus95"><img src="https://avatars.githubusercontent.com/u/77744293?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/shashikarsiddharth"><img src="https://avatars.githubusercontent.com/u/60960197?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/trypa11"><img src="https://avatars.githubusercontent.com/u/67585616?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Zeokat"><img src="https://avatars.githubusercontent.com/u/1313154?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/alban-stourbe-wmx"><img src="https://avatars.githubusercontent.com/u/159776828?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/anykno"><img src="https://avatars.githubusercontent.com/u/2528207?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ronaudinho"><img src="https://avatars.githubusercontent.com/u/10264710?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/boy-hack"><img src="https://avatars.githubusercontent.com/u/18695984?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/iuliu8899"><img src="https://avatars.githubusercontent.com/u/31680027?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/debasishbsws"><img src="https://avatars.githubusercontent.com/u/65381620?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/denysvitali-niantic"><img src="https://avatars.githubusercontent.com/u/157139422?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/fail-open"><img src="https://avatars.githubusercontent.com/u/72417455?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Xc1Ym"><img src="https://avatars.githubusercontent.com/u/29765332?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/XTeam-Wing"><img src="https://avatars.githubusercontent.com/u/25416365?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Weltolk"><img src="https://avatars.githubusercontent.com/u/40228052?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tonghuaroot"><img src="https://avatars.githubusercontent.com/u/23011166?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/praetorian-thendrickson"><img src="https://avatars.githubusercontent.com/u/69640071?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/S0obi"><img src="https://avatars.githubusercontent.com/u/4180104?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/skahn007gl"><img src="https://avatars.githubusercontent.com/u/144735608?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/shouichi"><img src="https://avatars.githubusercontent.com/u/99586?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/seb-elttam"><img src="https://avatars.githubusercontent.com/u/111818823?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/AdallomRoy"><img src="https://avatars.githubusercontent.com/u/4046118?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/rotemreiss"><img src="https://avatars.githubusercontent.com/u/9288082?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/oscarintherocks"><img src="https://avatars.githubusercontent.com/u/1785821?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/xxcdd"><img src="https://avatars.githubusercontent.com/u/42600601?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/chen2aaron"><img src="https://avatars.githubusercontent.com/u/9978183?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/voidz0r"><img src="https://avatars.githubusercontent.com/u/1032286?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vince-isec"><img src="https://avatars.githubusercontent.com/u/149686094?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/true13"><img src="https://avatars.githubusercontent.com/u/18207552?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/skhalsa-sigsci"><img src="https://avatars.githubusercontent.com/u/68570441?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ShuBo6"><img src="https://avatars.githubusercontent.com/u/41125338?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/seeyarh"><img src="https://avatars.githubusercontent.com/u/16869800?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/securibee"><img src="https://avatars.githubusercontent.com/u/51520913?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/sduc"><img src="https://avatars.githubusercontent.com/u/2879617?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/scottdharvey"><img src="https://avatars.githubusercontent.com/u/25498254?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/rykkard"><img src="https://avatars.githubusercontent.com/u/51889048?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/monitor403"><img src="https://avatars.githubusercontent.com/u/45124775?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mlec1"><img src="https://avatars.githubusercontent.com/u/42201667?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/meme-lord"><img src="https://avatars.githubusercontent.com/u/17912559?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/LazyMaple"><img src="https://avatars.githubusercontent.com/u/12314941?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/lvyaoting"><img src="https://avatars.githubusercontent.com/u/166296299?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/llussy"><img src="https://avatars.githubusercontent.com/u/18432966?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/linchizhen"><img src="https://avatars.githubusercontent.com/u/170242051?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kiokuless"><img src="https://avatars.githubusercontent.com/u/110003596?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Jarnpher553"><img src="https://avatars.githubusercontent.com/u/10233873?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/c-f"><img src="https://avatars.githubusercontent.com/u/35263248?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/hanghuge"><img src="https://avatars.githubusercontent.com/u/166206050?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/testwill"><img src="https://avatars.githubusercontent.com/u/8717479?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/galoget"><img src="https://avatars.githubusercontent.com/u/8353133?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/fudancoder"><img src="https://avatars.githubusercontent.com/u/171416994?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/revolunet"><img src="https://avatars.githubusercontent.com/u/124937?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jsoref"><img src="https://avatars.githubusercontent.com/u/2119212?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MachadoOtto"><img src="https://avatars.githubusercontent.com/u/93268441?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jonathanwalker"><img src="https://avatars.githubusercontent.com/u/14978093?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/nHurD"><img src="https://avatars.githubusercontent.com/u/233374?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jessekelly881"><img src="https://avatars.githubusercontent.com/u/22938931?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/JaneX8"><img src="https://avatars.githubusercontent.com/u/5116641?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/atomiczsec"><img src="https://avatars.githubusercontent.com/u/75549184?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/M-Faheem-Khan"><img src="https://avatars.githubusercontent.com/u/17150767?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/denandz"><img src="https://avatars.githubusercontent.com/u/5291556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tibbon"><img src="https://avatars.githubusercontent.com/u/82880?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dany74q"><img src="https://avatars.githubusercontent.com/u/2129762?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0x123456789"><img src="https://avatars.githubusercontent.com/u/36066426?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/danigoland"><img src="https://avatars.githubusercontent.com/u/15079567?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ChrisMandich"><img src="https://avatars.githubusercontent.com/u/14286797?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/austintraver"><img src="https://avatars.githubusercontent.com/u/25112463?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/socialsister"><img src="https://avatars.githubusercontent.com/u/155628741?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Anemys"><img src="https://avatars.githubusercontent.com/u/51196227?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/andreangelucci"><img src="https://avatars.githubusercontent.com/u/18552197?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/AlexS778"><img src="https://avatars.githubusercontent.com/u/98418121?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/noraj"><img src="https://avatars.githubusercontent.com/u/16578570?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/akkuman"><img src="https://avatars.githubusercontent.com/u/7813511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zrquan"><img src="https://avatars.githubusercontent.com/u/33086594?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/1efty"><img src="https://avatars.githubusercontent.com/u/18194777?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/rsrdesarrollo"><img src="https://avatars.githubusercontent.com/u/5142014?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/razin99"><img src="https://avatars.githubusercontent.com/u/44442082?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MetzinAround"><img src="https://avatars.githubusercontent.com/u/65838556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vil02"><img src="https://avatars.githubusercontent.com/u/65706193?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mrschyte"><img src="https://avatars.githubusercontent.com/u/8571?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/PeterDaveHello"><img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parthmalhotra"><img src="https://avatars.githubusercontent.com/u/28601533?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/owenrumney"><img src="https://avatars.githubusercontent.com/u/3049157?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Ovi3"><img src="https://avatars.githubusercontent.com/u/29408109?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Bisstocuz"><img src="https://avatars.githubusercontent.com/u/42398278?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/daffainfo"><img src="https://avatars.githubusercontent.com/u/36522826?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mhmdiaa"><img src="https://avatars.githubusercontent.com/u/19687798?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MiryangJung"><img src="https://avatars.githubusercontent.com/u/48237511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0xmin"><img src="https://avatars.githubusercontent.com/u/44919834?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/maikthulhu"><img src="https://avatars.githubusercontent.com/u/680830?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/sttlr"><img src="https://avatars.githubusercontent.com/u/40246850?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/iamRjarpan"><img src="https://avatars.githubusercontent.com/u/45498226?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/leoloobeek"><img src="https://avatars.githubusercontent.com/u/8801754?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KristinnVikar"><img src="https://avatars.githubusercontent.com/u/93918469?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kant01ne"><img src="https://avatars.githubusercontent.com/u/5072452?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KeisukeYamashita"><img src="https://avatars.githubusercontent.com/u/23056537?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/1hehaq"><img src="https://avatars.githubusercontent.com/u/162917546?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
</p>
|
||||
|
||||
<br>
|
||||
<br>
|
||||
<br>
|
||||
|
||||
<div align="center">
|
||||
|
||||
<sub>**`nuclei`** is distributed under [**MIT License**](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)</sub>
|
||||
|
||||
</div>
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://docs.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Check Nuclei Documentation"></a>
|
||||
</h1>
|
||||
+5
-13
@@ -33,8 +33,7 @@
|
||||
<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_PT-BR.md">Portuguese</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -59,7 +58,7 @@ Nuclei使用零误报的定制模板向目标发送请求,同时可以对主
|
||||
|
||||
# 安装Nuclei
|
||||
|
||||
Nuclei需要 **go1.24.2** 才能安装成功。执行下列命令安装最新版本的Nuclei
|
||||
Nuclei需要 **go1.21** 才能安装成功。执行下列命令安装最新版本的Nuclei
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -119,7 +118,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 +237,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 +261,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 +288,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 +317,7 @@ HONEYPOT:
|
||||
|
||||
```
|
||||
|
||||
更多信息请参考文档: https://docs.projectdiscovery.io/getting-started/running
|
||||
更多信息请参考文档: https://docs.nuclei.sh/getting-started/running
|
||||
|
||||
|
||||
### 运行Nuclei
|
||||
@@ -377,7 +369,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>
|
||||
|
||||
+4
-12
@@ -31,8 +31,7 @@
|
||||
<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_PT-BR.md">Portuguese</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -56,7 +55,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.21** 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 +117,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 +243,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 +266,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 +293,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 +325,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
|
||||
|
||||
+5
-13
@@ -33,8 +33,7 @@
|
||||
<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_PT-BR.md">Portuguese</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -54,7 +53,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.21** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -98,7 +97,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 +210,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 +232,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 +259,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 +288,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 +340,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>
|
||||
|
||||
+3
-4
@@ -30,8 +30,7 @@
|
||||
<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">韓国語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">インドネシア語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">スペイン語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_PT-BR.md">ポルトガル語</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">スペイン語</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -53,7 +52,7 @@ Nucleiは、テンプレートに基づいてターゲット間でリクエス
|
||||
|
||||
# Nucleiのインストール
|
||||
|
||||
Nucleiを正常にインストールするには、**go1.24.2**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
Nucleiを正常にインストールするには、**go1.21**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -113,7 +112,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)
|
||||
|
||||
|
||||
+5
-13
@@ -31,8 +31,7 @@
|
||||
<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">한국어</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">스페인어</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_PT-BR.md">포르투갈어</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">스페인어</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -52,7 +51,7 @@ Nuclei는 템플릿을 기반으로 대상 간에 요청을 보내기 위해 사
|
||||
|
||||
# 설치
|
||||
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.24.2**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.21**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -96,7 +95,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 +208,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 +231,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 +258,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 +286,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 +340,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>
|
||||
|
||||
-377
@@ -1,377 +0,0 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://nuclei.projectdiscovery.io"><img src="static/nuclei-logo.png" width="200px" alt="Nuclei"></a>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Scanner de vulnerabilidades rápido e personalizável baseado em uma DSL simples baseada em YAML.</h4>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#como-funciona">Como funciona</a> •
|
||||
<a href="#instalacao-do-nuclei">Instalação</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">Documentação</a> •
|
||||
<a href="#créditos">Créditos</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">Perguntas Frequentes</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Junte-se ao Discord</a>
|
||||
</p>
|
||||
|
||||
<p 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_PT-BR.md">Portuguese</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
O Nuclei é utilizado para enviar solicitações para vários alvos baseados em um modelo, resultando em zero falsos positivos e proporcionando uma varredura rápida em um grande número de hosts. O Nuclei oferece suporte a uma variedade de protocolos, incluindo TCP, DNS, HTTP, SSL, Arquivo, Whois, Websocket, Headless, Código, entre outros. Com modelos poderosos e flexíveis, o Nuclei pode ser usado para modelar todos os tipos de verificações de segurança.
|
||||
|
||||
Temos um [repositório dedicado](https://github.com/projectdiscovery/nuclei-templates) que abriga vários tipos de modelos de vulnerabilidades, contribuídos por **mais de 300** pesquisadores e engenheiros de segurança.
|
||||
|
||||
## Como funciona
|
||||
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
|
||||
|
||||
| :exclamation: **Aviso** |
|
||||
|---------------------------------|
|
||||
| **Este projeto está em desenvolvimento ativo**. Alterações significativas são esperadas em versões futuras. Consulte o changelog antes de atualizar. |
|
||||
| Este projeto foi desenvolvido principalmente para ser usado como uma ferramenta CLI independente. **Executar o Nuclei como um serviço pode implicar riscos de segurança.** É recomendável utilizá-lo com precaução e medidas de segurança adicionais. |
|
||||
|
||||
# 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:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Mais métodos de instalação [podem ser encontrados aqui](https://docs.projectdiscovery.io/tools/nuclei/install).**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
### Modelos do Nuclei
|
||||
|
||||
O Nuclei possui suporte integrado para download/atualização automática de modelos a partir da versão [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2). O projeto [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) fornece uma lista de modelos prontos para uso, atualizados constantemente pela comunidade.
|
||||
|
||||
Você também pode usar a flag `update-templates` para atualizar os modelos do Nuclei a qualquer momento; também pode criar seus próprios testes para seu fluxo de trabalho e necessidades específicas seguindo o [guia de modelos](https://docs.projectdiscovery.io/templates/) do Nuclei.
|
||||
|
||||
A referência de sintaxe YAML DSL está disponível [aqui](SYNTAX-REFERENCE.md).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Uso
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
Isso mostrará ajuda sobre a ferramenta. Aqui estão todas as opções que ela suporta.
|
||||
|
||||
|
||||
```console
|
||||
Nuclei é um scanner de vulnerabilidades rápido e baseado em templates
|
||||
que se concentra em sua ampla configurabilidade, extensibilidade e facilidade de uso.
|
||||
|
||||
Usage:
|
||||
./nuclei [flags]
|
||||
|
||||
Flags:
|
||||
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)
|
||||
-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)
|
||||
|
||||
TARGET-FORMAT:
|
||||
-im, -input-mode string modo do arquivo de entrada (list, burp, jsonl, yaml, openapi, swagger) (padrão "list")
|
||||
-ro, -required-only usar apenas campos obrigatórios no formato de entrada ao gerar requisições
|
||||
-sfv, -skip-format-validation pular a validação de formato (como variáveis ausentes) ao processar o arquivo de entrada
|
||||
|
||||
TEMPLATES:
|
||||
-nt, -new-templates executar apenas os novos templates adicionados na última versão de nuclei-templates
|
||||
-ntv, -new-templates-version string[] executar os novos templates adicionados na versão especificada
|
||||
-as, -automatic-scan escaneamento da web automático utilizando a detecção de tecnologia do Wappalyzer para mapeamento de tags
|
||||
-t, -templates string[] lista de templates ou diretório de templates a executar (separados por vírgulas, arquivo)
|
||||
-turl, -template-url string[] URL de template ou lista contendo URLs de templates a executar (separados por vírgulas, arquivo)
|
||||
-w, -workflows string[] lista de fluxos de trabalho ou diretório de fluxos de trabalho a executar (separados por vírgulas, arquivo)
|
||||
-wurl, -workflow-url string[] URL de fluxo de trabalho ou lista contendo URLs de fluxos de trabalho para executar (separados por vírgulas, arquivo)
|
||||
-validate valida os templates passados para o nuclei
|
||||
-nss, -no-strict-syntax desativa a verificação de sintaxe estrita nos templates
|
||||
-td, -template-display exibe o conteúdo dos templates
|
||||
-tl lista todos os templates disponíveis
|
||||
-tgl lista todas as tags disponíveis
|
||||
-sign assina os templates com a chave privada definida na variável de ambiente NUCLEI_SIGNATURE_PRIVATE_KEY
|
||||
-code habilita o carregamento de templates baseados em protocolos de código
|
||||
-dut, -disable-unsigned-templates desativa a execução de templates não assinados ou com assinatura incompatível
|
||||
|
||||
FILTERING:
|
||||
-a, -author string[] templates a serem executados com base nos autores (separados por vírgulas, arquivo)
|
||||
-tags string[] templates a serem executados com base em tags (separados por vírgulas, arquivo)
|
||||
-etags, -exclude-tags string[] templates a excluir com base em tags (separados por vírgulas, arquivo)
|
||||
-itags, -include-tags string[] tags a executar mesmo que estejam excluídas por padrão ou configuração
|
||||
-id, -template-id string[] templates a serem executados com base em IDs de template (separados por vírgulas, arquivo, permitem curingas)
|
||||
-eid, -exclude-id string[] templates a excluir com base em IDs de template (separados por vírgulas, arquivo)
|
||||
-it, -include-templates string[] caminho do arquivo de template ou diretório a executar mesmo que estejam excluídos por padrão ou configuração
|
||||
-et, -exclude-templates string[] caminho do arquivo de template ou diretório a excluir (separados por vírgulas, arquivo)
|
||||
-em, -exclude-matchers string[] matchers de template a excluir no resultado
|
||||
-s, -severity value[] templates a executar com base na criticidade. Valores possíveis: info, baixo, médio, alto, crítico, desconhecido
|
||||
-es, -exclude-severity value[] templates a excluir com base na criticidade. Valores possíveis: info, baixo, médio, alto, crítico, desconhecido
|
||||
-pt, -type value[] templates a executar com base no tipo de protocolo. Valores possíveis: dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-ept, -exclude-type value[] templates a excluir com base no tipo de protocolo. Valores possíveis: dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-tc, -template-condition string[] templates a executar com base em condição de expressão
|
||||
|
||||
OUTPUT:
|
||||
-o, -output string arquivo de saída para salvar as ocorrências/vulnerabilidades detectadas
|
||||
-sresp, -store-resp armazenar todas as solicitações/respostas enviadas pelo nuclei no diretório de saída
|
||||
-srd, -store-resp-dir string armazenar todas as solicitações/respostas enviadas pelo nuclei em um diretório personalizado (padrão "output")
|
||||
-silent exibir apenas os resultados
|
||||
-nc, -no-color desativar a coloração do conteúdo de saída (códigos de escape ANSI)
|
||||
-j, -jsonl salvar a saída no formato JSONL(ines)
|
||||
-irr, -include-rr -omit-raw incluir pares solicitação/resposta nas saídas JSON, JSONL e Markdown (apenas para achados) [OBSOLETO usar -omit-raw] (padrão true)
|
||||
-or, -omit-raw omitir os pares solicitação/resposta nas saídas JSON, JSONL e Markdown (apenas para achados)
|
||||
-ot, -omit-template omitir o template codificado na saída JSON, JSONL
|
||||
-nm, -no-meta desativar a exibição de metadados dos resultados na saída CLI
|
||||
-ts, -timestamp ativar a exibição do carimbo de data/hora na saída CLI
|
||||
-rdb, -report-db string banco de dados de relatórios do nuclei (usar sempre para persistir os dados dos relatórios)
|
||||
-ms, -matcher-status exibir o estado de falha de correspondência
|
||||
-me, -markdown-export string diretório para exportar resultados no formato Markdown
|
||||
-se, -sarif-export string arquivo para exportar resultados no formato SARIF
|
||||
-je, -json-export string arquivo para exportar resultados no formato JSON
|
||||
-jle, -jsonl-export string arquivo para exportar resultados no formato JSONL(ines)
|
||||
|
||||
CONFIGURATIONS:
|
||||
-config string caminho do arquivo de configuração do nuclei
|
||||
-fr, -follow-redirects ativar o acompanhamento de redirecionamentos para templates HTTP
|
||||
-fhr, -follow-host-redirects seguir redirecionamentos no mesmo host
|
||||
-mr, -max-redirects int número máximo de redirecionamentos a seguir para templates HTTP (padrão 10)
|
||||
-dr, -disable-redirects desativar redirecionamentos para templates HTTP
|
||||
-rc, -report-config string arquivo de configuração do módulo de relatórios do nuclei
|
||||
-H, -header string[] cabeçalho/cookie personalizado a incluir em todas as solicitações HTTP no formato header:value (CLI, arquivo)
|
||||
-V, -var value variáveis personalizadas no formato key=value
|
||||
-r, -resolvers string arquivo contendo uma lista de resolvers para o nuclei
|
||||
-sr, -system-resolvers usar resolução DNS do sistema como fallback em caso de erro
|
||||
-dc, -disable-clustering desativar o agrupamento de solicitações
|
||||
-passive ativar o modo de processamento passivo de respostas HTTP
|
||||
-fh2, -force-http2 forçar conexões HTTP2 nas solicitações
|
||||
-ev, -env-vars ativar o uso de variáveis de ambiente no template
|
||||
-cc, -client-cert string arquivo de certificado de cliente (codificado em PEM) usado para autenticar-se contra os hosts escaneados
|
||||
-ck, -client-key string arquivo de chave de cliente (codificado em PEM) usado para autenticar-se contra os hosts escaneados
|
||||
-ca, -client-ca string arquivo de autoridade de certificação de cliente (codificado em PEM) usado para autenticar-se contra os hosts escaneados
|
||||
-sml, -show-match-line exibir linhas de correspondência para templates de arquivo, funciona apenas com extratores
|
||||
-ztls usar a biblioteca ztls com fallback automático para padrão no tls13 [OBSOLETO] fallback automático para ztls já está ativado por padrão
|
||||
-sni string nome de host tls sni a ser usado (padrão: nome de domínio de entrada)
|
||||
-dt, -dialer-timeout value tempo limite para solicitações de rede
|
||||
-dka, -dialer-keep-alive value duração do keep-alive para solicitações de rede
|
||||
-lfa, -allow-local-file-access permitir acesso a arquivos (payload) em qualquer lugar do sistema
|
||||
-lna, -restrict-local-network-access bloquear conexões à rede local/privada
|
||||
-i, -interface string interface de rede a ser usada para o escaneamento de rede
|
||||
-at, -attack-type string tipo de combinações de payload a realizar (batteringram, pitchfork, clusterbomb)
|
||||
-sip, -source-ip string endereço IP de origem a ser usado para o escaneamento de rede
|
||||
-rsr, -response-size-read int tamanho máximo de resposta a ser lido em bytes (padrão 10485760)
|
||||
-rss, -response-size-save int tamanho máximo de resposta a ser salvo em bytes (padrão 1048576)
|
||||
-reset remove todos os arquivos de configuração e dados do nuclei (incluindo os nuclei-templates)
|
||||
-tlsi, -tls-impersonate ativar randomização experimental do client hello (ja3) tls
|
||||
|
||||
INTERACTSH:
|
||||
-iserver, -interactsh-server string URL do servidor interactsh para instância auto-hospedada (padrão: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
-itoken, -interactsh-token string token de autenticação para o servidor interactsh auto-hospedado
|
||||
-interactions-cache-size int número de solicitações a serem mantidas no cache de interações (padrão 5000)
|
||||
-interactions-eviction int número de segundos a esperar antes de remover solicitações do cache (padrão 60)
|
||||
-interactions-poll-duration int número de segundos a esperar antes de cada solicitação de polling de interações (padrão 5)
|
||||
-interactions-cooldown-period int tempo adicional para o polling de interações antes de encerrar (padrão 5)
|
||||
-ni, -no-interactsh desativar o servidor interactsh para testes OAST, excluir templates baseados em OAST
|
||||
|
||||
FUZZING:
|
||||
-ft, -fuzzing-type string sobrescreve o tipo de fuzzing definido no template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string sobrescreve o modo de fuzzing definido no template (multiple, single)
|
||||
-fuzz habilita o carregamento de templates de fuzzing (Obsoleto: usar -dast em vez disso)
|
||||
-dast executa apenas templates DAST
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover ativa o motor uncover
|
||||
-uq, -uncover-query string[] consulta de busca uncover
|
||||
-ue, -uncover-engine string[] motor de busca uncover (shodan,censys,fofa,shodan-idb,quake,hunter,zoomeye,netlas,criminalip,publicwww,hunterhow) (padrão shodan)
|
||||
-uf, -uncover-field string campos uncover a serem retornados (ip,port,host) (padrão "ip:port")
|
||||
-ul, -uncover-limit int resultados uncover a serem retornados (padrão 100)
|
||||
-ur, -uncover-ratelimit int sobrescreve o limite de taxa dos motores com o limite de taxa do motor uncover (padrão 60 req/min)
|
||||
|
||||
RATE-LIMIT:
|
||||
-rl, -rate-limit int número máximo de solicitações a serem enviadas por segundo (padrão 150)
|
||||
-rlm, -rate-limit-minute int número máximo de solicitações a serem enviadas por minuto
|
||||
-bs, -bulk-size int número máximo de hosts a serem analisados em paralelo por template (padrão 25)
|
||||
-c, -concurrency int número máximo de templates a serem executados em paralelo (padrão 25)
|
||||
-hbs, -headless-bulk-size int número máximo de hosts headless a serem analisados em paralelo por template (padrão 10)
|
||||
-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)
|
||||
-retries int número de tentativas para solicitações com falha (padrão 1)
|
||||
-ldp, -leave-default-ports manter as portas HTTP/HTTPS padrão (exemplo: host:80, host:443)
|
||||
-mhe, -max-host-error int número máximo de erros para um host antes de ignorá-lo no scan (padrão 30)
|
||||
-te, -track-error string[] adiciona o erro especificado à lista de rastreamento de erros máximos por host (standard, file)
|
||||
-nmhe, -no-mhe desativa a exclusão de hosts do scan com base em erros
|
||||
-project utiliza uma pasta de projeto para evitar enviar a mesma solicitação várias vezes
|
||||
-project-path string define um caminho específico para o projeto (padrão "/tmp")
|
||||
-spm, -stop-at-first-match interrompe o processamento de solicitações HTTP após a primeira correspondência (pode quebrar a lógica de templates/fluxos de trabalho)
|
||||
-stream modo de transmissão - começa a trabalhar sem ordenar a entrada
|
||||
-ss, -scan-strategy value estratégia a ser usada durante o scan (auto/host-spray/template-spray) (padrão auto)
|
||||
-irt, -input-read-timeout value tempo limite para leitura da entrada (padrão 3m0s)
|
||||
-nh, -no-httpx desativa a análise httpx para entradas que não sejam URLs
|
||||
-no-stdin desativa o processamento de entrada padrão
|
||||
|
||||
HEADLESS:
|
||||
-headless habilita templates que requerem suporte para navegadores sem interface gráfica (headless browser) (o usuário root no Linux desativará o sandbox)
|
||||
-page-timeout int segundos para esperar cada página no modo headless (padrão 20)
|
||||
-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:
|
||||
-debug exibe todas as solicitações e respostas
|
||||
-dreq, -debug-req exibe todas as solicitações enviadas
|
||||
-dresp, -debug-resp exibe todas as respostas recebidas
|
||||
-p, -proxy string[] lista de proxies HTTP/SOCKS5 a serem usados (separados por vírgulas ou arquivo de entrada)
|
||||
-pi, -proxy-internal proxy para todas as solicitações internas
|
||||
-ldf, -list-dsl-function lista todas as assinaturas de funções DSL suportadas
|
||||
-tlog, -trace-log string arquivo para gravar o log de rastreamento de solicitações enviadas
|
||||
-elog, -error-log string arquivo para gravar o log de erros de solicitações enviadas
|
||||
-version exibe a versão do nuclei
|
||||
-hm, -hang-monitor ativa o monitoramento de travamentos do nuclei
|
||||
-v, -verbose exibe saída detalhada
|
||||
-profile-mem string arquivo opcional para despejo de memória do nuclei
|
||||
-vv exibe os templates carregados para o scan
|
||||
-svd, -show-var-dump exibe o dump de variáveis para depuração
|
||||
-ep, -enable-pprof ativa o servidor de depuração pprof
|
||||
-tv, -templates-version exibe a versão dos templates do nuclei (nuclei-templates) instalados
|
||||
-hc, -health-check executa verificações de diagnóstico
|
||||
|
||||
UPDATE:
|
||||
-up, -update atualiza o mecanismo do nuclei para a última versão lançada
|
||||
-ut, -update-templates atualiza os nuclei-templates para a última versão lançada
|
||||
-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)
|
||||
-si, -stats-interval int número de segundos a esperar entre as atualizações de estatísticas (padrão 5)
|
||||
-mp, -metrics-port int porta para expor métricas do nuclei (padrão 9092)
|
||||
|
||||
CLOUD:
|
||||
-auth configura a chave de API do cloud do ProjectDiscovery (pdcp)
|
||||
-cup, -cloud-upload faz upload dos resultados do scan para o dashboard do pdcp
|
||||
-sid, -scan-id string faz upload dos resultados do scan para o ID de scan fornecido
|
||||
|
||||
AUTHENTICATION:
|
||||
-sf, -secret-file string[] caminho para o arquivo de configuração contendo os secrets para o scan autenticado do nuclei
|
||||
-ps, -prefetch-secrets pré-carrega os secrets do arquivo de secrets
|
||||
|
||||
|
||||
EXAMPLES:
|
||||
Executar nuclei em um único host:
|
||||
$ nuclei -target example.com
|
||||
|
||||
Executar nuclei com diretórios específicos de templates:
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
|
||||
Executar nuclei contra uma lista de hosts:
|
||||
$ nuclei -list hosts.txt
|
||||
|
||||
Executar nuclei com saída JSON:
|
||||
$ nuclei -target example.com -json-export output.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
|
||||
```
|
||||
|
||||
### Executando Nuclei
|
||||
|
||||
Consulte https://docs.projectdiscovery.io/tools/nuclei/running para obter detalhes sobre como executar o Nuclei.
|
||||
|
||||
### Uso de Nuclei com código Go
|
||||
|
||||
O guia completo sobre como usar o Nuclei como biblioteca/SDK está disponível em [godoc](https://pkg.go.dev/github.com/projectdiscovery/nuclei/v3/lib#section-readme).
|
||||
|
||||
|
||||
### Recursos
|
||||
|
||||
Você pode acessar a documentação principal do Nuclei em https://docs.projectdiscovery.io/tools/nuclei/ e obter mais informações sobre o Nuclei na nuvem com a [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io).
|
||||
|
||||
Consulte https://docs.projectdiscovery.io/tools/nuclei/resources para acessaar mais recursos e vídeos sobre o Nuclei!
|
||||
|
||||
### Créditos
|
||||
|
||||
Obrigado a todos os incríveis [contribuidores da comunidade que enviaram em PRs](https://github.com/projectdiscovery/nuclei/graphs/contributors) e mantêm este projeto atualizado. :heart:
|
||||
|
||||
Se você tem uma ideia ou algum tipo de melhoria, sinta-se à vontade para contribuir e participar do projeto. Envie seu PR!
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=projectdiscovery/nuclei&max=500">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
|
||||
Confira também os seguintes projetos de código aberto que podem se adequar ao seu fluxo de trabalho:
|
||||
|
||||
[FFuF](https://github.com/ffuf/ffuf), [Qsfuzz](https://github.com/ameenmaali/qsfuzz), [Inception](https://github.com/proabiral/inception), [Snallygaster](https://github.com/hannob/snallygaster), [Gofingerprint](https://github.com/Static-Flow/gofingerprint), [Sn1per](https://github.com/1N3/Sn1per/tree/master/templates), [Google tsunami](https://github.com/google/tsunami-security-scanner), [Jaeles](https://github.com/jaeles-project/jaeles), [ChopChop](https://github.com/michelin/ChopChop)
|
||||
|
||||
### Licença
|
||||
|
||||
O Nuclei é distribuído sob a [Licença MIT](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://docs.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Check Nuclei Documentation"></a>
|
||||
</h1>
|
||||
-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
+18
-146
@@ -1373,19 +1373,6 @@ Fuzzing describes schema to fuzz http requests
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>analyzer</code> <i><a href="#analyzersanalyzertemplate">analyzers.AnalyzerTemplate</a></i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Analyzer is an analyzer to use for matching the response.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>self-contained</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
@@ -1417,19 +1404,6 @@ Valid values:
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>skip-secret-file</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
SkipSecretFile skips the authentication or authorization configured in the secret file.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>cookie-reuse</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
@@ -1664,19 +1638,6 @@ FuzzPreConditionOperator is the operator between multiple PreConditions for fuzz
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>global-matchers</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
GlobalMatchers marks matchers as static and applies globally to all result events from other templates
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2038,61 +1999,6 @@ Appears in:
|
||||
|
||||
|
||||
|
||||
## analyzers.AnalyzerTemplate
|
||||
AnalyzerTemplate is the template for the analyzer
|
||||
|
||||
Appears in:
|
||||
|
||||
|
||||
- <code><a href="#httprequest">http.Request</a>.analyzer</code>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>name</code> <i>string</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Name is the name of the analyzer to use
|
||||
|
||||
|
||||
Valid values:
|
||||
|
||||
|
||||
- <code>time_delay</code>
|
||||
|
||||
- <code>xss_context</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>parameters</code> <i>map[string]interface{}</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Parameters is the parameters for the analyzer
|
||||
|
||||
Parameters are different for each analyzer. For example, you can customize
|
||||
time_delay analyzer with sleep_duration, time_slope_error_range, etc. Refer
|
||||
to the docs for each analyzer to get an idea about parameters.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
## SignatureTypeHolder
|
||||
SignatureTypeHolder is used to hold internal type of the signature
|
||||
|
||||
@@ -3220,8 +3126,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>
|
||||
|
||||
@@ -3291,19 +3197,6 @@ read-all: false
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>stop-at-first-match</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3758,18 +3651,8 @@ Enum Values:
|
||||
|
||||
- <code>files</code>
|
||||
|
||||
- <code>waitdom</code>
|
||||
|
||||
- <code>waitfcp</code>
|
||||
|
||||
- <code>waitfmp</code>
|
||||
|
||||
- <code>waitidle</code>
|
||||
|
||||
- <code>waitload</code>
|
||||
|
||||
- <code>waitstable</code>
|
||||
|
||||
- <code>getresource</code>
|
||||
|
||||
- <code>extract</code>
|
||||
@@ -3786,8 +3669,6 @@ Enum Values:
|
||||
|
||||
- <code>waitevent</code>
|
||||
|
||||
- <code>dialog</code>
|
||||
|
||||
- <code>keyboard</code>
|
||||
|
||||
- <code>debug</code>
|
||||
@@ -3858,33 +3739,11 @@ Appears in:
|
||||
Part Definitions:
|
||||
|
||||
|
||||
- <code>template-id</code> - ID of the template executed
|
||||
- <code>template-info</code> - Info Block of the template executed
|
||||
- <code>template-path</code> - Path of the template executed
|
||||
- <code>host</code> - Host is the input to the template
|
||||
- <code>port</code> - Port is the port of the host
|
||||
- <code>matched</code> - Matched is the input which was matched upon
|
||||
- <code>type</code> - Type is the type of request made
|
||||
- <code>timestamp</code> - Timestamp is the time when the request was made
|
||||
- <code>response</code> - JSON SSL protocol handshake details
|
||||
- <code>cipher</code> - Cipher is the encryption algorithm used
|
||||
- <code>domains</code> - Domains are the list of domain names in the certificate
|
||||
- <code>fingerprint_hash</code> - Fingerprint hash is the unique identifier of the certificate
|
||||
- <code>ip</code> - IP is the IP address of the server
|
||||
- <code>issuer_cn</code> - Issuer CN is the common name of the certificate issuer
|
||||
- <code>issuer_dn</code> - Issuer DN is the distinguished name of the certificate issuer
|
||||
- <code>issuer_org</code> - Issuer organization is the organization of the certificate issuer
|
||||
- <code>not_after</code> - Timestamp after which the remote cert expires
|
||||
- <code>not_before</code> - Timestamp before which the certificate is not valid
|
||||
- <code>probe_status</code> - Probe status indicates if the probe was successful
|
||||
- <code>serial</code> - Serial is the serial number of the certificate
|
||||
- <code>sni</code> - SNI is the server name indication used in the handshake
|
||||
- <code>subject_an</code> - Subject AN is the list of subject alternative names
|
||||
- <code>subject_cn</code> - Subject CN is the common name of the certificate subject
|
||||
- <code>subject_dn</code> - Subject DN is the distinguished name of the certificate subject
|
||||
- <code>subject_org</code> - Subject organization is the organization of the certificate subject
|
||||
- <code>tls_connection</code> - TLS connection is the type of TLS connection used
|
||||
- <code>tls_version</code> - TLS version is the version of the TLS protocol used
|
||||
- <code>host</code> - Host is the input to the template
|
||||
- <code>matched</code> - Matched is the input which was matched upon
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -4480,6 +4339,19 @@ Code contains code to execute for the javascript request.
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>timeout</code> <i>int</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Timeout in seconds is optional timeout for each javascript script execution (i.e init, pre-condition, code)
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>stop-at-first-match</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
@@ -4514,7 +4386,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"
|
||||
+28
-35
@@ -2,64 +2,57 @@ package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"os"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/invopop/jsonschema"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
)
|
||||
|
||||
var pathRegex = regexp.MustCompile(`github\.com/projectdiscovery/nuclei/v3/(?:internal|pkg)/(?:.*/)?([A-Za-z.]+)`)
|
||||
|
||||
func writeToFile(filename string, data []byte) {
|
||||
file, err := os.Create(filename)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create file %s: %s\n", filename, err)
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
|
||||
_, err = file.Write(data)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not write to file %s: %s\n", filename, err)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 3 {
|
||||
log.Fatalf("syntax: %s md-docs-file jsonschema-file\n", os.Args[0])
|
||||
}
|
||||
|
||||
// Generate YAML documentation
|
||||
// Generate yaml syntax documentation
|
||||
data, err := templates.GetTemplateDoc().Encode()
|
||||
if err != nil {
|
||||
log.Fatalf("Could not encode docs: %s\n", err)
|
||||
}
|
||||
writeToFile(os.Args[1], data)
|
||||
|
||||
// Generate JSON Schema
|
||||
r := &jsonschema.Reflector{
|
||||
Namer: func(t reflect.Type) string {
|
||||
if t.Kind() == reflect.Slice {
|
||||
return ""
|
||||
}
|
||||
return t.String()
|
||||
},
|
||||
if len(os.Args) < 3 {
|
||||
log.Fatalf("syntax: %s md-docs-file jsonschema-file\n", os.Args[0])
|
||||
}
|
||||
|
||||
err = os.WriteFile(os.Args[1], data, 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not write docs: %s\n", err)
|
||||
}
|
||||
|
||||
// Generate jsonschema
|
||||
r := &jsonschema.Reflector{}
|
||||
r.Namer = func(r reflect.Type) string {
|
||||
if r.Kind() == reflect.Slice {
|
||||
return ""
|
||||
}
|
||||
return r.String()
|
||||
}
|
||||
jsonschemaData := r.Reflect(&templates.Template{})
|
||||
|
||||
var buf bytes.Buffer
|
||||
encoder := json.NewEncoder(&buf)
|
||||
encoder.SetIndent("", " ")
|
||||
if err := encoder.Encode(jsonschemaData); err != nil {
|
||||
log.Fatalf("Could not encode JSON schema: %s\n", err)
|
||||
}
|
||||
_ = encoder.Encode(jsonschemaData)
|
||||
|
||||
schema := pathRegex.ReplaceAllString(buf.String(), "$1")
|
||||
writeToFile(os.Args[2], []byte(schema))
|
||||
schema := buf.String()
|
||||
for _, match := range pathRegex.FindAllStringSubmatch(schema, -1) {
|
||||
schema = strings.ReplaceAll(schema, match[0], match[1])
|
||||
}
|
||||
err = os.WriteFile(os.Args[2], []byte(schema), 0644)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not write jsonschema: %s\n", err)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
var (
|
||||
success = aurora.Green("[✓]").String()
|
||||
failed = aurora.Red("[✘]").String()
|
||||
githubAction = os.Getenv("GH_ACTION") == "true"
|
||||
|
||||
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"
|
||||
|
||||
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 file.Close()
|
||||
|
||||
errored, failedTestCases := runTestCases(file, debug)
|
||||
|
||||
if githubAction {
|
||||
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
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/bash
|
||||
|
||||
# reading os type from arguments
|
||||
CURRENT_OS=$1
|
||||
|
||||
if [ "${CURRENT_OS}" == "windows-latest" ];then
|
||||
extension=.exe
|
||||
fi
|
||||
|
||||
echo "::group::Building functional-test binary"
|
||||
go build -o functional-test$extension
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Building Nuclei binary from current branch"
|
||||
go build -o nuclei_dev$extension ../nuclei
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Installing nuclei templates"
|
||||
./nuclei_dev$extension -update-templates
|
||||
echo "::endgroup::"
|
||||
|
||||
echo "::group::Building latest release of nuclei"
|
||||
go build -o nuclei$extension -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei
|
||||
echo "::endgroup::"
|
||||
|
||||
echo 'Starting Nuclei functional test'
|
||||
./functional-test$extension -main ./nuclei$extension -dev ./nuclei_dev$extension -testcases testcases.txt
|
||||
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
|
||||
@@ -23,9 +23,7 @@ func main() {
|
||||
if err != nil {
|
||||
log.Fatalf("Could not create file: %s\n", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
defer file.Close()
|
||||
|
||||
err = filepath.WalkDir(templatesDirectory, func(path string, d fs.DirEntry, err error) error {
|
||||
if err != nil || d.IsDir() {
|
||||
|
||||
@@ -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 {
|
||||
@@ -129,7 +99,7 @@ type codePreCondition struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *codePreCondition) Execute(filePath string) error {
|
||||
results, err := testutils.RunNucleiArgsWithEnvAndGetResults(debug, getEnvValues(), "-t", filePath, "-u", "input", "-code", "-esc")
|
||||
results, err := testutils.RunNucleiArgsWithEnvAndGetResults(debug, getEnvValues(), "-t", filePath, "-u", "input", "-code")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
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 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{}},
|
||||
}
|
||||
@@ -24,7 +21,7 @@ type dslVersionWarning struct{}
|
||||
func (d *dslVersionWarning) Execute(templatePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "DSL version parsing warning test")
|
||||
fmt.Fprintf(w, "DSL version parsing warning test")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -40,7 +37,7 @@ type dslShowVersionWarning struct{}
|
||||
func (d *dslShowVersionWarning) Execute(templatePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "DSL version parsing warning test")
|
||||
fmt.Fprintf(w, "DSL version parsing warning test")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -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)
|
||||
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)
|
||||
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)
|
||||
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, "blog.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)
|
||||
_, _ = w.Write([]byte(fmt.Sprint(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,25 +1,22 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"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/utils/json"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
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{}},
|
||||
@@ -58,7 +55,7 @@ func (h *httpFuzzQuery) Execute(filePath string) error {
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -78,7 +75,7 @@ func (h *fuzzModeOverride) Execute(filePath string) error {
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -123,7 +120,7 @@ func (h *fuzzTypeOverride) Execute(filePath string) error {
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
value := r.URL.Query().Get("id")
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
fmt.Fprintf(w, "This is test matcher text: %v", value)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -167,7 +164,7 @@ func (h *HeadlessFuzzingQuery) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
resp := fmt.Sprintf("<html><body>%s</body></html>", r.URL.Query().Get("url"))
|
||||
_, _ = fmt.Fprint(w, resp)
|
||||
fmt.Fprint(w, resp)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -193,7 +190,7 @@ func (h *fuzzMultipleMode) Execute(filePath string) error {
|
||||
}
|
||||
w.Header().Set("Content-Type", "text/html")
|
||||
resp := fmt.Sprintf("<html><body><h1>This is multi-mode fuzzing test: %v <h1></body></html>", xClientId)
|
||||
_, _ = fmt.Fprint(w, resp)
|
||||
fmt.Fprint(w, resp)
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -203,4 +200,4 @@ func (h *fuzzMultipleMode) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
return expectResultsCount(got, 1)
|
||||
}
|
||||
}
|
||||
@@ -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{}},
|
||||
}
|
||||
|
||||
@@ -85,15 +82,14 @@ func (h *clientCertificate) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(w, "Hello, %s!\n", r.TLS.PeerCertificates[0].Subject)
|
||||
fmt.Fprintf(w, "Hello, %s!\n", r.TLS.PeerCertificates[0].Subject)
|
||||
})
|
||||
|
||||
_ = os.WriteFile("server.crt", []byte(serverCRT), permissionutil.ConfigFilePermission)
|
||||
_ = os.WriteFile("server.key", []byte(serverKey), permissionutil.ConfigFilePermission)
|
||||
defer func() {
|
||||
_ = os.Remove("server.crt")
|
||||
_ = os.Remove("server.key")
|
||||
}()
|
||||
defer os.Remove("server.crt")
|
||||
defer os.Remove("server.key")
|
||||
|
||||
serverCert, _ := tls.LoadX509KeyPair("server.crt", "server.key")
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
@@ -1,23 +1,18 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
||||
"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{}},
|
||||
{Path: "protocols/headless/headless-self-contained.yaml", TestCase: &headlessSelfContained{}},
|
||||
{Path: "protocols/headless/headless-header-action.yaml", TestCase: &headlessHeaderActions{}},
|
||||
{Path: "protocols/headless/headless-extract-values.yaml", TestCase: &headlessExtractValues{}},
|
||||
@@ -35,7 +30,7 @@ type headlessBasic struct{}
|
||||
func (h *headlessBasic) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "<html><body>%s</body></html>", r.URL.Query().Get("_"))
|
||||
_, _ = w.Write([]byte("<html><body></body></html>"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -181,9 +176,7 @@ func (h *headlessFileUpload) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
defer file.Close()
|
||||
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
@@ -240,9 +233,7 @@ func (h *headlessFileUploadNegative) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = file.Close()
|
||||
}()
|
||||
defer file.Close()
|
||||
|
||||
content, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
@@ -1,37 +1,32 @@
|
||||
//go:build integration
|
||||
// +build integration
|
||||
|
||||
package integration_test
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"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/utils/json"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
"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 {
|
||||
@@ -143,7 +108,7 @@ func (h *httpMatcherExtractorDynamicExtractor) Execute(filePath string) error {
|
||||
<a href="/domains">Domains</a>
|
||||
</body>
|
||||
</html>`
|
||||
_, _ = fmt.Fprint(w, html)
|
||||
fmt.Fprint(w, html)
|
||||
})
|
||||
router.GET("/domains", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
html := `<!DOCTYPE html>
|
||||
@@ -156,7 +121,7 @@ func (h *httpMatcherExtractorDynamicExtractor) Execute(filePath string) error {
|
||||
</body>
|
||||
</html>
|
||||
`
|
||||
_, _ = fmt.Fprint(w, html)
|
||||
fmt.Fprint(w, html)
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -178,7 +143,7 @@ func (h *httpInteractshRequest) Execute(filePath string) error {
|
||||
value := r.Header.Get("url")
|
||||
if value != "" {
|
||||
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -190,31 +155,7 @@ func (h *httpInteractshRequest) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
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)
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type httpDefaultMatcherCondition struct{}
|
||||
@@ -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
|
||||
@@ -272,7 +213,7 @@ func (h *httpInteractshStopAtFirstMatchRequest) Execute(filePath string) error {
|
||||
value := r.Header.Get("url")
|
||||
if value != "" {
|
||||
if resp, _ := retryablehttp.DefaultClient().Get(value); resp != nil {
|
||||
_ = resp.Body.Close()
|
||||
resp.Body.Close()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -294,7 +235,7 @@ func (h *httpGetHeaders) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if strings.EqualFold(r.Header.Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test headers matcher text")
|
||||
fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -315,7 +256,7 @@ func (h *httpGetQueryString) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test querystring matcher text")
|
||||
fmt.Fprintf(w, "This is test querystring matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -338,7 +279,7 @@ func (h *httpGetRedirects) Execute(filePath string) error {
|
||||
http.Redirect(w, r, "/redirected", http.StatusFound)
|
||||
})
|
||||
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -388,7 +329,7 @@ func (h *httpDisableRedirects) Execute(filePath string) error {
|
||||
http.Redirect(w, r, "/redirected", http.StatusMovedPermanently)
|
||||
})
|
||||
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -407,7 +348,7 @@ type httpGet struct{}
|
||||
func (h *httpGet) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -426,7 +367,7 @@ type httpDSLVariable struct{}
|
||||
func (h *httpDSLVariable) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -509,7 +450,7 @@ func (h *httpPostBody) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test post-body matcher text")
|
||||
fmt.Fprintf(w, "This is test post-body matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -544,7 +485,7 @@ func (h *httpPostJSONBody) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(obj.Username, "test") && strings.EqualFold(obj.Password, "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "This is test post-json-body matcher text")
|
||||
fmt.Fprintf(w, "This is test post-json-body matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -584,7 +525,7 @@ func (h *httpPostMultipartBody) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(password[0], "nuclei") && strings.EqualFold(file[0].Filename, "username") {
|
||||
_, _ = fmt.Fprintf(w, "This is test post-multipart matcher text")
|
||||
fmt.Fprintf(w, "This is test post-multipart matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -614,12 +555,12 @@ func (h *httpRawDynamicExtractor) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("testing"), "parameter") {
|
||||
_, _ = fmt.Fprintf(w, "Token: 'nuclei'")
|
||||
fmt.Fprintf(w, "Token: 'nuclei'")
|
||||
}
|
||||
})
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if strings.EqualFold(r.URL.Query().Get("username"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "Test is test-dynamic-extractor-raw matcher text")
|
||||
fmt.Fprintf(w, "Test is test-dynamic-extractor-raw matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -643,7 +584,7 @@ func (h *httpRawGetQuery) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if strings.EqualFold(r.URL.Query().Get("test"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "Test is test raw-get-query-matcher text")
|
||||
fmt.Fprintf(w, "Test is test raw-get-query-matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -663,7 +604,7 @@ type httpRawGet struct{}
|
||||
func (h *httpRawGet) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "Test is test raw-get-matcher text")
|
||||
fmt.Fprintf(w, "Test is test raw-get-matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -687,12 +628,12 @@ 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")
|
||||
fmt.Fprintf(w, "Test is test raw-params-matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -744,11 +685,11 @@ func (h *httpRawPayload) Execute(filePath string) error {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
if !strings.EqualFold(r.Header.Get("another_header"), "bnVjbGVp") && !strings.EqualFold(r.Header.Get("another_header"), "Z3Vlc3Q=") {
|
||||
if !(strings.EqualFold(r.Header.Get("another_header"), "bnVjbGVp") || strings.EqualFold(r.Header.Get("another_header"), "Z3Vlc3Q=")) {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && (strings.EqualFold(r.Form.Get("password"), "nuclei") || strings.EqualFold(r.Form.Get("password"), "guest")) {
|
||||
_, _ = fmt.Fprintf(w, "Test is raw-payload matcher text")
|
||||
fmt.Fprintf(w, "Test is raw-payload matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -778,7 +719,7 @@ func (h *httpRawPostBody) Execute(filePath string) error {
|
||||
return
|
||||
}
|
||||
if strings.EqualFold(r.Form.Get("username"), "test") && strings.EqualFold(r.Form.Get("password"), "nuclei") {
|
||||
_, _ = fmt.Fprintf(w, "Test is test raw-post-body-matcher text")
|
||||
fmt.Fprintf(w, "Test is test raw-post-body-matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -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
|
||||
}
|
||||
@@ -888,7 +829,10 @@ func (h *httpPaths) Execute(filepath string) error {
|
||||
}
|
||||
|
||||
if len(expected) > len(actual) {
|
||||
actualValuesIndex := max(len(actual)-1, 0)
|
||||
actualValuesIndex := len(actual) - 1
|
||||
if actualValuesIndex < 0 {
|
||||
actualValuesIndex = 0
|
||||
}
|
||||
return fmt.Errorf("missing values : %v", expected[actualValuesIndex:])
|
||||
} else if len(expected) < len(actual) {
|
||||
return fmt.Errorf("unexpected values : %v", actual[len(expected)-1:])
|
||||
@@ -928,7 +872,7 @@ func (h *httpRawCookieReuse) Execute(filePath string) error {
|
||||
}
|
||||
|
||||
if strings.EqualFold(cookie.Value, "test") {
|
||||
_, _ = fmt.Fprintf(w, "Test is test-cookie-reuse matcher text")
|
||||
fmt.Fprintf(w, "Test is test-cookie-reuse matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -999,17 +943,16 @@ 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)
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
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
|
||||
}
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "", debug, "-esc")
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1029,24 +972,23 @@ 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)
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
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
|
||||
}
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "", debug, "-esc")
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1070,29 +1012,26 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
gotReqToEndpoints = append(gotReqToEndpoints, "/two")
|
||||
_, _ = w.Write([]byte("This is self-contained response"))
|
||||
})
|
||||
server := httptest.NewServer(router)
|
||||
server := &http.Server{
|
||||
Addr: fmt.Sprintf("localhost:%d", defaultStaticPort),
|
||||
Handler: router,
|
||||
}
|
||||
go func() {
|
||||
_ = server.ListenAndServe()
|
||||
}()
|
||||
defer 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()
|
||||
}()
|
||||
defer 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())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -1102,38 +1041,18 @@ 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
|
||||
func (h *httpGetCaseInsensitive) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "THIS IS TEST MATCHER TEXT")
|
||||
fmt.Fprintf(w, "THIS IS TEST MATCHER TEXT")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1152,7 +1071,7 @@ type httpGetCaseInsensitiveCluster struct{}
|
||||
func (h *httpGetCaseInsensitiveCluster) Execute(filesPath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1229,78 +1148,13 @@ 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
|
||||
func (h *httpStopAtFirstMatch) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test")
|
||||
fmt.Fprintf(w, "This is test")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1319,7 +1173,7 @@ type httpStopAtFirstMatchWithExtractors struct{}
|
||||
func (h *httpStopAtFirstMatchWithExtractors) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test")
|
||||
fmt.Fprintf(w, "This is test")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1338,7 +1192,7 @@ type httpVariables struct{}
|
||||
func (h *httpVariables) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "%s\n%s\n%s", r.Header.Get("Test"), r.Header.Get("Another"), r.Header.Get("Email"))
|
||||
fmt.Fprintf(w, "%s\n%s\n%s", r.Header.Get("Test"), r.Header.Get("Another"), r.Header.Get("Email"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1361,30 +1215,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
|
||||
@@ -1464,7 +1294,7 @@ func (h *httpRedirectMatchURL) Execute(filePath string) error {
|
||||
_, _ = w.Write([]byte("This is test redirects matcher text"))
|
||||
})
|
||||
router.GET("/redirected", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
fmt.Fprintf(w, "This is test redirects matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1512,7 +1342,7 @@ func (h *annotationTimeout) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
time.Sleep(4 * time.Second)
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1532,7 +1362,7 @@ func (h *customAttackType) Execute(filePath string) error {
|
||||
got := []string{}
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
got = append(got, r.URL.RawQuery)
|
||||
_, _ = fmt.Fprintf(w, "This is test custom payload")
|
||||
fmt.Fprintf(w, "This is test custom payload")
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1580,7 +1410,7 @@ func (h *httpCLBodyWithoutHeader) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header()["Content-Length"] = []string{"-1"}
|
||||
_, _ = fmt.Fprintf(w, "this is a test")
|
||||
fmt.Fprintf(w, "this is a test")
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1600,7 +1430,7 @@ func (h *httpCLBodyWithHeader) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.Header()["Content-Length"] = []string{"50000"}
|
||||
_, _ = fmt.Fprintf(w, "this is a test")
|
||||
fmt.Fprintf(w, "this is a test")
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1619,7 +1449,7 @@ type ConstantWithCliVar struct{}
|
||||
func (h *ConstantWithCliVar) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprint(w, r.URL.Query().Get("p"))
|
||||
fmt.Fprint(w, r.URL.Query().Get("p"))
|
||||
})
|
||||
ts := httptest.NewTLSServer(router)
|
||||
defer ts.Close()
|
||||
@@ -1631,26 +1461,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
|
||||
@@ -1676,10 +1486,10 @@ type httpDisablePathAutomerge struct{}
|
||||
func (h *httpDisablePathAutomerge) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/api/v1/test", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprint(w, r.URL.Query().Get("id"))
|
||||
fmt.Fprint(w, r.URL.Query().Get("id"))
|
||||
})
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprint(w, "empty path in raw request")
|
||||
fmt.Fprint(w, "empty path in raw request")
|
||||
})
|
||||
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -1713,10 +1523,10 @@ func (h *httpPreprocessor) Execute(filePath string) error {
|
||||
value := r.URL.RequestURI()
|
||||
if re.MatchString(value) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprint(w, "ok")
|
||||
fmt.Fprint(w, "ok")
|
||||
} else {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
_, _ = fmt.Fprint(w, "not ok")
|
||||
fmt.Fprint(w, "not ok")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -1737,11 +1547,11 @@ func (h *httpMultiRequest) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/ping", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprint(w, "ping")
|
||||
fmt.Fprint(w, "ping")
|
||||
})
|
||||
router.GET("/pong", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
_, _ = fmt.Fprint(w, "pong")
|
||||
fmt.Fprint(w, "pong")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -0,0 +1,224 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"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"
|
||||
githubAction = os.Getenv("GH_ACTION") == "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,
|
||||
}
|
||||
// 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 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 githubAction {
|
||||
debug = true
|
||||
fmt.Println("::group::Failed integration tests in debug mode")
|
||||
_ = runTests(failedTestTemplatePaths)
|
||||
fmt.Println("::endgroup::")
|
||||
}
|
||||
|
||||
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{}},
|
||||
}
|
||||
@@ -51,9 +48,9 @@ func (h *goIntegrationTest) Execute(templatePath string) error {
|
||||
router := httprouter.New()
|
||||
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
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")
|
||||
fmt.Fprintf(w, "This is test headers matcher text")
|
||||
}
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
@@ -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,207 @@
|
||||
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 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 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()
|
||||
|
||||
_, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-template-url", ts.URL+"/404")
|
||||
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()
|
||||
|
||||
_, err := testutils.RunNucleiBareArgsAndGetResults(debug, nil, "-target", ts.URL, "-workflow-url", ts.URL+"/404")
|
||||
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)
|
||||
}
|
||||
@@ -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{}},
|
||||
@@ -17,7 +14,7 @@ type multiProtoDynamicExtractor struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *multiProtoDynamicExtractor) Execute(templatePath string) error {
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "docs.projectdiscovery.io", debug)
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(templatePath, "blog.projectdiscovery.io", debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -0,0 +1,229 @@
|
||||
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 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 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 conn.Close()
|
||||
|
||||
_, _ = conn.Write([]byte("Authentication successful"))
|
||||
})
|
||||
defer ts.Close()
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "", debug)
|
||||
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 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 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 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,141 @@
|
||||
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 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 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 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 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 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 {
|
||||
ts := testutils.NewTCPServer(&tls.Config{}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer 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,30 @@
|
||||
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 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,21 @@
|
||||
//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"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
)
|
||||
|
||||
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{}},
|
||||
@@ -27,17 +25,42 @@ var workflowTestcases = []integrationCase{
|
||||
{Path: "workflow/dns-value-share-workflow.yaml", TestCase: &workflowDnsKeyValueShare{}},
|
||||
{Path: "workflow/code-value-share-workflow.yaml", TestCase: &workflowCodeKeyValueShare{}, DisableOn: isCodeDisabled}, // isCodeDisabled declared in code.go
|
||||
{Path: "workflow/multiprotocol-value-share-workflow.yaml", TestCase: &workflowMultiProtocolKeyValueShare{}},
|
||||
{Path: "workflow/multimatch-value-share-workflow.yaml", TestCase: &workflowMultiMatchKeyValueShare{}},
|
||||
{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
|
||||
func (h *workflowBasic) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -56,7 +79,7 @@ type workflowConditionMatched struct{}
|
||||
func (h *workflowConditionMatched) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -75,7 +98,7 @@ type workflowConditionUnmatch struct{}
|
||||
func (h *workflowConditionUnmatch) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -94,7 +117,7 @@ type workflowMatcherName struct{}
|
||||
func (h *workflowMatcherName) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -113,7 +136,7 @@ type workflowComplexConditions struct{}
|
||||
func (h *workflowComplexConditions) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -137,11 +160,11 @@ type workflowHttpKeyValueShare struct{}
|
||||
func (h *workflowHttpKeyValueShare) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "href=\"test-value\"")
|
||||
fmt.Fprintf(w, "href=\"test-value\"")
|
||||
})
|
||||
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
_, _ = fmt.Fprintf(w, "%s", body)
|
||||
fmt.Fprintf(w, "%s", body)
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -189,11 +212,11 @@ func (h *workflowMultiProtocolKeyValueShare) Execute(filePath string) error {
|
||||
router := httprouter.New()
|
||||
// the response of path1 contains a domain that will be extracted and shared with the second template
|
||||
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "href=\"blog.projectdiscovery.io\"")
|
||||
fmt.Fprintf(w, "href=\"blog.projectdiscovery.io\"")
|
||||
})
|
||||
// path2 responds with the value of the "extracted" query parameter, e.g.: /path2?extracted=blog.projectdiscovery.io => blog.projectdiscovery.io
|
||||
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "%s", r.URL.Query().Get("extracted"))
|
||||
fmt.Fprintf(w, "%s", r.URL.Query().Get("extracted"))
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
@@ -206,44 +229,6 @@ func (h *workflowMultiProtocolKeyValueShare) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
|
||||
type workflowMultiMatchKeyValueShare struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *workflowMultiMatchKeyValueShare) Execute(filePath string) error {
|
||||
var receivedData []string
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
_, _ = fmt.Fprintf(w, "href=\"test-value-%s\"", r.URL.Query().Get("v"))
|
||||
})
|
||||
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
receivedData = append(receivedData, string(body))
|
||||
_, _ = fmt.Fprintf(w, "test-value")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we received the data from both request to /path1 and it is not overwritten by the later one.
|
||||
// They will appear in brackets because of another bug: https://github.com/orgs/projectdiscovery/discussions/3766
|
||||
if !sliceutil.Contains(receivedData, "[test-value-1]") || !sliceutil.Contains(receivedData, "[test-value-2]") {
|
||||
return fmt.Errorf(
|
||||
"incorrect data: did not receive both extracted data from the first request!\nReceived Data:\n\t%s\nResults:\n\t%s",
|
||||
strings.Join(receivedData, "\n\t"),
|
||||
strings.Join(results, "\n\t"),
|
||||
)
|
||||
}
|
||||
// The number of expected results is 3: the workflow's Matcher Name based condition check forwards both match, and the other branch with simple subtemplates goes with one
|
||||
return expectResultsCount(results, 3)
|
||||
}
|
||||
|
||||
type workflowSharedCookies 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
|
||||
@@ -155,34 +142,4 @@
|
||||
# # Username for the elasticsearch instance
|
||||
# username: test
|
||||
# # Password is the password for elasticsearch instance
|
||||
# password: test
|
||||
#linear:
|
||||
# # api-key is the API key for the linear account
|
||||
# api-key: ""
|
||||
# # allow-list sets a tracker level filter to only create issues for templates with
|
||||
# # these severity labels or tags (does not affect exporters. set those globally)
|
||||
# deny-list:
|
||||
# severity: critical
|
||||
# # deny-list sets a tracker level filter to never create issues for templates with
|
||||
# # these severity labels or tags (does not affect exporters. set those globally)
|
||||
# deny-list:
|
||||
# severity: low
|
||||
# # team-id is the ID of the team in Linear
|
||||
# team-id: ""
|
||||
# # project-id is the ID of the project in Linear
|
||||
# project-id: ""
|
||||
# # duplicate-issue-check flag to enable duplicate tracking issue check
|
||||
# duplicate-issue-check: false
|
||||
# # open-state-id is the ID of the open state in Linear
|
||||
# open-state-id: ""
|
||||
#mongodb:
|
||||
# # the connection string to the MongoDB database
|
||||
# # (e.g., mongodb://root:example@localhost:27017/nuclei?ssl=false&authSource=admin)
|
||||
# connection-string: ""
|
||||
# # the name of the collection to store the issues
|
||||
# collection-name: ""
|
||||
# # excludes the Request and Response from the results (helps with filesize)
|
||||
# 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
|
||||
# password: test
|
||||
+86
-310
@@ -9,20 +9,16 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"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 +36,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 +70,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 +86,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,75 +95,36 @@ 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
|
||||
}
|
||||
|
||||
// Profiling & tracing related code
|
||||
// Profiling related code
|
||||
if memProfile != "" {
|
||||
memProfile = strings.TrimSuffix(memProfile, filepath.Ext(memProfile))
|
||||
|
||||
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)
|
||||
}
|
||||
return f
|
||||
f, err := os.Create(memProfile)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("profile: could not create memory profile %q: %v", memProfile, err)
|
||||
}
|
||||
|
||||
memProfileFile := createProfileFile(".mem", "memory")
|
||||
cpuProfileFile := createProfileFile(".cpu", "CPU")
|
||||
traceFile := createProfileFile(".trace", "trace")
|
||||
|
||||
oldMemProfileRate := runtime.MemProfileRate
|
||||
old := runtime.MemProfileRate
|
||||
runtime.MemProfileRate = 4096
|
||||
|
||||
// Start tracing
|
||||
if err := trace.Start(traceFile); err != nil {
|
||||
options.Logger.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.Print().Msgf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, memProfile)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
pprof.StopCPUProfile()
|
||||
_ = memProfileFile.Close()
|
||||
_ = traceFile.Close()
|
||||
trace.Stop()
|
||||
|
||||
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())
|
||||
_ = pprof.Lookup("heap").WriteTo(f, 0)
|
||||
f.Close()
|
||||
runtime.MemProfileRate = old
|
||||
gologger.Print().Msgf("profile: memory profiling disabled, %s", memProfile)
|
||||
}()
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
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,62 +137,53 @@ 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")
|
||||
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()
|
||||
// on successful execution remove the resume file in case it exists
|
||||
if fileutil.FileExists(resumeFileName) {
|
||||
_ = os.Remove(resumeFileName)
|
||||
os.Remove(resumeFileName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -267,9 +206,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 +216,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",
|
||||
@@ -288,21 +224,17 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.AutomaticScan, "automatic-scan", "as", false, "automatic web scan using wappalyzer technology detection to tags mapping"),
|
||||
flagSet.StringSliceVarP(&options.Templates, "templates", "t", nil, "list of template or template directory to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.TemplateURLs, "template-url", "turl", nil, "template url or list containing template urls to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVarP(&options.AITemplatePrompt, "prompt", "ai", "", "generate and run template using ai prompt"),
|
||||
flagSet.StringSliceVarP(&options.Workflows, "workflows", "w", nil, "list of workflow or workflow directory to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.WorkflowURLs, "workflow-url", "wurl", nil, "workflow url or list containing workflow urls to run (comma-separated, file)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
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"),
|
||||
flagSet.BoolVar(&options.EnableCodeTemplates, "code", false, "enable loading code protocol-based templates"),
|
||||
flagSet.BoolVarP(&options.DisableUnsignedTemplates, "disable-unsigned-templates", "dut", false, "disable running unsigned templates or templates with mismatched signature"),
|
||||
flagSet.BoolVarP(&options.EnableSelfContainedTemplates, "enable-self-contained", "esc", false, "enable loading self-contained templates"),
|
||||
flagSet.BoolVarP(&options.EnableGlobalMatchersTemplates, "enable-global-matchers", "egm", false, "enable loading global matchers templates"),
|
||||
flagSet.BoolVar(&options.EnableFileTemplates, "file", false, "enable loading file templates"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("filters", "Filtering",
|
||||
@@ -330,7 +262,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,8 +272,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),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("configs", "Configurations",
|
||||
@@ -367,6 +297,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.BoolVarP(&options.ShowMatchLine, "show-match-line", "sml", false, "show match lines for file templates, works with extractors only"),
|
||||
flagSet.BoolVar(&options.ZTLS, "ztls", false, "use ztls library with autofallback to standard one for tls13 [Deprecated] autofallback to ztls is enabled by default"), //nolint:all
|
||||
flagSet.StringVar(&options.SNI, "sni", "", "tls sni hostname to use (default: input domain name)"),
|
||||
flagSet.DurationVarP(&options.DialerTimeout, "dialer-timeout", "dt", 0, "timeout for network requests."),
|
||||
flagSet.DurationVarP(&options.DialerKeepAlive, "dialer-keep-alive", "dka", 0, "keep-alive duration for network requests."),
|
||||
flagSet.BoolVarP(&options.AllowLocalFileAccess, "allow-local-file-access", "lfa", false, "allows file (payload) access anywhere on the system"),
|
||||
flagSet.BoolVarP(&options.RestrictLocalNetworkAccess, "restrict-local-network-access", "lna", false, "blocks connections to the local / private network"),
|
||||
@@ -375,6 +306,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
||||
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 0, "max response size to read in bytes"),
|
||||
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", unitutils.Mega, "max response size to read in bytes"),
|
||||
flagSet.DurationVarP(&options.ResponseReadTimeout, "response-read-timeout", "rrt", time.Duration(5*time.Second), "response read timeout in seconds"),
|
||||
flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"),
|
||||
flagSet.BoolVarP(&options.TlsImpersonate, "tls-impersonate", "tlsi", false, "enable experimental client hello (ja3) tls randomization"),
|
||||
flagSet.StringVarP(&options.HttpApiEndpoint, "http-api-endpoint", "hae", "", "experimental http api endpoint"),
|
||||
@@ -395,15 +327,9 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"),
|
||||
flagSet.BoolVar(&fuzzFlag, "fuzz", false, "enable loading fuzzing templates (Deprecated: use -dast instead)"),
|
||||
flagSet.BoolVar(&options.DAST, "dast", false, "enable / run dast (fuzz) nuclei templates"),
|
||||
flagSet.BoolVarP(&options.DASTServer, "dast-server", "dts", false, "enable dast server mode (live fuzzing)"),
|
||||
flagSet.BoolVarP(&options.DASTReport, "dast-report", "dtr", false, "write dast scan report to file"),
|
||||
flagSet.StringVarP(&options.DASTServerToken, "dast-server-token", "dtst", "", "dast server token (optional)"),
|
||||
flagSet.StringVarP(&options.DASTServerAddress, "dast-server-address", "dtsa", "localhost:9055", "dast server address"),
|
||||
flagSet.BoolVarP(&options.DisplayFuzzPoints, "display-fuzz-points", "dfp", false, "display fuzz points in the output for debugging"),
|
||||
flagSet.IntVar(&options.FuzzParamFrequency, "fuzz-param-frequency", 10, "frequency of uninteresting parameters for fuzzing before skipping"),
|
||||
flagSet.StringVarP(&options.FuzzAggressionLevel, "fuzz-aggression", "fa", "low", "fuzzing aggression level controls payload count for fuzz (low, medium, high)"),
|
||||
flagSet.StringSliceVarP(&options.Scope, "fuzz-scope", "cs", nil, "in scope url regex to be followed by fuzzer", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.OutOfScope, "fuzz-out-scope", "cos", nil, "out of scope url regex to be excluded by fuzzer", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("uncover", "Uncover",
|
||||
@@ -426,7 +352,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 +380,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"),
|
||||
)
|
||||
|
||||
@@ -471,10 +395,9 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CallbackVar(printVersion, "version", "show nuclei version"),
|
||||
flagSet.BoolVarP(&options.HangMonitor, "hang-monitor", "hm", false, "enable nuclei hang monitoring"),
|
||||
flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"),
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "generate memory (heap) profile & trace files"),
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "optional nuclei memory profile dump file"),
|
||||
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "svd", false, "show variables dump for debugging"),
|
||||
flagSet.IntVarP(&options.VarDumpLimit, "var-dump-limit", "vdl", 255, "limit the number of characters displayed in var dump"),
|
||||
flagSet.BoolVarP(&options.EnablePprof, "enable-pprof", "ep", false, "enable pprof debugging server"),
|
||||
flagSet.CallbackVarP(printTemplateVersion, "templates-version", "tv", "shows the version of the installed nuclei-templates"),
|
||||
flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"),
|
||||
@@ -487,28 +410,18 @@ 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"),
|
||||
flagSet.IntVarP(&options.StatsInterval, "stats-interval", "si", 5, "number of seconds to wait between showing a statistics update"),
|
||||
flagSet.IntVarP(&options.MetricsPort, "metrics-port", "mp", 9092, "port to expose nuclei metrics on"),
|
||||
flagSet.BoolVarP(&options.HTTPStats, "http-stats", "hps", false, "enable http status capturing (experimental)"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("cloud", "Cloud",
|
||||
flagSet.DynamicVar(&pdcpauth, "auth", "true", "configure projectdiscovery cloud (pdcp) api key"),
|
||||
flagSet.StringVarP(&options.TeamID, "team-id", "tid", _pdcp.TeamIDEnv, "upload scan results to given team id (optional)"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard [DEPRECATED use -dashboard]"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard"),
|
||||
flagSet.StringVarP(&options.ScanID, "scan-id", "sid", "", "upload scan results to existing scan id (optional)"),
|
||||
flagSet.StringVarP(&options.ScanName, "scan-name", "sname", "", "scan name to set (optional)"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "dashboard", "pd", false, "upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard"),
|
||||
flagSet.StringVarP(&options.ScanUploadFile, "dashboard-upload", "pdu", "", "upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("Authentication", "Authentication",
|
||||
@@ -548,11 +461,6 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
options.DAST = true
|
||||
}
|
||||
|
||||
// All cloud-based templates depend on both code and self-contained templates.
|
||||
if options.EnableCodeTemplates {
|
||||
options.EnableSelfContainedTemplates = true
|
||||
}
|
||||
|
||||
// api key hierarchy: cli flag > env var > .pdcp/credential file
|
||||
if pdcpauth == "true" {
|
||||
runner.AuthWithPDCP()
|
||||
@@ -566,16 +474,7 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
|
||||
}
|
||||
}
|
||||
|
||||
// guard cloud services with credentials
|
||||
if options.AITemplatePrompt != "" {
|
||||
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")
|
||||
}
|
||||
}
|
||||
|
||||
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 +496,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 +515,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 +530,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 +566,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 +595,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 +627,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 +652,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 +678,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,196 +0,0 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/gologger/levels"
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/runner"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
var (
|
||||
projectPath string
|
||||
targetURL string
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
// Set up
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent)
|
||||
_ = os.Setenv("DISABLE_STDOUT", "true")
|
||||
|
||||
var err error
|
||||
|
||||
projectPath, err = os.MkdirTemp("", "nuclei-benchmark-")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
dummyServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
}))
|
||||
targetURL = dummyServer.URL
|
||||
|
||||
// Execute tests
|
||||
|
||||
exitCode := m.Run()
|
||||
|
||||
// Tear down
|
||||
|
||||
dummyServer.Close()
|
||||
_ = os.RemoveAll(projectPath)
|
||||
_ = os.Unsetenv("DISABLE_STDOUT")
|
||||
|
||||
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"},
|
||||
ProjectPath: projectPath,
|
||||
StatsInterval: 5,
|
||||
MetricsPort: 9092,
|
||||
MaxHostError: 30,
|
||||
NoHostErrors: true,
|
||||
BulkSize: 25,
|
||||
TemplateThreads: 25,
|
||||
HeadlessBulkSize: 10,
|
||||
HeadlessTemplateThreads: 10,
|
||||
Timeout: 10,
|
||||
Retries: 1,
|
||||
RateLimit: 150,
|
||||
RateLimitDuration: time.Duration(time.Second),
|
||||
RateLimitMinute: 0,
|
||||
PageTimeout: 20,
|
||||
InteractionsCacheSize: 5000,
|
||||
InteractionsPollDuration: 5,
|
||||
InteractionsEviction: 60,
|
||||
InteractionsCoolDownPeriod: 5,
|
||||
MaxRedirects: 10,
|
||||
Silent: true,
|
||||
VarDumpLimit: 255,
|
||||
JSONRequests: true,
|
||||
StoreResponseDir: "output",
|
||||
InputFileMode: "list",
|
||||
ResponseReadSize: 0,
|
||||
ResponseSaveSize: 1048576,
|
||||
InputReadTimeout: time.Duration(3 * time.Minute),
|
||||
UncoverField: "ip:port",
|
||||
UncoverLimit: 100,
|
||||
UncoverRateLimit: 60,
|
||||
ScanStrategy: "auto",
|
||||
FuzzAggressionLevel: "low",
|
||||
FuzzParamFrequency: 10,
|
||||
TeamID: "none",
|
||||
JsConcurrency: 120,
|
||||
PayloadConcurrency: 25,
|
||||
ProbeConcurrency: 50,
|
||||
LoadHelperFileFunction: types.DefaultOptions().LoadHelperFileFunction,
|
||||
// DialerKeepAlive: time.Duration(0),
|
||||
// DASTServerAddress: "localhost:9055",
|
||||
ExecutionId: "test",
|
||||
Logger: gologger.DefaultLogger,
|
||||
}
|
||||
}
|
||||
|
||||
func runEnumBenchmark(b *testing.B, options *types.Options) {
|
||||
runner.ParseOptions(options)
|
||||
|
||||
nucleiRunner, err := runner.New(options)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create runner: %s", err)
|
||||
}
|
||||
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.ReportAllocs()
|
||||
|
||||
for b.Loop() {
|
||||
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}
|
||||
|
||||
runEnumBenchmark(b, options)
|
||||
})
|
||||
|
||||
// Case: https://github.com/projectdiscovery/nuclei/pull/6258
|
||||
b.Run("Multiproto", func(b *testing.B) {
|
||||
options := getDefaultOptions()
|
||||
options.Targets = []string{targetURL}
|
||||
options.Templates = []string{"./cmd/nuclei/testdata/benchmark/multiproto/"}
|
||||
|
||||
runEnumBenchmark(b, options)
|
||||
})
|
||||
}
|
||||
@@ -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"
|
||||
-239
@@ -1,239 +0,0 @@
|
||||
id: basic-template-multiproto-mixed
|
||||
|
||||
info:
|
||||
name: Test Template Multiple Protocols (Mixed)
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
http:
|
||||
- method: GET
|
||||
id: first_iter_http
|
||||
path:
|
||||
- '{{BaseURL}}/1'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/2'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/3'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/4'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/5'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/6'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/7'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/8'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- method: GET
|
||||
path:
|
||||
- '{{BaseURL}}/9'
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /10 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /11 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /12 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /13 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /14 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET / HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /15 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /16 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /17 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
|
||||
- raw:
|
||||
- |
|
||||
GET /18 HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
Origin: {{BaseURL}}
|
||||
Connection: close
|
||||
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_4) AppleWebKit/537.36 (KHTML, like Gecko)
|
||||
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
|
||||
Accept-Language: en-US,en;q=0.9
|
||||
|
||||
matchers:
|
||||
- type: word
|
||||
words:
|
||||
- "Test is test matcher text"
|
||||
Loaded 100 of 922 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user