Compare commits

..
1 Commits
Author SHA1 Message Date
Mzack9999 d075a4e217 per-target rate limit 2025-08-19 20:39:16 +02:00
298 changed files with 2091 additions and 13492 deletions

No files matched your search

-76
View File
@@ -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
+6 -12
View File
@@ -36,10 +36,7 @@ body:
description: |
Steps to reproduce the behavior, for example, commands to run Nuclei.
💡 Prefer copying text output over using screenshots for easier troubleshooting.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
placeholder: |
@@ -52,10 +49,7 @@ body:
description: |
Please copy and paste any relevant log output. This will be automatically formatted into code, so no need for backticks.
💡 Prefer copying text output over using screenshots for easier troubleshooting.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.<br>
📊 For performance or memory investigations, use **`-profile-mem`**, which generates `*.cpu`, `*.mem`, and `*.trace` files. Since GitHub doesn't support these formats directly, compress them (e.g., .zip or .tar.gz) and attach the archive under the "Anything else" section below.
📝 For a more detailed output that could help in troubleshooting, you may want to run Nuclei with the **`-verbose`** or **`-debug`** flags. This will provide additional insights into what's happening under the hood.
:warning: **Please redact any literal target hosts/URLs or other sensitive information.**
render: shell
@@ -64,9 +58,9 @@ body:
label: Environment
description: |
Examples:
- **OS**: Ubuntu 24.04
- **Nuclei** (`nuclei -version`): v3.6.0
- **Go** (`go version`): go1.24.0 _(only if you've installed it via the `go install` command)_
- **OS**: Ubuntu 20.04
- **Nuclei** (`nuclei -version`): v3.3.1
- **Go** (`go version`): go1.22.0 _(only if you've installed it via the `go install` command)_
value: |
- OS:
- Nuclei:
@@ -78,7 +72,7 @@ body:
attributes:
label: Anything else?
description: |
Links? References? Templates? CPU, memory, and trace profiles? Anything that will give us more context about the issue you are encountering!
Links? References? Templates? Anything that will give us more context about the issue you are encountering!
Tip: You can attach images or log files by clicking this area to highlight it and then dragging files in.
validations:
+8 -12
View File
@@ -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
@@ -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
-3
View File
@@ -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
View File
@@ -2,7 +2,6 @@ addReviewers: true
reviewers:
- dogancanbakir
- dwisiswant0
- mzack9999
numberOfReviewers: 1
skipKeywords:
+17 -13
View File
@@ -1,24 +1,28 @@
name: 🔀 Auto merge PR
name: 🤖 Auto Merge
on:
# pull_request:
# types: [opened, synchronize, reopened, ready_for_review]
# pull_request_review:
# types: [submitted]
pull_request_review:
types: [submitted]
workflow_run:
workflows: ["♾️ Compatibility Checks"]
types: [completed]
workflows: ["♾️ Compatibility Check"]
types:
- completed
permissions:
contents: write
pull-requests: write
issues: write
repository-projects: write
jobs:
auto-merge-dependabot:
auto-merge:
runs-on: ubuntu-latest
if: ${{ github.event.workflow_run.conclusion == 'success' }}
if: github.actor == 'dependabot[bot]'
steps:
- uses: projectdiscovery/actions/pr/approve@v1
- uses: projectdiscovery/actions/pr/merge@v1
- uses: actions/checkout@v4
with:
auto: "true"
token: ${{ secrets.DEPENDABOT_PAT }}
- uses: ahmadnassri/action-dependabot-auto-merge@v2
with:
github-token: ${{ secrets.DEPENDABOT_PAT }}
target: all
+3 -4
View File
@@ -7,14 +7,13 @@ on:
- dev
jobs:
compat-checks:
if: ${{ github.actor == 'dependabot[bot]' }}
check:
if: github.actor == 'dependabot[bot]'
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go/compat-checks@v1
with:
go-version: "stable"
release-test: true
-61
View File
@@ -1,61 +0,0 @@
name: 📊 Flamegraph
on:
workflow_call: {}
jobs:
flamegraph:
name: "Flamegraph"
env:
PROFILE_MEM: "/tmp/nuclei-profile"
TARGET_URL: "http://honey.scanme.sh/-/?foo=bar"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- run: make build
- name: "Setup environment (push)"
if: ${{ github.event_name == 'push' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
- name: "Setup environment (pull_request)"
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
- run: ./bin/nuclei -update-templates
- run: |
./bin/nuclei -silent -target="${TARGET_URL}" \
-disable-update-check \
-no-mhe -no-httpx \
-code -dast -file -headless \
-enable-self-contained -enable-global-matchers \
-rate-limit=0 \
-concurrency=250 \
-headless-concurrency=100 \
-payload-concurrency=250 \
-bulk-size=250 \
-headless-bulk-size=100 \
-profile-mem="${PROFILE_MEM}"
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
+3 -3
View File
@@ -4,14 +4,14 @@ on:
push:
branches:
- dev
workflow_dispatch: {}
workflow_dispatch:
jobs:
publish-docs:
if: ${{ !endsWith(github.actor, '[bot]') }}
if: "${{ !endsWith(github.actor, '[bot]') }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/setup/git@v1
- run: make syntax-docs
+38 -33
View File
@@ -1,50 +1,55 @@
name: 👤 Generate PGO
on:
workflow_dispatch: {}
workflow_call: {}
push:
branches: ["dev"]
paths:
- '**.go'
- '**.mod'
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
# TODO(dwisiswant0): https://go.dev/doc/pgo#merging-profiles
jobs:
pgo:
strategy:
matrix:
targets: [150]
runs-on: ubuntu-latest
if: github.repository == 'projectdiscovery/nuclei'
permissions:
contents: write
env:
PGO_FILE: "cmd/nuclei/default.pgo"
TARGET: "https://honey.scanme.sh"
TARGET_COUNT: "100"
TARGET_LIST: "/tmp/targets.txt"
PROFILE_MEM: "/tmp/nuclei-profile"
LIST_FILE: "/tmp/targets-${{ matrix.targets }}.txt"
PROFILE_MEM: "/tmp/nuclei-profile-${{ matrix.targets }}-targets"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/git@v1
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- run: |
for i in {1..${{ env.TARGET_COUNT }}}; do
echo "${{ env.TARGET }}/-/?_=${i}" >> "${{ env.TARGET_LIST }}";
done
- run: make build
- run: ./bin/nuclei -update-templates
- run: |
./bin/nuclei -silent -list="${TARGET_LIST}" \
-disable-update-check \
-no-mhe -no-httpx \
-code -dast -file -headless \
-enable-self-contained -enable-global-matchers \
-rate-limit=0 \
-concurrency=250 \
-headless-concurrency=100 \
-payload-concurrency=250 \
-bulk-size=250 \
-headless-bulk-size=100 \
-profile-mem="${PROFILE_MEM}" \
env:
DISABLE_STDOUT: "1"
- run: mv "${PROFILE_MEM}.cpu" "${PGO_FILE}"
- uses: actions/upload-artifact@v6
- name: Generate list
run: for i in {1..${{ matrix.targets }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
# NOTE(dwisiswant0): use `-no-mhe` flag to get better samples.
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}" -no-mhe
working-directory: cmd/nuclei/
- run: mv "${PROFILE_MEM}.cpu" ${PGO_FILE}
# NOTE(dwisiswant0): shall we prune $PGO_FILE git history?
# if we prune it, this won't be linear since it requires a force-push.
# if we don't, the git objects will just keep growing bigger.
#
# Ref:
# - https://go.dev/blog/pgo#:~:text=We%20recommend%20committing%20default.pgo%20files%20to%20your%20repository
# - https://gist.github.com/nottrobin/5758221
- uses: projectdiscovery/actions/commit@v1
with:
files: "${PGO_FILE}"
message: "build: update PGO profile :robot:"
- run: git push origin $GITHUB_REF
- uses: actions/upload-artifact@v4
with:
name: "pgo"
path: "${{ env.PGO_FILE }}"
overwrite: true
+3 -3
View File
@@ -3,7 +3,7 @@ name: 🐛 govulncheck
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch: {}
workflow_dispatch:
jobs:
govulncheck:
@@ -16,11 +16,11 @@ jobs:
env:
OUTPUT: "/tmp/results.sarif"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- run: go install golang.org/x/vuln/cmd/govulncheck@latest
- run: govulncheck -scan package -format sarif ./... > $OUTPUT
- uses: github/codeql-action/upload-sarif@v4
- uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: "${{ env.OUTPUT }}"
category: "govulncheck"
+5 -7
View File
@@ -1,8 +1,8 @@
name: 🔨 Performance Regression
on:
workflow_call: {}
workflow_dispatch: {}
workflow_call:
workflow_dispatch:
jobs:
perf-regression:
@@ -11,15 +11,13 @@ jobs:
env:
BENCH_OUT: "/tmp/bench.out"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- run: make build-test
- run: ./bin/nuclei.test -test.run - -test.bench=. -test.benchmem ./cmd/nuclei/ | tee $BENCH_OUT
env:
DISABLE_STDOUT: "1"
- uses: actions/cache/restore@v5
- uses: actions/cache/restore@v4
with:
path: ./cache
key: ${{ runner.os }}-benchmark
@@ -33,7 +31,7 @@ jobs:
github-token: ${{ secrets.GITHUB_TOKEN }}
comment-on-alert: true
summary-always: true
- uses: actions/cache/save@v5
- uses: actions/cache/save@v4
if: github.event_name == 'push'
with:
path: ./cache
+43
View File
@@ -0,0 +1,43 @@
name: 🔨 Performance Test
on:
schedule:
- cron: '0 0 * * 0' # Weekly
workflow_dispatch:
jobs:
perf-test:
strategy:
matrix:
count: [50, 100, 150]
runs-on: ubuntu-latest
if: github.repository == 'projectdiscovery/nuclei'
env:
LIST_FILE: "/tmp/targets-${{ matrix.count }}.txt"
PROFILE_MEM: "/tmp/nuclei-perf-test-${{ matrix.count }}"
steps:
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- run: make verify
- name: Generate list
run: for i in {1..${{ matrix.count }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
- run: go run . -l "${LIST_FILE}" -profile-mem="${PROFILE_MEM}"
env:
NUCLEI_ARGS: host-error-stats
working-directory: cmd/nuclei/
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
+4 -16
View File
@@ -4,28 +4,16 @@ on:
push:
tags:
- '*'
workflow_dispatch: {}
workflow_dispatch:
jobs:
pgo:
name: "Generate PGO"
uses: ./.github/workflows/generate-pgo.yaml
release:
name: "Release"
needs: ["pgo"]
jobs:
release:
runs-on: ubuntu-latest-16-cores
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
with:
fetch-depth: 0
- uses: actions/download-artifact@v7
with:
name: "pgo"
path: "cmd/nuclei/"
- uses: projectdiscovery/actions/setup/go@v1
with:
go-version: "stable"
- uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKER_USERNAME }}
+3 -2
View File
@@ -13,7 +13,7 @@ jobs:
issues: write
pull-requests: write
steps:
- uses: actions/stale@v10
- uses: actions/stale@v9
with:
days-before-stale: 90
days-before-close: 7
@@ -37,4 +37,5 @@ jobs:
it, please comment or feel free to reopen it.
close-issue-label: "Status: Abandoned"
close-pr-label: "Status: Abandoned"
exempt-issue-labels: "Type: Enhancement"
exempt-issue-labels: "Status: Abandoned"
exempt-pr-labels: "Status: Abandoned"
+47 -35
View File
@@ -19,12 +19,11 @@ concurrency:
jobs:
lint:
name: "Lint"
if: ${{ !endsWith(github.actor, '[bot]') }}
if: "${{ !endsWith(github.actor, '[bot]') }}"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/golangci-lint/v2@v1
tests:
@@ -36,19 +35,8 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: "${{ matrix.os }}"
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- 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
@@ -64,10 +52,8 @@ jobs:
needs: ["tests"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- name: "Simple"
run: go run .
working-directory: examples/simple/
@@ -88,11 +74,9 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- uses: projectdiscovery/actions/setup/python@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- run: bash run.sh "${{ matrix.os }}"
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
@@ -109,11 +93,9 @@ jobs:
os: [ubuntu-latest, windows-latest, macOS-latest]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/nuclei@v1
- uses: projectdiscovery/actions/setup/python@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- run: bash run.sh
env:
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
@@ -124,9 +106,8 @@ jobs:
needs: ["tests"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
- run: make template-validate
codeql:
@@ -138,30 +119,61 @@ jobs:
contents: read
security-events: write
steps:
- uses: actions/checkout@v6
- uses: github/codeql-action/init@v4
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with:
languages: 'go'
- uses: github/codeql-action/autobuild@v4
- uses: github/codeql-action/analyze@v4
- uses: github/codeql-action/autobuild@v3
- uses: github/codeql-action/analyze@v3
release:
name: "Release test"
needs: ["tests"]
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6
- uses: actions/checkout@v4
- uses: projectdiscovery/actions/setup/go@v1
with:
go-version: "stable"
- uses: projectdiscovery/actions/goreleaser@v1
flamegraph:
name: "Flamegraph"
needs: ["tests"]
uses: ./.github/workflows/flamegraph.yaml
env:
PROFILE_MEM: "/tmp/nuclei"
TARGET_URL: "http://scanme.sh/a/?b=c"
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: make build
- name: "Setup environment (push)"
if: ${{ github.event_name == 'push' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-${GITHUB_REF_NAME}-${GITHUB_SHA}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei-${GITHUB_REF_NAME} (${GITHUB_SHA})" >> $GITHUB_ENV
- name: "Setup environment (pull_request)"
if: ${{ github.event_name == 'pull_request' }}
run: |
echo "PROFILE_MEM=${PROFILE_MEM}-pr-${{ github.event.number }}" >> $GITHUB_ENV
echo "FLAMEGRAPH_NAME=nuclei (PR #${{ github.event.number }})" >> $GITHUB_ENV
- run: ./bin/nuclei -silent -update-templates
- run: ./bin/nuclei -silent -u "${TARGET_URL}" -profile-mem="${PROFILE_MEM}"
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-cpu
with:
profile: "${{ env.PROFILE_MEM }}.cpu"
name: "${{ env.FLAMEGRAPH_NAME }} CPU profiles"
continue-on-error: true
- uses: projectdiscovery/actions/flamegraph@v1
id: flamegraph-mem
with:
profile: "${{ env.PROFILE_MEM }}.mem"
name: "${{ env.FLAMEGRAPH_NAME }} memory profiles"
continue-on-error: true
- if: ${{ steps.flamegraph-mem.outputs.message == '' }}
run: |
echo "::notice::CPU flamegraph: ${{ steps.flamegraph-cpu.outputs.url }}"
echo "::notice::Memory (heap) flamegraph: ${{ steps.flamegraph-mem.outputs.url }}"
perf-regression:
name: "Performance regression"
needs: ["tests"]
uses: ./.github/workflows/perf-regression.yaml
+1 -3
View File
@@ -8,7 +8,7 @@
.gitignore
.idea
.vscode
.claude/settings.local.json
# Binaries
/bin/*
/bindgen
@@ -28,8 +28,6 @@
/scrapefunc
/scrapefuncs
/tsgen
/integration_tests/integration-test
/integration_tests/nuclei
# Templates
/*.yaml
+2 -3
View File
@@ -11,9 +11,8 @@ builds:
id: nuclei-cli
env:
- CGO_ENABLED=0
- GOEXPERIMENT=greenteagc
goos: [windows, linux, darwin]
goarch: [amd64, '386', arm, arm64]
goos: [windows,linux,darwin]
goarch: [amd64,'386',arm,arm64]
ignore:
- goos: darwin
goarch: '386'
-83
View File
@@ -1,83 +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 integration tests (builds and runs test suite)
- `make functional` - Run functional tests
- `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 `integration_tests/` and can be run via `make 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
- **integration_tests/** - Integration test suite with protocol-specific tests
- **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 -3
View File
@@ -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 |
+1 -1
View File
@@ -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.
+7 -7
View File
@@ -15,8 +15,8 @@ ifneq ($(shell go env GOOS),darwin)
endif
.PHONY: all build build-stats clean devtools-all devtools-bindgen devtools-scrapefuncs
.PHONY: devtools-tsgen docs docgen dsl-docs functional fuzzplayground go-build lint lint-strict syntax-docs
.PHONY: integration jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test test-with-lint
.PHONY: devtools-tsgen docs docgen dsl-docs functional fuzzplayground go-build syntax-docs
.PHONY: integration jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test
.PHONY: tidy ts verify download vet template-validate
all: build
@@ -75,7 +75,7 @@ syntax-docs: docgen
syntax-docs:
./bin/docgen SYNTAX-REFERENCE.md nuclei-jsonschema.json
test: GOFLAGS = -race -v -timeout 30m -count 1
test: GOFLAGS = -race -v
test:
$(GOTEST) $(GOFLAGS) ./...
@@ -147,13 +147,13 @@ template-validate: build
template-validate:
./bin/nuclei -ut
./bin/nuclei -validate \
-et .github/ \
-et helpers/payloads/ \
-et http/technologies \
-t dns \
-t ssl \
-t network \
-t http/exposures \
-ept code
./bin/nuclei -validate \
-w workflows \
-et .github/ \
-et helpers/payloads/ \
-et http/technologies \
-ept code
+8 -8
View File
@@ -14,6 +14,8 @@
<p align="center">
<img src="https://img.shields.io/badge/go-1.22-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white">
&nbsp;&nbsp;
<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>
&nbsp;&nbsp;
<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>
@@ -33,7 +35,7 @@
- Reduce false positives by simulating real-world steps to verify a vulnerability.
- Ultra-fast parallel scan processing and request clustering.
- Integrate into CI/CD pipelines for vulnerability detection and regression testing.
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS, JavaScript, Code and more.
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS JavaScript, Code and more.
- Integrate with Jira, Splunk, GitHub, Elastic, GitLab.
<br>
@@ -83,7 +85,7 @@ _For security teams and enterprises, we provide a cloud-hosted service built on
- 50x faster scans
- Large scale scanning with high accuracy
- Integrations with cloud services (AWS, GCP, Azure, Cloudflare, Fastly, Terraform, Kubernetes)
- Integrations with cloud services (AWS, GCP, Azure, CloudFlare, Fastly, Terraform, Kubernetes)
- Jira, Slack, Linear, APIs and Webhooks
- Executive and compliance reporting
- Plus: Real-time scanning, SAML SSO, SOC 2 compliant platform (with EU and US hosting options), shared team workspaces, and more
@@ -97,7 +99,7 @@ _For security teams and enterprises, we provide a cloud-hosted service built on
## Documentation
Browse the full Nuclei [**`documentation here`**](https://docs.projectdiscovery.io/tools/nuclei/running). If you’re new to Nuclei, check out our [**`foundational YouTube series`**](https://www.youtube.com/playlist?list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl).
Browse the full Nuclei [**`documentation here`**](https://docs.projectdiscovery.io/tools/nuclei/running). If you’re new to Nuclei, check out our [**`foundational Youtube series`**](https://www.youtube.com/playlist?list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl).
<div align="center">
@@ -109,7 +111,7 @@ Browse the full Nuclei [**`documentation here`**](https://docs.projectdiscovery.
### Installation
`nuclei` requires **go >= 1.24.2** to install successfully. Run the following command to get the repo:
`nuclei` requires **go1.23** to install successfully. Run the following command to get the repo:
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -140,7 +142,7 @@ TARGET:
-u, -target string[] target URLs/hosts to scan
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
-eh, -exclude-hosts string[] hosts to exclude to scan from the input list (ip, cidr, hostname)
-resume string resume scan from and save to specified file (clustering will be disabled)
-resume string resume scan using resume.cfg (clustering will be disabled)
-sa, -scan-all-ips scan all the IP's associated with dns record
-iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4)
@@ -161,7 +163,7 @@ TEMPLATES:
-validate validate the passed templates to nuclei
-nss, -no-strict-syntax disable strict syntax check on templates
-td, -template-display displays the templates content
-tl list all templates matching current filters
-tl list all available templates
-tgl list all available tags
-sign signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable
-code enable loading code protocol-based templates
@@ -284,7 +286,6 @@ RATE-LIMIT:
-jsc, -js-concurrency int maximum number of javascript runtimes to be executed in parallel (default 120)
-pc, -payload-concurrency int max payload concurrency for each template (default 25)
-prc, -probe-concurrency int http probe concurrency with httpx (default 50)
-tlc, -template-loading-concurrency int maximum number of concurrent template loading operations (default 50)
OPTIMIZATIONS:
-timeout int time to wait in seconds before timeout (default 10)
@@ -355,7 +356,6 @@ CLOUD:
AUTHENTICATION:
-sf, -secret-file string[] path to config file containing secrets for nuclei authenticated scan
-ps, -prefetch-secrets prefetch secrets from the secrets file
# NOTE: Headers in secrets files preserve exact casing (useful for case-sensitive APIs)
EXAMPLES:
+3 -4
View File
@@ -59,7 +59,7 @@ Nuclei使用零误报的定制模板向目标发送请求,同时可以对主
# 安装Nuclei
Nuclei需要 **go1.24.2** 才能安装成功。执行下列命令安装最新版本的Nuclei
Nuclei需要 **go1.22** 才能安装成功。执行下列命令安装最新版本的Nuclei
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -119,7 +119,7 @@ Nuclei是一款注重于可配置性、可扩展性和易用性的基于模板
目标:
-u, -target string[] 指定扫描的目标URL/主机(多个目标则指定多个-u参数)
-l, -list string 指定包含要扫描的目标URL/主机列表的文件路径(一行一个)
-resume string 从指定文件恢复扫描并保存到指定文件(将禁用请求聚类)
-resume string 使用指定的resume.cfg文件恢复扫描(将禁用请求聚类)
-sa, -scan-all-ips 扫描由目标解析出来的所有IP(针对域名对应多个IP的情况)
-iv, -ip-version string[] 要扫描的主机名的IP版本(4,6)-(默认为4)
@@ -238,7 +238,6 @@ UNCOVER引擎:
-c, -concurrency int 并行执行的最大模板数量(默认:25)
-hbs, -headless-bulk-size int 每个模板并行运行的无头主机最大数量(默认:10)
-headc, -headless-concurrency int 并行指定无头主机最大数量(默认:10)
-tlc, -template-loading-concurrency int 最大并发模板加载操作数(默认:50)
优化:
@@ -371,7 +370,7 @@ Nuclei允许您定制自己的测试方法,可以轻松的运行您的程序
- 可以在几分钟处理上千台主机
- 使用YAML语法定制自动化测试
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](https://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](https://chaos.projectdiscovery.io)。
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](http://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](http://chaos.projectdiscovery.io)。
</td>
</tr>
+2 -3
View File
@@ -56,7 +56,7 @@ Tenemos un [repositorio dedicado](https://github.com/projectdiscovery/nuclei-tem
# Instalación de Nuclei
Nuclei requiere **go1.24.2** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
Nuclei requiere **go1.22** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -118,7 +118,7 @@ TARGET:
-u, -target string[] URLs/hosts a escanear
-l, -list string ruta al archivo que contiene la lista de URLs/hosts a escanear (uno por línea)
-eh, -exclude-hosts string[] hosts a excluir para escanear de la lista de entrada (ip, cidr, hostname)
-resume string reanudar el escaneo desde y guardar en el archivo especificado (la clusterización quedará inhabilitada)
-resume string reanudar el escaneo usando resume.cfg (la clusterización quedará inhabilitada)
-sa, -scan-all-ips escanear todas las IP asociadas al registro dns
-iv, -ip-version string[] versión IP a escanear del nombre de host (4,6) - (por defecto 4)
@@ -244,7 +244,6 @@ RATE-LIMIT:
-headc, -headless-concurrency int número máximo de plantillas headless a ejecutar en paralelo (por defecto 10)
-jsc, -js-concurrency int número máximo de entornos de ejecución de JavaScript a ejecutar en paralelo (por defecto 120)
-pc, -payload-concurrency int concurrencia máxima de carga útil para cada plantilla (por defecto 25)
-tlc, -template-loading-concurrency int número máximo de operaciones de carga de plantillas concurrentes (por defecto 50)
OPTIMIZATIONS:
-timeout int tiempo de espera en segundos (por defecto 10)
+3 -4
View File
@@ -54,7 +54,7 @@ Kami memiliki [repositori khusus](https://github.com/projectdiscovery/nuclei-tem
# Instalasi Nuclei
Nuclei membutuhkan **go1.24.2** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
Nuclei membutuhkan **go1.22** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -98,7 +98,7 @@ Flags:
TARGET:
-u, -target string[] target URLs/hosts to scan
-l, -list string path to file containing a list of target URLs/hosts to scan (one per line)
-resume string resume scan from and save to specified file (clustering will be disabled)
-resume string resume scan using resume.cfg (clustering will be disabled)
-sa, -scan-all-ips scan all the IP's associated with dns record
-iv, -ip-version string[] IP version to scan of hostname (4,6) - (default 4)
@@ -211,7 +211,6 @@ RATE-LIMIT:
-c, -concurrency int maximum number of templates to be executed in parallel (default 25)
-hbs, -headless-bulk-size int maximum number of headless hosts to be analyzed in parallel per template (default 10)
-headc, -headless-concurrency int maximum number of headless templates to be executed in parallel (default 10)
-tlc, -template-loading-concurrency int maximum number of concurrent template loading operations (default 50)
OPTIMIZATIONS:
-timeout int time to wait in seconds before timeout (default 10)
@@ -342,7 +341,7 @@ Nuclei memungkinkan Anda untuk menyesuaikan pendekatan pengujian Anda dengan ran
- Dapat memproses ribuan host hanya dalam beberapa menit.
- Mudah mengotomatiskan pendekatan pengujian khusus Anda dengan sintaks DSL berbasis YAML sederhana kami.
Silakan periksa proyek sumber terbuka kami yang lain yang mungkin cocok dengan alur kerja celah berhadiah Anda: [github.com/projectdiscovery](https://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](https://chaos.projectdiscovery.io).
Silakan periksa proyek sumber terbuka kami yang lain yang mungkin cocok dengan alur kerja celah berhadiah Anda: [github.com/projectdiscovery](http://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](http://chaos.projectdiscovery.io).
</td>
</tr>
+2 -2
View File
@@ -53,7 +53,7 @@ Nucleiは、テンプレートに基づいてターゲット間でリクエス
# Nucleiのインストール
Nucleiを正常にインストールするには、**go1.24.2**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
Nucleiを正常にインストールするには、**go1.22**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -113,7 +113,7 @@ Nucleiは、広範な設定可能性、大規模な拡張性、および使い
ターゲット:
-u, -target string[] スキャンする対象のURL/ホスト
-l, -list string スキャンする対象のURL/ホストのリストが含まれているファイルへのパス(1行に1つ)
-resume string 指定されたファイルからスキャンを再開し、指定されたファイルに保存(クラスタリングは無効になります)
-resume string resume.cfgを使用してスキャンを再開(クラスタリングは無効になります)
-sa, -scan-all-ips DNSレコードに関連付けられているすべてのIPをスキャン
-iv, -ip-version string[] ホスト名のスキャンするIPバージョン(4,6)-(デフォルトは4)
+3 -4
View File
@@ -52,7 +52,7 @@ Nuclei는 템플릿을 기반으로 대상 간에 요청을 보내기 위해 사
# 설치
Nuclei를 성공적으로 설치하기 위해서 **go1.24.2**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
Nuclei를 성공적으로 설치하기 위해서 **go1.22**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -96,7 +96,7 @@ Nuclei는 빠르고, 템플릿 기반의 취약점 스캐너로
TARGET:
-u, -target string[] 스캔할 대상 URL/호스트
-l, -list string 스캔할 대상 URL/호스트 목록이 있는 파일 경로 (한 줄에 하나씩)
-resume string 지정된 파일에서 스캔을 재개하고 지정된 파일에 저장 (클러스터링은 비활성화됨)
-resume string resume.cfg를 사용하여 스캔 재개 (클러스터링은 비활성화됨)
-sa, -scan-all-ips dns 레코드와 관련된 모든 IP 스캔
-iv, -ip-version string[] 스캔할 호스트의 IP 버전 (4,6) - (기본값 4)
@@ -209,7 +209,6 @@ RATE-LIMIT:
-c, -concurrency int 병렬로 실행할 최대 템플릿 수 (기본값 25)
-hbs, -headless-bulk-size int 템플릿당 병렬로 분석할 최대 headless 호스트 수 (기본값 10)
-headc, -headless-concurrency int 병렬로 실행할 최대 headless 템플릿 수 (기본값 10)
-tlc, -template-loading-concurrency int 최대 동시 템플릿 로딩 작업 수 (기본값 50)
OPTIMIZATIONS:
-timeout int 타임아웃 전에 기다릴 초 수 (기본값 10)
@@ -342,7 +341,7 @@ Nuclei를 사용하면 자체 검사 모음으로 테스트 접근 방식을 사
- 몇 분 안에 수천 개의 호스트를 처리할 수 있음.
- 간단한 YAML DSL로 사용자 지정 테스트 접근 방식을 쉽게 자동화할 수 있음.
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](https://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](https://chaos.projectdiscovery.io).
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](http://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](http://chaos.projectdiscovery.io).
</td>
</tr>
+2 -3
View File
@@ -56,7 +56,7 @@ Temos um [repositório dedicado](https://github.com/projectdiscovery/nuclei-temp
# Instalação do Nuclei
O Nuclei requer **go1.24.2** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
O Nuclei requer **go1.22** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
```sh
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
@@ -118,7 +118,7 @@ TARGET:
-u, -target string[] URLs/hosts a serem escaneados
-l, -list string caminho do arquivo contendo a lista de URLs/hosts a serem escaneados (um por linha)
-eh, -exclude-hosts string[] hosts a serem excluídos do escaneamento na lista de entrada (ip, cidr, hostname)
-resume string retomar o escaneamento a partir de e salvar no arquivo especificado (a clusterização será desabilitada)
-resume string retomar o escaneamento usando resume.cfg (a clusterização será desabilitada)
-sa, -scan-all-ips escanear todos os IPs associados ao registro DNS
-iv, -ip-version string[] versão de IP a escanear do nome do host (4,6) - (padrão 4)
@@ -244,7 +244,6 @@ RATE-LIMIT:
-headc, -headless-concurrency int número máximo de templates headless a serem executados em paralelo (padrão 10)
-jsc, -js-concurrency int número máximo de ambientes de execução de JavaScript a serem executados em paralelo (padrão 120)
-pc, -payload-concurrency int concorrência máxima de payload para cada template (padrão 25)
-tlc, -template-loading-concurrency int número máximo de operações de carregamento de templates concorrentes (padrão 50)
OPTIMIZATIONS:
-timeout int tempo limite em segundos (padrão 10)
+2 -2
View File
@@ -3218,7 +3218,7 @@ 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
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>
@@ -4512,7 +4512,7 @@ permutations and combinations for all payloads.
</div>
<div class="dt">
Payload concurrency i.e threads for sending requests.
Payload concurreny i.e threads for sending requests.
+15 -15
View File
@@ -36,24 +36,24 @@
{{binary}} -tags cve,dos,fuzz
{{binary}} -tags cve -include-tags dos,fuzz
{{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}} -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,ssrf
{{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/2021/
{{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 -include-templates http/cves/2017/CVE-2017-7269.yaml
{{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}} -tags cve -author geeknik,pdteam -tc severity=='high'
{{binary}} -tc contains(authors,'pdteam')
{{binary}} -t http/cves/ -t http/exposures/ -tc contains(tags,'cve') -exclude-templates http/cves/2020/CVE-2020-9757.yaml
{{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
@@ -65,7 +65,7 @@
{{binary}} -w workflows
{{binary}} -w workflows -author geeknik,pdteam
{{binary}} -w workflows -severity high,critical
{{binary}} -w workflows -author geeknik,pdteam -severity high,critical
{{binary}} -w workflows -author geeknik,pdteam -severity high,critical
# Input Types
# http protocol
-3
View File
@@ -24,9 +24,6 @@ var codeTestCases = []TestCaseInfo{
{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() }},
{Path: "protocols/code/py-virtual.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return !osutils.IsLinux() || isCodeDisabled() }},
{Path: "protocols/code/pwsh-echo.yaml", TestCase: &codeSnippet{}, DisableOn: func() bool { return isCodeDisabled() }},
}
const (
-104
View File
@@ -1,104 +0,0 @@
package main
import (
"context"
"fmt"
"log"
"time"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/mongo"
"github.com/testcontainers/testcontainers-go"
mongocontainer "github.com/testcontainers/testcontainers-go/modules/mongodb"
osutil "github.com/projectdiscovery/utils/os"
mongoclient "go.mongodb.org/mongo-driver/mongo"
mongooptions "go.mongodb.org/mongo-driver/mongo/options"
)
const (
dbName = "test"
dbImage = "mongo:8"
)
var exportersTestCases = []TestCaseInfo{
{Path: "exporters/mongo", TestCase: &mongoExporter{}, DisableOn: func() bool {
return osutil.IsWindows() || osutil.IsOSX()
}},
}
type mongoExporter struct{}
func (m *mongoExporter) Execute(filepath string) error {
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
defer cancel()
// Start a MongoDB container
mongodbContainer, err := mongocontainer.Run(ctx, dbImage)
defer func() {
if err := testcontainers.TerminateContainer(mongodbContainer); err != nil {
log.Printf("failed to terminate container: %s", err)
}
}()
if err != nil {
return fmt.Errorf("failed to start container: %w", err)
}
connString, err := mongodbContainer.ConnectionString(ctx)
if err != nil {
return fmt.Errorf("failed to get connection string for MongoDB container: %s", err)
}
connString = connString + dbName
// Create a MongoDB exporter and write a test result to the database
opts := mongo.Options{
ConnectionString: connString,
CollectionName: "test",
BatchSize: 1, // Ensure we write the result immediately
}
exporter, err := mongo.New(&opts)
if err != nil {
return fmt.Errorf("failed to create MongoDB exporter: %s", err)
}
defer func() {
if err := exporter.Close(); err != nil {
fmt.Printf("failed to close exporter: %s\n", err)
}
}()
res := &output.ResultEvent{
Request: "test request",
Response: "test response",
}
err = exporter.Export(res)
if err != nil {
return fmt.Errorf("failed to export result event to MongoDB: %s", err)
}
// Verify that the result was written to the database
clientOptions := mongooptions.Client().ApplyURI(connString)
client, err := mongoclient.Connect(ctx, clientOptions)
if err != nil {
return fmt.Errorf("error creating MongoDB client: %s", err)
}
defer func() {
if err := client.Disconnect(ctx); err != nil {
fmt.Printf("failed to disconnect from MongoDB: %s\n", err)
}
}()
collection := client.Database(dbName).Collection(opts.CollectionName)
var actualRes output.ResultEvent
err = collection.FindOne(ctx, map[string]interface{}{"request": res.Request}).Decode(&actualRes)
if err != nil {
return fmt.Errorf("failed to find document in MongoDB: %s", err)
}
if actualRes.Request != res.Request || actualRes.Response != res.Response {
return fmt.Errorf("exported result does not match expected result: got %v, want %v", actualRes, res)
}
return nil
}
+9 -76
View File
@@ -19,7 +19,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
logutil "github.com/projectdiscovery/utils/log"
sliceutil "github.com/projectdiscovery/utils/slice"
stringsutil "github.com/projectdiscovery/utils/strings"
@@ -62,11 +62,9 @@ var httpTestcases = []TestCaseInfo{
{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-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{}},
@@ -79,7 +77,6 @@ var httpTestcases = []TestCaseInfo{
{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{}},
@@ -199,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
@@ -631,10 +628,10 @@ func (h *httpRawWithParams) Execute(filePath string) error {
// we intentionally use params["test"] instead of params.Get("test") to test the case where
// there are multiple parameters with the same name
if !reflect.DeepEqual(params["key1"], []string{"value1"}) {
errx = errkit.Append(errx, errkit.New("key1 not found in params", "expected", []string{"value1"}, "got", params["key1"]))
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value1"}, params["key1"])
}
if !reflect.DeepEqual(params["key2"], []string{"value2"}) {
errx = errkit.Append(errx, errkit.New("key2 not found in params", "expected", []string{"value2"}, "got", params["key2"]))
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value2"}, params["key2"])
}
_, _ = fmt.Fprintf(w, "Test is test raw-params-matcher text")
})
@@ -974,10 +971,10 @@ 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"))
})
@@ -1030,10 +1027,10 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
// 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()
@@ -1049,7 +1046,7 @@ 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
}
@@ -1156,26 +1153,6 @@ func (h *httpRaceMultiple) Execute(filePath string) error {
return expectResultsCount(results, 5)
}
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
@@ -1243,30 +1220,6 @@ func (h *httpVariables) Execute(filePath string) error {
return expectResultsCount(results, 0)
}
type httpVariablesThreadsPrevious struct{}
// Execute tests that variables can reference data extracted from previous requests
// when using threads mode (parallel execution).
func (h *httpVariablesThreadsPrevious) Execute(filePath string) error {
router := httprouter.New()
router.GET("/login", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
_, _ = fmt.Fprint(w, "token=secret123")
})
router.GET("/api", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
// Echo back the Authorization header so we can match on it
_, _ = fmt.Fprint(w, r.Header.Get("Authorization"))
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type httpVariableDSLFunction struct{}
// Execute executes a test case and returns an error if occurred
@@ -1513,26 +1466,6 @@ func (h *ConstantWithCliVar) Execute(filePath string) error {
return expectResultsCount(got, 1)
}
type constantsWithThreads struct{}
// Execute tests that constants are properly resolved when using threads mode.
func (h *constantsWithThreads) Execute(filePath string) error {
router := httprouter.New()
router.GET("/api/:version", func(w http.ResponseWriter, r *http.Request, p httprouter.Params) {
// Echo back the API key header and version so we can match on them
_, _ = fmt.Fprintf(w, "%s %s", r.Header.Get("X-API-Key"), p.ByName("version"))
})
ts := httptest.NewServer(router)
defer ts.Close()
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type matcherStatusTest struct{}
// Execute executes a test case and returns an error if occurred
+3 -28
View File
@@ -6,7 +6,6 @@ import (
"os"
"regexp"
"runtime"
"slices"
"strings"
"github.com/kitabisa/go-ci"
@@ -25,7 +24,7 @@ type TestCaseInfo struct {
}
var (
debug = isDebugMode()
debug = os.Getenv("DEBUG") == "true"
customTests = os.Getenv("TESTS")
protocol = os.Getenv("PROTO")
@@ -47,7 +46,6 @@ var (
"library": libraryTestcases,
"templatesPath": templatesPathTestCases,
"templatesDir": templatesDirTestCases,
"env_vars": templatesDirEnvTestCases,
"file": fileTestcases,
"offlineHttp": offlineHttpTestcases,
"customConfigDir": customConfigDirTestCases,
@@ -59,9 +57,7 @@ var (
"flow": flowTestcases,
"javascript": jsTestcases,
"matcher-status": matcherStatusTestcases,
"exporters": exportersTestCases,
}
// flakyTests are run with a retry count of 3
flakyTests = map[string]bool{
"protocols/http/self-contained-file-input.yaml": true,
@@ -92,12 +88,11 @@ func main() {
}
// start fuzz playground server
defer fuzzplayground.Cleanup()
server := fuzzplayground.GetPlaygroundServer()
defer func() {
fuzzplayground.Cleanup()
_ = server.Close()
}()
go func() {
if err := server.Start("localhost:8082"); err != nil {
if !strings.Contains(err.Error(), "Server closed") {
@@ -107,6 +102,7 @@ func main() {
}()
customTestsList := normalizeSplit(customTests)
failedTestTemplatePaths := runTests(customTestsList)
if len(failedTestTemplatePaths) > 0 {
@@ -133,27 +129,6 @@ func main() {
}
}
// isDebugMode checks if debug mode is enabled via any of the supported debug
// environment variables.
func isDebugMode() bool {
debugEnvVars := []string{
"DEBUG",
"ACTIONS_RUNNER_DEBUG", // GitHub Actions runner debug
// Add more debug environment variables here as needed
}
truthyValues := []string{"true", "1", "yes", "on", "enabled"}
for _, envVar := range debugEnvVars {
envValue := strings.ToLower(strings.TrimSpace(os.Getenv(envVar)))
if slices.Contains(truthyValues, envValue) {
return true
}
}
return false
}
// 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) {
+4 -238
View File
@@ -15,23 +15,13 @@ var jsTestcases = []TestCaseInfo{
{Path: "protocols/javascript/ssh-server-fingerprint.yaml", TestCase: &javascriptSSHServerFingerprint{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/net-multi-step.yaml", TestCase: &networkMultiStep{}},
{Path: "protocols/javascript/net-https.yaml", TestCase: &javascriptNetHttps{}},
{Path: "protocols/javascript/oracle-auth-test.yaml", TestCase: &javascriptOracleAuthTest{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/vnc-pass-brute.yaml", TestCase: &javascriptVncPassBrute{}},
{Path: "protocols/javascript/postgres-pass-brute.yaml", TestCase: &javascriptPostgresPassBrute{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/mysql-connect.yaml", TestCase: &javascriptMySQLConnect{}, DisableOn: func() bool { return osutils.IsWindows() || osutils.IsOSX() }},
{Path: "protocols/javascript/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
{Path: "protocols/javascript/no-port-args.yaml", TestCase: &javascriptNoPortArgs{}},
}
var (
redisResource *dockertest.Resource
sshResource *dockertest.Resource
oracleResource *dockertest.Resource
vncResource *dockertest.Resource
postgresResource *dockertest.Resource
mysqlResource *dockertest.Resource
pool *dockertest.Pool
defaultRetry = 3
redisResource *dockertest.Resource
sshResource *dockertest.Resource
pool *dockertest.Pool
defaultRetry = 3
)
type javascriptNetHttps struct{}
@@ -108,156 +98,6 @@ func (j *javascriptSSHServerFingerprint) Execute(filePath string) error {
return multierr.Combine(errs...)
}
type javascriptOracleAuthTest struct{}
func (j *javascriptOracleAuthTest) Execute(filePath string) error {
if oracleResource == nil || pool == nil {
// skip test as oracle is not running
return nil
}
tempPort := oracleResource.GetPort("1521/tcp")
finalURL := "localhost:" + tempPort
defer purge(oracleResource)
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 javascriptVncPassBrute struct{}
func (j *javascriptVncPassBrute) Execute(filePath string) error {
if vncResource == nil || pool == nil {
// skip test as vnc is not running
return nil
}
tempPort := vncResource.GetPort("5900/tcp")
finalURL := "localhost:" + tempPort
defer purge(vncResource)
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 javascriptPostgresPassBrute struct{}
func (j *javascriptPostgresPassBrute) Execute(filePath string) error {
if postgresResource == nil || pool == nil {
// skip test as postgres is not running
return nil
}
tempPort := postgresResource.GetPort("5432/tcp")
finalURL := "localhost:" + tempPort
defer purge(postgresResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let postgres server start
time.Sleep(3 * time.Second)
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
return nil
})
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err == nil {
return nil
} else {
errs = append(errs, err)
}
}
return multierr.Combine(errs...)
}
type javascriptMySQLConnect struct{}
func (j *javascriptMySQLConnect) Execute(filePath string) error {
if mysqlResource == nil || pool == nil {
// skip test as mysql is not running
return nil
}
tempPort := mysqlResource.GetPort("3306/tcp")
finalURL := "localhost:" + tempPort
defer purge(mysqlResource)
errs := []error{}
for i := 0; i < defaultRetry; i++ {
results := []string{}
var err error
_ = pool.Retry(func() error {
//let mysql server start
time.Sleep(5 * time.Second)
results, err = testutils.RunNucleiTemplateAndGetResults(filePath, finalURL, debug)
return nil
})
if err != nil {
return err
}
if err := expectResultsCount(results, 1); err == nil {
return nil
} else {
errs = append(errs, err)
}
}
return multierr.Combine(errs...)
}
type javascriptMultiPortsSSH struct{}
func (j *javascriptMultiPortsSSH) Execute(filePath string) error {
// use scanme.sh as target to ensure we match on the 2nd default port 22
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "scanme.sh", debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
type javascriptNoPortArgs struct{}
func (j *javascriptNoPortArgs) Execute(filePath string) error {
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, "yo.dawg", debug)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
// purge any given resource if it is not nil
func purge(resource *dockertest.Resource) {
if resource != nil && pool != nil {
@@ -323,78 +163,4 @@ func init() {
if err := sshResource.Expire(30); err != nil {
log.Printf("Could not expire resource: %s", err)
}
// setup a temporary oracle instance
oracleResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "gvenzl/oracle-xe",
Tag: "latest",
Env: []string{
"ORACLE_PASSWORD=mysecret",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start Oracle resource: %s", err)
return
}
// by default expire after 30 sec
if err := oracleResource.Expire(30); err != nil {
log.Printf("Could not expire Oracle resource: %s", err)
}
// setup a temporary vnc server
vncResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "dorowu/ubuntu-desktop-lxde-vnc",
Tag: "latest",
Env: []string{
"VNC_PASSWORD=mysecret",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start resource: %s", err)
return
}
// by default expire after 30 sec
if err := vncResource.Expire(30); err != nil {
log.Printf("Could not expire resource: %s", err)
}
// setup a temporary postgres instance
postgresResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "postgres",
Tag: "latest",
Env: []string{
"POSTGRES_PASSWORD=postgres",
"POSTGRES_USER=postgres",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start postgres resource: %s", err)
return
}
// by default expire after 30 sec
if err := postgresResource.Expire(30); err != nil {
log.Printf("Could not expire postgres resource: %s", err)
}
// setup a temporary mysql instance
mysqlResource, err = pool.RunWithOptions(&dockertest.RunOptions{
Repository: "mysql",
Tag: "latest",
Env: []string{
"MYSQL_ROOT_PASSWORD=secret",
},
Platform: "linux/amd64",
})
if err != nil {
log.Printf("Could not start mysql resource: %s", err)
return
}
// by default expire after 30 sec
if err := mysqlResource.Expire(30); err != nil {
log.Printf("Could not expire mysql resource: %s", err)
}
}
-2
View File
@@ -15,7 +15,6 @@ import (
"github.com/logrusorgru/aurora"
"github.com/pkg/errors"
"github.com/projectdiscovery/goflags"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
@@ -71,7 +70,6 @@ func executeNucleiAsLibrary(templatePath, templateURL string) ([]string, error)
defaultOpts := types.DefaultOptions()
defaultOpts.ExecutionId = "test"
defaultOpts.Logger = gologger.DefaultLogger
mockProgress := &testutils.MockProgressClient{}
reportingClient, err := reporting.New(&reporting.Options{ExecutionId: defaultOpts.ExecutionId}, "", false)
+2 -2
View File
@@ -10,7 +10,7 @@ import (
"github.com/julienschmidt/httprouter"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
permissionutil "github.com/projectdiscovery/utils/permission"
)
@@ -223,7 +223,7 @@ 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 errkit.Wrap(err, "failed to load template with id")
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
}
return expectResultsCount(results, 1)
}
+4 -4
View File
@@ -4,7 +4,7 @@ import (
"fmt"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
)
var profileLoaderTestcases = []TestCaseInfo{
@@ -18,7 +18,7 @@ type profileLoaderByRelFile struct{}
func (h *profileLoaderByRelFile) Execute(testName string) error {
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-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))
@@ -31,7 +31,7 @@ type profileLoaderById struct{}
func (h *profileLoaderById) Execute(testName string) error {
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-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))
@@ -45,7 +45,7 @@ type customProfileLoader struct{}
func (h *customProfileLoader) Execute(filepath string) error {
results, err := testutils.RunNucleiWithArgsAndGetResults(debug, "-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))
+2 -2
View File
@@ -4,7 +4,7 @@ import (
"os"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
)
var templatesDirTestCases = []TestCaseInfo{
@@ -17,7 +17,7 @@ type templateDirWithTargetTest struct{}
func (h *templateDirWithTargetTest) Execute(filePath string) error {
tempdir, err := os.MkdirTemp("", "nuclei-update-dir-*")
if err != nil {
return errkit.Wrap(err, "failed to create temp dir")
return errorutil.NewWithErr(err).Msgf("failed to create temp dir")
}
defer func() {
_ = os.RemoveAll(tempdir)
-217
View File
@@ -1,217 +0,0 @@
package main
import (
"os"
"path/filepath"
osutils "github.com/projectdiscovery/utils/os"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
"github.com/projectdiscovery/utils/errkit"
)
// isNotLinux returns true if not running on Linux (used to skip tests on non-Linux OS)
var isNotLinux = func() bool { return !osutils.IsLinux() }
var templatesDirEnvTestCases = []TestCaseInfo{
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templatesDirEnvBasicTest{}, DisableOn: isNotLinux},
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templatesDirEnvAbsolutePathTest{}, DisableOn: isNotLinux},
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templatesDirEnvRelativePathTest{}, DisableOn: isNotLinux},
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templatesDirEnvPrecedenceTest{}, DisableOn: isNotLinux},
{Path: "protocols/dns/cname-fingerprint.yaml", TestCase: &templatesDirEnvCustomTemplatesTest{}, DisableOn: isNotLinux},
}
// copyTemplateToDir copies a template file to a destination directory, preserving the directory structure
func copyTemplateToDir(templatePath, destDir string) error {
// Read the template file
templateData, err := os.ReadFile(templatePath)
if err != nil {
return errkit.Wrap(err, "failed to read template file")
}
// Create the destination path preserving directory structure
destPath := filepath.Join(destDir, templatePath)
destDirPath := filepath.Dir(destPath)
// Create the destination directory if it doesn't exist
if err := os.MkdirAll(destDirPath, 0755); err != nil {
return errkit.Wrap(err, "failed to create destination directory")
}
// Write the template file
if err := os.WriteFile(destPath, templateData, 0644); err != nil {
return errkit.Wrap(err, "failed to write template file")
}
return nil
}
// templatesDirEnvBasicTest tests basic functionality of NUCLEI_TEMPLATES_DIR
type templatesDirEnvBasicTest struct{}
// Execute executes a test case and returns an error if occurred
func (h *templatesDirEnvBasicTest) Execute(filePath string) error {
tempdir, err := os.MkdirTemp("", "nuclei-templates-dir-env-*")
if err != nil {
return errkit.Wrap(err, "failed to create temp dir")
}
defer func() {
_ = os.RemoveAll(tempdir)
}()
// Copy template to temp directory
if err := copyTemplateToDir(filePath, tempdir); err != nil {
return err
}
// Set NUCLEI_TEMPLATES_DIR and run nuclei
envVars := []string{"NUCLEI_TEMPLATES_DIR=" + tempdir}
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, envVars, "-t", filePath, "-u", "8x8exch02.8x8.com")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
// templatesDirEnvAbsolutePathTest tests that absolute paths work correctly
type templatesDirEnvAbsolutePathTest struct{}
// Execute executes a test case and returns an error if occurred
func (h *templatesDirEnvAbsolutePathTest) Execute(filePath string) error {
tempdir, err := os.MkdirTemp("", "nuclei-templates-dir-env-abs-*")
if err != nil {
return errkit.Wrap(err, "failed to create temp dir")
}
defer func() {
_ = os.RemoveAll(tempdir)
}()
// Get absolute path
absTempDir, err := filepath.Abs(tempdir)
if err != nil {
return errkit.Wrap(err, "failed to get absolute path")
}
// Copy template to temp directory
if err := copyTemplateToDir(filePath, absTempDir); err != nil {
return err
}
// Set NUCLEI_TEMPLATES_DIR with absolute path and run nuclei
envVars := []string{"NUCLEI_TEMPLATES_DIR=" + absTempDir}
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, envVars, "-t", filePath, "-u", "8x8exch02.8x8.com")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
// templatesDirEnvRelativePathTest tests that relative paths are resolved correctly
type templatesDirEnvRelativePathTest struct{}
// Execute executes a test case and returns an error if occurred
func (h *templatesDirEnvRelativePathTest) Execute(filePath string) error {
// Create temp directory in current working directory
tempdir, err := os.MkdirTemp(".", "nuclei-templates-dir-env-rel-*")
if err != nil {
return errkit.Wrap(err, "failed to create temp dir")
}
defer func() {
_ = os.RemoveAll(tempdir)
}()
// Get relative path (just the directory name)
relPath := filepath.Base(tempdir)
// Copy template to temp directory
if err := copyTemplateToDir(filePath, tempdir); err != nil {
return err
}
// Set NUCLEI_TEMPLATES_DIR with relative path and run nuclei
// Note: The implementation should convert relative paths to absolute
envVars := []string{"NUCLEI_TEMPLATES_DIR=" + relPath}
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, envVars, "-t", filePath, "-u", "8x8exch02.8x8.com")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
// templatesDirEnvPrecedenceTest tests that -ud flag takes precedence over NUCLEI_TEMPLATES_DIR
type templatesDirEnvPrecedenceTest struct{}
// Execute executes a test case and returns an error if occurred
func (h *templatesDirEnvPrecedenceTest) Execute(filePath string) error {
// Create two temp directories
envTempDir, err := os.MkdirTemp("", "nuclei-templates-dir-env-*")
if err != nil {
return errkit.Wrap(err, "failed to create env temp dir")
}
defer func() {
_ = os.RemoveAll(envTempDir)
}()
flagTempDir, err := os.MkdirTemp("", "nuclei-templates-dir-flag-*")
if err != nil {
return errkit.Wrap(err, "failed to create flag temp dir")
}
defer func() {
_ = os.RemoveAll(flagTempDir)
}()
// Copy template to flag temp directory (this should be used due to precedence)
if err := copyTemplateToDir(filePath, flagTempDir); err != nil {
return err
}
// Set NUCLEI_TEMPLATES_DIR to envTempDir (should be ignored due to -ud flag)
envVars := []string{"NUCLEI_TEMPLATES_DIR=" + envTempDir}
// Use -ud flag which should take precedence
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, envVars, "-t", filePath, "-u", "8x8exch02.8x8.com", "-ud", flagTempDir)
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
// templatesDirEnvCustomTemplatesTest tests that custom template subdirectories are correctly set
type templatesDirEnvCustomTemplatesTest struct{}
// Execute executes a test case and returns an error if occurred
func (h *templatesDirEnvCustomTemplatesTest) Execute(filePath string) error {
tempdir, err := os.MkdirTemp("", "nuclei-templates-dir-custom-*")
if err != nil {
return errkit.Wrap(err, "failed to create temp dir")
}
defer func() {
_ = os.RemoveAll(tempdir)
}()
// Create custom template subdirectories structure
customDirs := []string{"github", "s3", "gitlab", "azure"}
for _, dir := range customDirs {
customDirPath := filepath.Join(tempdir, dir)
if err := os.MkdirAll(customDirPath, 0755); err != nil {
return errkit.Wrap(err, "failed to create custom template directory")
}
}
// Copy template to temp directory
if err := copyTemplateToDir(filePath, tempdir); err != nil {
return err
}
// Set NUCLEI_TEMPLATES_DIR and run nuclei
envVars := []string{"NUCLEI_TEMPLATES_DIR=" + tempdir}
results, err := testutils.RunNucleiBareArgsAndGetResults(debug, envVars, "-t", filePath, "-u", "8x8exch02.8x8.com")
if err != nil {
return err
}
return expectResultsCount(results, 1)
}
+3 -16
View File
@@ -55,12 +55,6 @@
# # these severity labels or tags (does not affect exporters. set those globally)
# deny-list:
# severity: low
# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check
# duplicate-issue-check: false
# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates
# duplicate-issue-page-size: 100
# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit)
# duplicate-issue-max-pages: 0
#
# Gitea contains configuration options for a gitea issue tracker
#gitea:
@@ -87,10 +81,6 @@
# severity: low
# # duplicate-issue-check (optional) flag to enable duplicate tracking issue check
# duplicate-issue-check: false
# # duplicate-issue-page-size (optional) controls how many issues to fetch per page when searching for duplicates
# duplicate-issue-page-size: 100
# # duplicate-issue-max-pages (optional) limits how many pages to fetch when searching for duplicates (0 = no limit)
# duplicate-issue-max-pages: 0
#
# Jira contains configuration options for Jira issue tracker
#jira:
@@ -100,9 +90,6 @@
# update-existing: false
# # URL is the jira application url
# url: https://localhost/jira
# # site-url is the browsable URL for the Jira instance (optional)
# # If not provided, issue.Self will be used. Useful for OAuth where issue.Self contains api.atlassian.com
# site-url: https://your-company.atlassian.net
# # account-id is the account-id of the Jira user or username in case of on-prem Jira
# account-id: test-account-id
# # email is the email of the user for Jira instance
@@ -131,10 +118,10 @@
# status-not: Closed
# # Customfield supports name, id and freeform. name and id are to be used when the custom field is a dropdown.
# # freeform can be used if the custom field is just a text entry
# # Variables can be used to pull various pieces of data from the finding itself.
# # Variables can be used to pull various pieces of data from the finding itself.
# # Supported variables: $CVSSMetrics, $CVEID, $CWEID, $Host, $Severity, $CVSSScore, $Name
# custom-fields:
# customfield_00001:
# customfield_00001:
# name: "Nuclei"
# customfield_00002:
# freeform: $CVSSMetrics
@@ -185,4 +172,4 @@
# omit-raw: false
# # determines the number of results to be kept in memory before writing it to the database or 0 to
# # persist all in memory and write all results at the end (default)
# batch-size: 0
# batch-size: 0
+7 -55
View File
@@ -20,7 +20,6 @@ import (
_ "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/levels"
@@ -40,7 +39,7 @@ 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"
@@ -188,17 +187,14 @@ func main() {
options.Logger.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)
signal.Notify(c, os.Interrupt)
go func() {
@@ -258,7 +254,7 @@ on extensive configurability, massive extensibility and ease of use.`)
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.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),
)
@@ -267,8 +263,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",
@@ -283,7 +277,7 @@ on extensive configurability, massive extensibility and ease of use.`)
flagSet.BoolVar(&options.Validate, "validate", false, "validate the passed templates to nuclei"),
flagSet.BoolVarP(&options.NoStrictSyntax, "no-strict-syntax", "nss", false, "disable strict syntax check on templates"),
flagSet.BoolVarP(&options.TemplateDisplay, "template-display", "td", false, "displays the templates content"),
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all templates matching current filters"),
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all available templates"),
flagSet.BoolVar(&options.TagList, "tgl", false, "list all available tags"),
flagSet.StringSliceVarConfigOnly(&options.RemoteTemplateDomainList, "remote-template-domain", []string{"cloud.projectdiscovery.io"}, "allowed domain list to load remote templates from"),
flagSet.BoolVar(&options.SignTemplates, "sign", false, "signs the templates with the private key defined in NUCLEI_SIGNATURE_PRIVATE_KEY env variable"),
@@ -414,7 +408,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"),
@@ -578,7 +571,6 @@ 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)
@@ -587,49 +579,9 @@ Additional documentation is available at: https://docs.nuclei.sh/getting-started
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)
}
}
}
}
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")
+7 -67
View File
@@ -1,13 +1,9 @@
package main_test
import (
"fmt"
"net/http"
"net/http/httptest"
"os"
"runtime"
"runtime/pprof"
"strings"
"testing"
"time"
@@ -52,31 +48,6 @@ func TestMain(m *testing.M) {
os.Exit(exitCode)
}
// getUniqFilename generates a unique filename by appending .N if file exists
// Similar to wget's behavior: file.cpu.prof, file.cpu.1.prof, file.cpu.2.prof, etc.
func getUniqFilename(basePath string) string {
if _, err := os.Stat(basePath); os.IsNotExist(err) {
return basePath
}
lastDot := strings.LastIndex(basePath, ".")
var name, ext string
if lastDot != -1 {
name = basePath[:lastDot]
ext = basePath[lastDot:]
} else {
name = basePath
ext = ""
}
for i := 1; ; i++ {
newPath := fmt.Sprintf("%s.%d%s", name, i, ext)
if _, err := os.Stat(newPath); os.IsNotExist(err) {
return newPath
}
}
}
func getDefaultOptions() *types.Options {
return &types.Options{
RemoteTemplateDomainList: []string{"cloud.projectdiscovery.io"},
@@ -135,55 +106,24 @@ func runEnumBenchmark(b *testing.B, options *types.Options) {
}
defer nucleiRunner.Close()
benchNameSlug := strings.ReplaceAll(b.Name(), "/", "-")
// Start CPU profiling
cpuProfileBase := fmt.Sprintf("%s.cpu.prof", benchNameSlug)
cpuProfilePath := getUniqFilename(cpuProfileBase)
cpuProfile, err := os.Create(cpuProfilePath)
if err != nil {
b.Fatalf("failed to create CPU profile: %s", err)
}
defer func() { _ = cpuProfile.Close() }()
if err := pprof.StartCPUProfile(cpuProfile); err != nil {
b.Fatalf("failed to start CPU profile: %s", err)
}
defer pprof.StopCPUProfile()
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
for i := 0; i < b.N; i++ {
if err := nucleiRunner.RunEnumeration(); err != nil {
b.Fatalf("%s failed: %s", b.Name(), err)
}
}
b.StopTimer()
// Write heap profile
heapProfileBase := fmt.Sprintf("%s.heap.prof", benchNameSlug)
heapProfilePath := getUniqFilename(heapProfileBase)
heapProfile, err := os.Create(heapProfilePath)
if err != nil {
b.Fatalf("failed to create heap profile: %s", err)
}
defer func() { _ = heapProfile.Close() }()
runtime.GC() // Force GC before heap profile
if err := pprof.WriteHeapProfile(heapProfile); err != nil {
b.Fatalf("failed to write heap profile: %s", err)
}
}
func BenchmarkRunEnumeration(b *testing.B) {
// Default case: run enumeration with default options == all nuclei-templates
b.Run("Default", func(b *testing.B) {
options := getDefaultOptions()
options.Targets = []string{targetURL}
// b.Run("Default", func(b *testing.B) {
// options := getDefaultOptions()
// options.Targets = []string{targetURL}
runEnumBenchmark(b, options)
})
// runEnumBenchmark(b, options)
// })
// Case: https://github.com/projectdiscovery/nuclei/pull/6258
b.Run("Multiproto", func(b *testing.B) {
+18
View File
@@ -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"
+23 -23
View File
@@ -23,7 +23,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
"gopkg.in/yaml.v3"
)
@@ -243,7 +243,7 @@ func enhanceTemplate(data string) (string, bool, error) {
return data, false, err
}
if resp.StatusCode != 200 {
return data, false, errkit.New("unexpected status code: %v", resp.Status)
return data, false, errorutil.New("unexpected status code: %v", resp.Status)
}
var templateResp TemplateResp
if err := json.NewDecoder(resp.Body).Decode(&templateResp); err != nil {
@@ -254,20 +254,20 @@ func enhanceTemplate(data string) (string, bool, error) {
}
if templateResp.ValidateErrorCount > 0 {
if len(templateResp.ValidateError) > 0 {
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
return data, false, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
}
return data, false, errkit.New("validation failed", "tag", "validate")
return data, false, errorutil.New("validation failed").WithTag("validate")
}
if templateResp.Error.Name != "" {
return data, false, errkit.New("%s", templateResp.Error.Name)
return data, false, errorutil.New("%s", templateResp.Error.Name)
}
if strings.TrimSpace(templateResp.Enhanced) == "" && !templateResp.Lint {
if templateResp.LintError.Reason != "" {
return data, false, errkit.New(templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line, "tag", "lint")
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
}
return data, false, errkit.New("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
return data, false, errorutil.NewWithTag("lint", "at line: %v", templateResp.LintError.Mark.Line)
}
return data, false, errkit.New("template enhance failed")
return data, false, errorutil.New("template enhance failed")
}
// formatTemplate formats template data using templateman format api
@@ -277,7 +277,7 @@ func formatTemplate(data string) (string, bool, error) {
return data, false, err
}
if resp.StatusCode != 200 {
return data, false, errkit.New("unexpected status code: %v", resp.Status)
return data, false, errorutil.New("unexpected status code: %v", resp.Status)
}
var templateResp TemplateResp
if err := json.NewDecoder(resp.Body).Decode(&templateResp); err != nil {
@@ -288,20 +288,20 @@ func formatTemplate(data string) (string, bool, error) {
}
if templateResp.ValidateErrorCount > 0 {
if len(templateResp.ValidateError) > 0 {
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
return data, false, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
}
return data, false, errkit.New("validation failed", "tag", "validate")
return data, false, errorutil.New("validation failed").WithTag("validate")
}
if templateResp.Error.Name != "" {
return data, false, errkit.New("%s", templateResp.Error.Name)
return data, false, errorutil.New("%s", templateResp.Error.Name)
}
if strings.TrimSpace(templateResp.Updated) == "" && !templateResp.Lint {
if templateResp.LintError.Reason != "" {
return data, false, errkit.New(templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line, "tag", "lint")
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
}
return data, false, errkit.New("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
return data, false, errorutil.NewWithTag("lint", "at line: %v", templateResp.LintError.Mark.Line)
}
return data, false, errkit.New("template format failed")
return data, false, errorutil.New("template format failed")
}
// lintTemplate lints template data using templateman lint api
@@ -311,7 +311,7 @@ func lintTemplate(data string) (bool, error) {
return false, err
}
if resp.StatusCode != 200 {
return false, errkit.New("unexpected status code: %v", resp.Status)
return false, errorutil.New("unexpected status code: %v", resp.Status)
}
var lintResp TemplateLintResp
if err := json.NewDecoder(resp.Body).Decode(&lintResp); err != nil {
@@ -321,9 +321,9 @@ func lintTemplate(data string) (bool, error) {
return true, nil
}
if lintResp.LintError.Reason != "" {
return false, errkit.New(lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line, "tag", "lint")
return false, errorutil.NewWithTag("lint", lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line)
}
return false, errkit.New("at line: %v", lintResp.LintError.Mark.Line, "tag", "lint")
return false, errorutil.NewWithTag("lint", "at line: %v", lintResp.LintError.Mark.Line)
}
// validateTemplate validates template data using templateman validate api
@@ -333,7 +333,7 @@ func validateTemplate(data string) (bool, error) {
return false, err
}
if resp.StatusCode != 200 {
return false, errkit.New("unexpected status code: %v", resp.Status)
return false, errorutil.New("unexpected status code: %v", resp.Status)
}
var validateResp TemplateResp
if err := json.NewDecoder(resp.Body).Decode(&validateResp); err != nil {
@@ -344,14 +344,14 @@ func validateTemplate(data string) (bool, error) {
}
if validateResp.ValidateErrorCount > 0 {
if len(validateResp.ValidateError) > 0 {
return false, errkit.New(validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line, "tag", "validate")
return false, errorutil.NewWithTag("validate", validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line)
}
return false, errkit.New("validation failed", "tag", "validate")
return false, errorutil.New("validation failed").WithTag("validate")
}
if validateResp.Error.Name != "" {
return false, errkit.New("%s", validateResp.Error.Name)
return false, errorutil.New("%s", validateResp.Error.Name)
}
return false, errkit.New("template validation failed")
return false, errorutil.New("template validation failed")
}
// parseAndAddMaxRequests parses and adds max requests to templates
+80 -124
View File
@@ -1,13 +1,11 @@
module github.com/projectdiscovery/nuclei/v3
go 1.24.2
toolchain go1.24.4
go 1.24.1
require (
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
github.com/andygrunwald/go-jira v1.16.1
github.com/antchfx/htmlquery v1.3.5
github.com/andygrunwald/go-jira v1.16.0
github.com/antchfx/htmlquery v1.3.4
github.com/bluele/gcache v0.0.2
github.com/go-playground/validator/v10 v10.26.0
github.com/go-rod/rod v0.116.2
@@ -18,16 +16,16 @@ require (
github.com/json-iterator/go v1.1.12
github.com/julienschmidt/httprouter v1.3.0
github.com/logrusorgru/aurora v2.0.3+incompatible
github.com/miekg/dns v1.1.68
github.com/miekg/dns v1.1.66
github.com/olekukonko/tablewriter v1.0.8
github.com/pkg/errors v0.9.1
github.com/projectdiscovery/clistats v0.1.1
github.com/projectdiscovery/fastdialer v0.5.1
github.com/projectdiscovery/hmap v0.0.99
github.com/projectdiscovery/fastdialer v0.4.4
github.com/projectdiscovery/hmap v0.0.92
github.com/projectdiscovery/interactsh v1.2.4
github.com/projectdiscovery/rawhttp v0.1.90
github.com/projectdiscovery/retryabledns v1.0.112
github.com/projectdiscovery/retryablehttp-go v1.3.1
github.com/projectdiscovery/retryabledns v1.0.105
github.com/projectdiscovery/retryablehttp-go v1.0.119
github.com/projectdiscovery/yamldoc-go v1.0.6
github.com/remeh/sizedwaitgroup v1.0.0
github.com/rs/xid v1.6.0
@@ -37,28 +35,26 @@ require (
github.com/spf13/cast v1.9.2
github.com/syndtr/goleveldb v1.0.0
github.com/valyala/fasttemplate v1.2.2
github.com/weppos/publicsuffix-go v0.50.1
github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39
go.uber.org/multierr v1.11.0
golang.org/x/net v0.48.0
golang.org/x/net v0.41.0
golang.org/x/oauth2 v0.30.0
golang.org/x/text v0.32.0
golang.org/x/text v0.26.0
gopkg.in/yaml.v2 v2.4.0
)
require (
carvel.dev/ytt v0.52.0
code.gitea.io/sdk/gitea v0.17.0
code.gitea.io/sdk/gitea v0.21.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
github.com/DataDog/gostackparse v0.7.0
github.com/Masterminds/semver/v3 v3.2.1
github.com/Masterminds/semver/v3 v3.4.0
github.com/Mzack9999/gcache v0.0.0-20230410081825-519e28eab057
github.com/Mzack9999/goja v0.0.0-20250507184235-e46100e9c697
github.com/Mzack9999/goja_nodejs v0.0.0-20250507184139-66bcbf65c883
github.com/alexsnet/go-vnc v0.1.0
github.com/alitto/pond v1.9.2
github.com/antchfx/xmlquery v1.4.4
github.com/antchfx/xpath v1.3.5
github.com/antchfx/xpath v1.3.4
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2
github.com/aws/aws-sdk-go-v2 v1.36.5
github.com/aws/aws-sdk-go-v2/config v1.29.17
@@ -75,7 +71,7 @@ require (
github.com/getkin/kin-openapi v0.132.0
github.com/go-git/go-git/v5 v5.16.2
github.com/go-ldap/ldap/v3 v3.4.11
github.com/go-pg/pg/v10 v10.15.0
github.com/go-pg/pg v8.0.7+incompatible
github.com/go-sql-driver/mysql v1.9.3
github.com/goccy/go-json v0.10.5
github.com/google/uuid v1.6.0
@@ -87,72 +83,68 @@ require (
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa
github.com/lib/pq v1.10.9
github.com/mattn/go-sqlite3 v1.14.28
github.com/maypok86/otter/v2 v2.2.1
github.com/mholt/archives v0.1.5
github.com/mholt/archives v0.1.3
github.com/microsoft/go-mssqldb v1.9.2
github.com/ory/dockertest/v3 v3.12.0
github.com/praetorian-inc/fingerprintx v1.1.15
github.com/projectdiscovery/dsl v0.8.10
github.com/projectdiscovery/dsl v0.5.0
github.com/projectdiscovery/fasttemplate v0.0.2
github.com/projectdiscovery/gcache v0.0.0-20241015120333-12546c6e3f4c
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
github.com/projectdiscovery/goflags v0.1.74
github.com/projectdiscovery/gologger v1.1.66
github.com/projectdiscovery/gologger v1.1.54
github.com/projectdiscovery/gostruct v0.0.2
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81
github.com/projectdiscovery/httpx v1.7.4
github.com/projectdiscovery/mapcidr v1.1.97
github.com/projectdiscovery/gozero v0.0.3
github.com/projectdiscovery/httpx v1.7.0
github.com/projectdiscovery/mapcidr v1.1.34
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
github.com/projectdiscovery/networkpolicy v0.1.33
github.com/projectdiscovery/ratelimit v0.0.82
github.com/projectdiscovery/networkpolicy v0.1.18
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819165347-d56e782c656d
github.com/projectdiscovery/rdap v0.9.0
github.com/projectdiscovery/sarif v0.0.1
github.com/projectdiscovery/tlsx v1.2.2
github.com/projectdiscovery/uncover v1.2.0
github.com/projectdiscovery/useragent v0.0.106
github.com/projectdiscovery/utils v0.8.0
github.com/projectdiscovery/wappalyzergo v0.2.61
github.com/projectdiscovery/tlsx v1.1.9
github.com/projectdiscovery/uncover v1.1.0
github.com/projectdiscovery/useragent v0.0.101
github.com/projectdiscovery/utils v0.4.23
github.com/projectdiscovery/wappalyzergo v0.2.36
github.com/redis/go-redis/v9 v9.11.0
github.com/seh-msft/burpxml v1.0.1
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
github.com/sijms/go-ora/v2 v2.9.0
github.com/stretchr/testify v1.11.1
github.com/stretchr/testify v1.10.0
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
github.com/testcontainers/testcontainers-go v0.38.0
github.com/testcontainers/testcontainers-go/modules/mongodb v0.37.0
github.com/yassinebenaid/godump v0.11.1
github.com/zmap/zgrab2 v0.1.8
gitlab.com/gitlab-org/api/client-go v0.130.1
go.mongodb.org/mongo-driver v1.17.4
golang.org/x/term v0.38.0
golang.org/x/term v0.32.0
gopkg.in/yaml.v3 v3.0.1
moul.io/http2curl v1.0.0
)
require (
aead.dev/minisign v0.2.0 // indirect
dario.cat/mergo v1.0.2 // indirect
dario.cat/mergo v1.0.0 // indirect
filippo.io/edwards25519 v1.1.0 // indirect
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a // indirect
github.com/42wim/httpsig v1.2.2 // indirect
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 // indirect
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 // indirect
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 // indirect
github.com/BurntSushi/toml v1.3.2 // indirect
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/Mzack9999/go-http-digest-auth-client v0.6.1-0.20220414142836-eb8883508809 // indirect
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
github.com/ProtonMail/go-crypto v1.1.6 // indirect
github.com/PuerkitoBio/goquery v1.11.0 // indirect
github.com/STARRY-S/zip v0.2.3 // indirect
github.com/PuerkitoBio/goquery v1.10.3 // indirect
github.com/STARRY-S/zip v0.2.1 // indirect
github.com/VividCortex/ewma v1.2.0 // indirect
github.com/akrylysov/pogreb v0.10.2 // indirect
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
github.com/alecthomas/kingpin/v2 v2.4.0 // indirect
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 // indirect
github.com/andybalholm/brotli v1.2.0 // indirect
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 // indirect
github.com/andybalholm/cascadia v1.3.3 // indirect
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.11 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.32 // indirect
@@ -174,15 +166,12 @@ require (
github.com/bits-and-blooms/bitset v1.13.0 // indirect
github.com/bits-and-blooms/bloom/v3 v3.5.0 // indirect
github.com/bodgit/plumbing v1.3.0 // indirect
github.com/bodgit/sevenzip v1.6.1 // indirect
github.com/bodgit/sevenzip v1.6.0 // indirect
github.com/bodgit/windows v1.0.1 // indirect
github.com/brianvoe/gofakeit/v7 v7.2.1 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/caddyserver/certmagic v0.19.2 // indirect
github.com/cenkalti/backoff/v4 v4.3.0 // indirect
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
github.com/censys/censys-sdk-go v0.19.1 // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
github.com/charmbracelet/lipgloss v1.1.1-0.20250404203927-76690c660834 // indirect
@@ -190,37 +179,27 @@ require (
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf // indirect
github.com/charmbracelet/x/term v0.2.1 // indirect
github.com/cheggaaa/pb/v3 v3.1.6 // indirect
github.com/cheggaaa/pb/v3 v3.1.4 // indirect
github.com/cloudflare/cfssl v1.6.4 // indirect
github.com/cloudflare/circl v1.6.1 // indirect
github.com/cloudwego/base64x v0.1.5 // indirect
github.com/cnf/structhash v0.0.0-20250313080605-df4c6cc74a9a // indirect
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 // indirect
github.com/containerd/continuity v0.4.5 // indirect
github.com/containerd/errdefs v1.0.0 // indirect
github.com/containerd/errdefs/pkg v0.3.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/platforms v0.2.1 // indirect
github.com/cpuguy83/dockercfg v0.3.2 // indirect
github.com/cyphar/filepath-securejoin v0.5.1 // indirect
github.com/cyphar/filepath-securejoin v0.4.1 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/davidmz/go-pageant v1.0.2 // indirect
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
github.com/distribution/reference v0.6.0 // indirect
github.com/djherbis/times v1.6.0 // indirect
github.com/dlclark/regexp2 v1.11.5 // indirect
github.com/docker/cli v27.4.1+incompatible // indirect
github.com/docker/docker v28.3.3+incompatible // indirect
github.com/docker/go-connections v0.6.0 // indirect
github.com/docker/docker v28.0.0+incompatible // indirect
github.com/docker/go-connections v0.5.0 // indirect
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 // indirect
github.com/ebitengine/purego v0.8.4 // indirect
github.com/emirpasic/gods v1.18.1 // indirect
github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/felixge/fgprof v0.9.5 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/free5gc/util v1.0.5-0.20230511064842-2e120956883b // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gaissmai/bart v0.26.0 // indirect
github.com/gaissmai/bart v0.20.5 // indirect
github.com/geoffgarside/ber v1.1.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gin-gonic/gin v1.9.1 // indirect
@@ -228,37 +207,33 @@ require (
github.com/go-fed/httpsig v1.1.0 // indirect
github.com/go-git/gcfg v1.5.1-0.20230307220236-3a3c6141e376 // indirect
github.com/go-git/go-billy/v5 v5.6.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.3.0 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-openapi/jsonpointer v0.21.0 // indirect
github.com/go-openapi/swag v0.23.0 // indirect
github.com/go-pg/zerochecker v0.2.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/go-viper/mapstructure/v2 v2.3.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.2.2 // indirect
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
github.com/golang-sql/sqlexp v0.1.0 // indirect
github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 // indirect
github.com/golang/snappy v0.0.4 // indirect
github.com/google/certificate-transparency-go v1.3.2 // indirect
github.com/google/certificate-transparency-go v1.1.4 // indirect
github.com/google/go-github/v30 v30.1.0 // indirect
github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect
github.com/gorilla/css v1.0.1 // indirect
github.com/gosimple/slug v1.15.0 // indirect
github.com/gosimple/unidecode v1.0.1 // indirect
github.com/hashicorp/errwrap v1.1.0 // indirect
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-retryablehttp v0.7.8 // indirect
github.com/hashicorp/go-uuid v1.0.3 // indirect
github.com/hashicorp/go-version v1.7.0 // indirect
github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf // indirect
github.com/hdm/jarm-go v0.0.7 // indirect
github.com/iangcarroll/cookiemonster v1.6.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/itchyny/timefmt-go v0.1.6 // indirect
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
@@ -268,10 +243,9 @@ require (
github.com/jcmturner/rpc/v2 v2.0.3 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect
github.com/kataras/jwt v0.1.10 // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.2 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
github.com/klauspost/pgzip v1.2.6 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
@@ -279,11 +253,10 @@ require (
github.com/leodido/go-urn v1.4.0 // indirect
github.com/libdns/libdns v0.2.1 // indirect
github.com/logrusorgru/aurora/v4 v4.0.0 // indirect
github.com/lor00x/goldap v0.0.0-20240304151906-8d785c64d1c8 // indirect
github.com/lucasb-eyer/go-colorful v1.3.0 // indirect
github.com/lufia/plan9stats v0.0.0-20250821153705-5981dea3221d // indirect
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/mackerelio/go-osstat v0.2.4 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.14 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
@@ -291,76 +264,63 @@ require (
github.com/mholt/acmez v1.2.0 // indirect
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
github.com/mikelolasagasti/xz v1.0.1 // indirect
github.com/minio/minlz v1.0.1 // indirect
github.com/minio/minlz v1.0.0 // indirect
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect
github.com/moby/go-archive v0.1.0 // indirect
github.com/moby/patternmatcher v0.6.0 // indirect
github.com/moby/sys/sequential v0.6.0 // indirect
github.com/moby/sys/user v0.4.0 // indirect
github.com/moby/sys/userns v0.1.0 // indirect
github.com/moby/term v0.5.2 // indirect
github.com/moby/sys/user v0.3.0 // indirect
github.com/moby/term v0.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
github.com/montanaflynn/stats v0.7.1 // indirect
github.com/morikuni/aec v1.0.0 // indirect
github.com/muesli/reflow v0.3.0 // indirect
github.com/muesli/termenv v0.16.0 // indirect
github.com/nwaples/rardecode/v2 v2.2.1 // indirect
github.com/nwaples/rardecode/v2 v2.1.0 // indirect
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 // indirect
github.com/oasdiff/yaml3 v0.0.0-20250309153720-d2182401db90 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.1 // indirect
github.com/opencontainers/runc v1.2.8 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/opencontainers/runc v1.2.3 // indirect
github.com/openrdap/rdap v0.9.1 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/perimeterx/marshmallow v1.1.5 // indirect
github.com/pierrec/lz4/v4 v4.1.22 // indirect
github.com/pierrec/lz4/v4 v4.1.21 // indirect
github.com/pjbgf/sha1cd v0.3.2 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
github.com/projectdiscovery/asnmap v1.1.1 // indirect
github.com/projectdiscovery/blackrock v0.0.1 // indirect
github.com/projectdiscovery/cdncheck v1.2.16 // indirect
github.com/projectdiscovery/cdncheck v1.1.26 // indirect
github.com/projectdiscovery/freeport v0.0.7 // indirect
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 // indirect
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
github.com/refraction-networking/utls v1.7.1 // indirect
github.com/sashabaranov/go-openai v1.37.0 // indirect
github.com/sergi/go-diff v1.3.2-0.20230802210424-5b0b94c5c0d3 // indirect
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
github.com/shirou/gopsutil/v4 v4.25.7 // indirect
github.com/shoenig/go-m1cpu v0.1.6 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/sorairolake/lzip-go v0.3.8 // indirect
github.com/spf13/afero v1.15.0 // indirect
github.com/sorairolake/lzip-go v0.3.5 // indirect
github.com/tidwall/btree v1.7.0 // indirect
github.com/tidwall/buntdb v1.3.1 // indirect
github.com/tidwall/gjson v1.18.0 // indirect
github.com/tidwall/grect v0.1.4 // indirect
github.com/tidwall/match v1.2.0 // indirect
github.com/tidwall/match v1.1.1 // indirect
github.com/tidwall/pretty v1.2.1 // indirect
github.com/tidwall/rtred v0.1.2 // indirect
github.com/tidwall/tinyqueue v0.1.1 // indirect
github.com/tim-ywliu/nested-logrus-formatter v1.3.2 // indirect
github.com/tklauser/go-sysconf v0.3.15 // indirect
github.com/tklauser/numcpus v0.10.0 // indirect
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
github.com/tklauser/go-sysconf v0.3.12 // indirect
github.com/tklauser/numcpus v0.6.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
github.com/ulikunitz/xz v0.5.15 // indirect
github.com/ulikunitz/xz v0.5.12 // indirect
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/vmihailenco/bufpool v0.1.11 // indirect
github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect
github.com/vmihailenco/tagparser v0.1.2 // indirect
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
github.com/vulncheck-oss/go-exploit v1.51.0 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
@@ -373,18 +333,14 @@ require (
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
github.com/ysmood/fetchup v0.2.3 // indirect
github.com/ysmood/got v0.40.0 // indirect
github.com/yuin/goldmark v1.7.13 // indirect
github.com/yuin/goldmark v1.7.8 // indirect
github.com/yuin/goldmark-emoji v1.0.5 // indirect
github.com/zcalusic/sysinfo v1.0.2 // indirect
github.com/zeebo/blake3 v0.2.3 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
go.opentelemetry.io/otel/trace v1.38.0 // indirect
go4.org v0.0.0-20230225012048-214862532bf5 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/sync v0.19.0 // indirect
golang.org/x/sync v0.15.0 // indirect
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
mellium.im/sasl v0.3.2 // indirect
)
@@ -404,16 +360,16 @@ require (
github.com/yusufpapurcu/wmi v1.2.4 // indirect
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect
github.com/zmap/zcrypto v0.0.0-20240512203510-0fef58d9a9db // indirect
go.etcd.io/bbolt v1.4.0 // indirect
go.uber.org/zap v1.27.0 // indirect
go.etcd.io/bbolt v1.3.10 // indirect
go.uber.org/zap v1.25.0 // indirect
goftp.io/server/v2 v2.0.1 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/exp v0.0.0-20250911091902-df9299821621
golang.org/x/mod v0.30.0 // indirect
golang.org/x/sys v0.39.0 // indirect
golang.org/x/time v0.14.0 // indirect
golang.org/x/tools v0.39.0
google.golang.org/protobuf v1.36.6 // indirect
golang.org/x/crypto v0.39.0 // indirect
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b
golang.org/x/mod v0.25.0 // indirect
golang.org/x/sys v0.33.0 // indirect
golang.org/x/time v0.11.0 // indirect
golang.org/x/tools v0.34.0
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
)
+170 -282
View File
@@ -1,7 +1,5 @@
aead.dev/minisign v0.2.0 h1:kAWrq/hBRu4AARY6AlciO83xhNnW9UaC8YipS2uhLPk=
aead.dev/minisign v0.2.0/go.mod h1:zdq6LdSd9TbuSxchxwhpA9zEb9YXcVGoE8JakuiGaIQ=
carvel.dev/ytt v0.52.0 h1:tkJPL8Gun5snVfypNXbmMKwnbwMyspcTi3Ypyso3nRY=
carvel.dev/ytt v0.52.0/go.mod h1:QgmuU7E15EXW1r2wxTt7zExVz14IHwEG4WNMmaFBkJo=
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
@@ -35,17 +33,17 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
code.gitea.io/sdk/gitea v0.17.0 h1:8JPBss4+Jf7AE1YcfyiGrngTXE8dFSG3si/bypsTH34=
code.gitea.io/sdk/gitea v0.17.0/go.mod h1:ndkDk99BnfiUCCYEUhpNzi0lpmApXlwRFqClBlOlEBg=
dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8=
dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA=
code.gitea.io/sdk/gitea v0.21.0 h1:69n6oz6kEVHRo1+APQQyizkhrZrLsTLXey9142pfkD4=
code.gitea.io/sdk/gitea v0.21.0/go.mod h1:tnBjVhuKJCn8ibdyyhvUyxrR1Ca2KHEoTWoukNhXQPA=
dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk=
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a h1:3i+FJ7IpSZHL+VAjtpQeZCRhrpP0odl5XfoLBY4fxJ8=
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a/go.mod h1:C7hXLmFmPYPjIDGfQl1clsmQ5TMEQfmzWTrJk475bUs=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0 h1:Gt0j3wceWMwPmiazCa8MzMA0MfhmPIz0Qp0FJ6qcM0U=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.0/go.mod h1:Ot/6aikWnKWi4l9QB7qVSwa8iMphQNqkWALMoNT3rzM=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1 h1:B+blDbyVIG3WaikNxPnhPiJ1MThR03b3vKGtER95TP4=
@@ -54,16 +52,16 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1 h1:FPKJS1T+clwv+OLGt13a8UjqeRuh0O4SJ3lUriThc+4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.1/go.mod h1:j2chePtV91HrC22tGoRX3sGY42uF13WzmmV80/OdVAA=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0 h1:LR0kAX9ykz8G4YgLCaRDVJ3+n43R8MneB5dTy2konZo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.8.0/go.mod h1:DWAciXemNf++PQJLeXUB4HHH5OpsAh12HZnu2wXE1jA=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1 h1:Wgf5rZba3YZqeTNJPtvqZoBu1sBN/L4sry+u2U3Y75w=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.3.1/go.mod h1:xxCBG/f/4Vbmh2XQJBsOmNdxWUY5j/s27jujKPbQf14=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1 h1:bFWuoEKg+gImo7pvkiQEFAc8ocibADgXeiLAxWhWmkI=
github.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.1.1/go.mod h1:Vih/3yc6yac2JzU4hzpaDupBJP0Flaia9rXXrU8xyww=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1 h1:lhZdRq7TIx0GJQvSyX2Si406vrYsov2FXGp/RnSEtcs=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1/go.mod h1:8cl44BDmi+effbARHMQjgOKA2AYvcohNm7KEt42mSV8=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25UVaW/CKtUDjefjrs0SPonmDGUVOYP0=
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
@@ -71,15 +69,13 @@ github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1/go.mo
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2 h1:oygO0locgZJe7PpYPXT5A29ZkwJaPqcva7BVeemZOZs=
github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/DataDog/gostackparse v0.7.0 h1:i7dLkXHvYzHV308hnkvVGDL3BR4FWl7IsXNPz/IGQh4=
github.com/DataDog/gostackparse v0.7.0/go.mod h1:lTfqcJKqS9KnXQGnyQMCugq3u1FP6UZMfWR0aitKFMM=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible h1:1G1pk05UrOh0NlF1oeaaix1x8XzrfjIDK47TY0Zehcw=
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.5.2/go.mod h1:WpS1mjBmmwHBEWmogvA2mj8546UReBk4v8QkMxJ6pZY=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
@@ -98,11 +94,11 @@ github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAE
github.com/ProtonMail/go-crypto v0.0.0-20230217124315-7d5c6f04bbb8/go.mod h1:I0gYDMZ6Z5GRU7l58bNFSkPTFN6Yl12dsUlAZ8xy98g=
github.com/ProtonMail/go-crypto v1.1.6 h1:ZcV+Ropw6Qn0AX9brlQLAUXfqLBc7Bl+f/DmNxpLfdw=
github.com/ProtonMail/go-crypto v1.1.6/go.mod h1:rA3QumHc/FZ8pAHreoekgiAbzpNsfQAosU5td4SnOrE=
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
github.com/RumbleDiscovery/rumble-tools v0.0.0-20201105153123-f2adbb3244d2/go.mod h1:jD2+mU+E2SZUuAOHZvZj4xP4frlOo+N/YrXDvASFhkE=
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow=
github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4=
github.com/akrylysov/pogreb v0.10.2 h1:e6PxmeyEhWyi2AKOBIJzAEi4HkiC+lKyCocRGlnDi78=
@@ -128,25 +124,23 @@ github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAu
github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa h1:LHTHcTQiSGT7VVbI0o4wBRNQIgn917usHWOd6VAffYI=
github.com/alexbrainman/sspi v0.0.0-20231016080023-1a75b4708caa/go.mod h1:cEWa1LVoE5KvSD9ONXsZrj0z6KqySlCCNKHlLzbqAt4=
github.com/alexsnet/go-vnc v0.1.0 h1:vBCwPNy79WEL8V/Z5A0ngEFCvTWBAjmS048lkR2rdmY=
github.com/alexsnet/go-vnc v0.1.0/go.mod h1:bbRsg41Sh3zvrnWsw+REKJVGZd8Of2+S0V1G0ZaBhlU=
github.com/alitto/pond v1.9.2 h1:9Qb75z/scEZVCoSU+osVmQ0I0JOeLfdTDafrbcJ8CLs=
github.com/alitto/pond v1.9.2/go.mod h1:xQn3P/sHTYcU/1BR3i86IGIrilcrGC2LiS+E2+CJWsI=
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
github.com/andygrunwald/go-jira v1.16.1 h1:WoQEar5XoDRAibOgKzTFELlPNlKAtnfWr296R9zdFLA=
github.com/andygrunwald/go-jira v1.16.1/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU=
github.com/andygrunwald/go-jira v1.16.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ=
github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/antchfx/htmlquery v1.3.5 h1:aYthDDClnG2a2xePf6tys/UyyM/kRcsFRm+ifhFKoU0=
github.com/antchfx/htmlquery v1.3.5/go.mod h1:5oyIPIa3ovYGtLqMPNjBF2Uf25NPCKsMjCnQ8lvjaoA=
github.com/antchfx/htmlquery v1.3.4 h1:Isd0srPkni2iNTWCwVj/72t7uCphFeor5Q8nCzj1jdQ=
github.com/antchfx/htmlquery v1.3.4/go.mod h1:K9os0BwIEmLAvTqaNSua8tXLWRWZpocZIH73OzWQbwM=
github.com/antchfx/xmlquery v1.4.4 h1:mxMEkdYP3pjKSftxss4nUHfjBhnMk4imGoR96FRY2dg=
github.com/antchfx/xmlquery v1.4.4/go.mod h1:AEPEEPYE9GnA2mj5Ur2L5Q5/2PycJ0N9Fusrx9b12fc=
github.com/antchfx/xpath v1.3.3/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/xpath v1.3.5 h1:PqbXLC3TkfeZyakF5eeh3NTWEbYl4VHNVeufANzDbKQ=
github.com/antchfx/xpath v1.3.5/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/antchfx/xpath v1.3.4 h1:1ixrW1VnXd4HurCj7qnqnR0jo14g8JMe20Fshg1Vgz4=
github.com/antchfx/xpath v1.3.4/go.mod h1:i54GszH55fYfBmoZXapTHN8T8tkcHfRgLyVwwqzXNcs=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
@@ -197,6 +191,8 @@ github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuP
github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A=
github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
@@ -209,12 +205,10 @@ github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
github.com/bluele/gcache v0.0.2/go.mod h1:m15KV+ECjptwSPxKhOhQoAFQVtUFjTVkc3H8o0t/fp0=
github.com/bodgit/plumbing v1.3.0 h1:pf9Itz1JOQgn7vEOE7v7nlEfBykYqvUYioC61TwWCFU=
github.com/bodgit/plumbing v1.3.0/go.mod h1:JOTb4XiRu5xfnmdnDJo6GmSbSbtSyufrsyZFByMtKEs=
github.com/bodgit/sevenzip v1.6.1 h1:kikg2pUMYC9ljU7W9SaqHXhym5HyKm8/M/jd31fYan4=
github.com/bodgit/sevenzip v1.6.1/go.mod h1:GVoYQbEVbOGT8n2pfqCIMRUaRjQ8F9oSqoBEqZh5fQ8=
github.com/bodgit/sevenzip v1.6.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
github.com/bodgit/windows v1.0.1 h1:tF7K6KOluPYygXa3Z2594zxlkbKPAOvqr97etrGNIz4=
github.com/bodgit/windows v1.0.1/go.mod h1:a6JLwrB4KrTR5hBpp8FI9/9W9jJfeQ2h4XDXU74ZCdM=
github.com/brianvoe/gofakeit/v7 v7.2.1 h1:AGojgaaCdgq4Adzrd2uWdbGNDyX6MWNhHdQBraNfOHI=
github.com/brianvoe/gofakeit/v7 v7.2.1/go.mod h1:QXuPeBw164PJCzCUZVmgpgHJ3Llj49jSLVkKPMtxtxA=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
@@ -231,11 +225,7 @@ github.com/caddyserver/certmagic v0.19.2 h1:HZd1AKLx4592MalEGQS39DKs2ZOAJCEM/xYP
github.com/caddyserver/certmagic v0.19.2/go.mod h1:fsL01NomQ6N+kE2j37ZCnig2MFosG+MIO4ztnmG/zz8=
github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=
github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
github.com/censys/censys-sdk-go v0.19.1 h1:CG8rQKgwrKuoICd3oU0uddALMfJnboeMkDg/e74HYyc=
github.com/censys/censys-sdk-go v0.19.1/go.mod h1:DgPz5NgL+EfoueXLPG9UG1e7hS0OhtlywgpkIuu3ZRE=
github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko=
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
@@ -259,8 +249,8 @@ github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Y
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
github.com/charmbracelet/x/term v0.2.1/go.mod h1:oQ4enTYFV7QN4m0i9mzHrViD7TQKvNEEkHUMCmsxdUg=
github.com/cheggaaa/pb/v3 v3.1.6 h1:h0x+vd7EiUohAJ29DJtJy+SNAc55t/elW3jCD086EXk=
github.com/cheggaaa/pb/v3 v3.1.6/go.mod h1:urxmfVtaxT+9aWk92DbsvXFZtNSWQSO5TRAp+MJ3l1s=
github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
github.com/chromedp/cdproto v0.0.0-20230802225258-3cf4e6d46a89/go.mod h1:GKljq0VrfU4D5yc+2qA6OVr8pmO/MBbPEWqWQ/oqGEs=
github.com/chromedp/chromedp v0.9.2/go.mod h1:LkSXJKONWTCHAfQasKFUZI+mxqS4tZqhmtGzzhLsnLs=
github.com/chromedp/sysutil v1.0.0/go.mod h1:kgWmDdq8fTzXYcKIBqIYvRRTnYb9aNS9moAV0xufSww=
@@ -282,24 +272,14 @@ github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCy
github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w=
github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY=
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
github.com/cnf/structhash v0.0.0-20250313080605-df4c6cc74a9a h1:Ohw57yVY2dBTt+gsC6aZdteyxwlxfbtgkFEMTEkwgSw=
github.com/cnf/structhash v0.0.0-20250313080605-df4c6cc74a9a/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4=
github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=
github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=
github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY=
github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/cyphar/filepath-securejoin v0.5.1 h1:eYgfMq5yryL4fbWfkLpFFy2ukSELzaJOTaUTuh+oF48=
github.com/cyphar/filepath-securejoin v0.5.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/cyphar/filepath-securejoin v0.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
@@ -310,29 +290,23 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c h1:+Zo5Ca9GH0RoeVZQKzFJcTLoAixx5s5Gq3pTIS+n354=
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:HJGU9ULdREjOcVGZVPB5s6zYmHi1RxzT71l2wQyLmnE=
github.com/djherbis/times v1.6.0 h1:w2ctJ92J8fBvWPxugmXIv7Nz7Q3iDMKNx9v5ocVH20c=
github.com/djherbis/times v1.6.0/go.mod h1:gOHeRAz2h+VJNZ5Gmc/o7iD9k4wW7NMVqieYCY99oc0=
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZQ=
github.com/dlclark/regexp2 v1.11.5/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
github.com/docker/cli v27.4.1+incompatible h1:VzPiUlRJ/xh+otB75gva3r05isHMo5wXDfPRi5/b4hI=
github.com/docker/cli v27.4.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/docker v28.3.3+incompatible h1:Dypm25kh4rmk49v1eiVbsAtpAsYURjYkaKubwuBdxEI=
github.com/docker/docker v28.3.3+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=
github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=
github.com/docker/docker v28.0.0+incompatible h1:Olh0KS820sJ7nPsBKChVhk5pzqcwDR15fumfAd/p9hM=
github.com/docker/docker v28.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c=
github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc=
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4=
github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s=
github.com/dsnet/golib v0.0.0-20171103203638-1ea166775780/go.mod h1:Lj+Z9rebOhdfkVLjJ8T6VcRQv3SXugXy999NBtR9aFY=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw=
github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
github.com/elazarl/goproxy v1.7.2 h1:Y2o6urb7Eule09PjlhQRGNsqRfPmYI3KKQLFpCAV3+o=
github.com/elazarl/goproxy v1.7.2/go.mod h1:82vkLNir0ALaW14Rc399OTTjyNREgmdL2cVoIbS6XaE=
github.com/emirpasic/gods v1.18.1 h1:FXtiHYKDGKCW2KzwZKx0iC0PQmdlorYgdFG9jPXJ1Bc=
@@ -341,16 +315,12 @@ github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymF
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731 h1:R/ZjJpjQKsZ6L/+Gf9WHbt31GG8NMVcpRqUE+1mMIyo=
github.com/ericlagergren/decimal v0.0.0-20240411145413-00de7ca16731/go.mod h1:M9R1FoZ3y//hwwnJtO51ypFGwm8ZfpxPT/ZLtO1mcgQ=
github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/felixge/fgprof v0.9.5 h1:8+vR6yu2vvSKn08urWyEuxx75NWPEvybbkBirEpsbVY=
github.com/felixge/fgprof v0.9.5/go.mod h1:yKl+ERSa++RYOs32d8K6WEXCB4uXdLls4ZaZPpayhMM=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
@@ -362,8 +332,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM=
github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8=
github.com/gaissmai/bart v0.26.0 h1:xOZ57E9hJLBiQaSyeZa9wgWhGuzfGACgqp4BE77OkO0=
github.com/gaissmai/bart v0.26.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c=
github.com/gaissmai/bart v0.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
github.com/getkin/kin-openapi v0.132.0 h1:3ISeLMsQzcb5v26yeJrBcdTCEQTag36ZjaGk7MIRUwk=
@@ -402,22 +372,14 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=
github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
github.com/go-pg/pg/v10 v10.15.0 h1:6DQwbaxJz/e4wvgzbxBkBLiL/Uuk87MGgHhkURtzx24=
github.com/go-pg/pg/v10 v10.15.0/go.mod h1:FIn/x04hahOf9ywQ1p68rXqaDVbTRLYlu4MQR0lhoB8=
github.com/go-pg/zerochecker v0.2.0 h1:pp7f72c3DobMWOb2ErtZsnrPaSvHd2W4o9//8HtF4mU=
github.com/go-pg/zerochecker v0.2.0/go.mod h1:NJZ4wKL0NmTtz0GKCoJ8kym6Xn/EQzXRl2OnAe7MmDo=
github.com/go-pg/pg v8.0.7+incompatible h1:ty/sXL1OZLo+47KK9N8llRcmbA9tZasqbQ/OO4ld53g=
github.com/go-pg/pg v8.0.7+incompatible/go.mod h1:a2oXow+aFOrvwcKs3eIA0lNFmMilrxK2sOkB5NWe0vA=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
@@ -435,8 +397,8 @@ github.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI6
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/goburrow/cache v0.1.4 h1:As4KzO3hgmzPlnaMniZU9+VmoNYseUhuELbxy9mRBfw=
github.com/goburrow/cache v0.1.4/go.mod h1:cDFesZDnIlrHoNlMYqqMpCRawuXulgx+y7mXU8HZ+/c=
github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=
@@ -497,8 +459,8 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
github.com/google/certificate-transparency-go v1.3.2 h1:9ahSNZF2o7SYMaKaXhAumVEzXB2QaayzII9C8rv7v+A=
github.com/google/certificate-transparency-go v1.3.2/go.mod h1:H5FpMUaGa5Ab2+KCYsxg6sELw3Flkl7pGZzWdBoYLXs=
github.com/google/certificate-transparency-go v1.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ=
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -509,6 +471,7 @@ github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
@@ -523,8 +486,6 @@ github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO
github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8=
github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
@@ -550,24 +511,22 @@ github.com/gorilla/css v1.0.1 h1:ntNaBIghp6JmvWnxbZKANoLyuXTPZ4cAMlo6RyhlbO8=
github.com/gorilla/css v1.0.1/go.mod h1:BvnYkspnSzMmwRK+b8/xgNPLiIuNZr6vbZBTPQ2A3b0=
github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4=
github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM=
github.com/gosimple/slug v1.15.0 h1:wRZHsRrRcs6b0XnxMUBM6WK1U1Vg5B0R7VkIf1Xzobo=
github.com/gosimple/slug v1.15.0/go.mod h1:UiRaFH+GEilHstLUmcBgWcI42viBN7mAb818JrYOeFQ=
github.com/gosimple/unidecode v1.0.1 h1:hZzFTMMqSswvf0LBJZCZgThIZrpDHFXux9KeGmn6T/o=
github.com/gosimple/unidecode v1.0.1/go.mod h1:CP0Cr1Y1kogOtx0bJblKzsVWrqYaqfNOnHzpgWw4Awc=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI=
github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
github.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=
github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-uuid v1.0.3 h1:2gKiV6YVmrJ1i2CKKa9obLvRieoRGviZFL26PcT/Co8=
github.com/hashicorp/go-uuid v1.0.3/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
github.com/hashicorp/go-version v1.5.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
@@ -581,8 +540,6 @@ github.com/hdm/jarm-go v0.0.7/go.mod h1:kinGoS0+Sdn1Rr54OtanET5E5n7AlD6T6CrJAKDj
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/iangcarroll/cookiemonster v1.6.0 h1:NPFkn/ZZYZgzXhJ1awRnYhZ3fJK3hKWgbctfTW21kew=
github.com/iangcarroll/cookiemonster v1.6.0/go.mod h1:n3MvoAq56NkNyCEyhcYs3ZJMzTc9rL3w7IaITI0apMg=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/ianlancetaylor/demangle v0.0.0-20230524184225-eabc099b10ab/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw=
github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA=
@@ -630,10 +587,6 @@ github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfV
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/k14s/difflib v0.0.0-20201117154628-0c031775bf57 h1:CwBRArr+BWBopnUJhDjJw86rPL/jGbEjfHWKzTasSqE=
github.com/k14s/difflib v0.0.0-20201117154628-0c031775bf57/go.mod h1:B0xN2MiNBGWOWi9CcfAo9LBI8IU4J1utlbOIJCsmKr4=
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 h1:4bcRTTSx+LKSxMWibIwzHnDNmaN1x52oEpvnjCy+8vk=
github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368/go.mod h1:lKGj1op99m4GtQISxoD2t+K+WO/q2NzEPKvfXFQfbCA=
github.com/kataras/jwt v0.1.10 h1:GBXOF9RVInDPhCFBiDumRG9Tt27l7ugLeLo8HL5SeKQ=
github.com/kataras/jwt v0.1.10/go.mod h1:xkimAtDhU/aGlQqjwvgtg+VyuPwMiyZHaY8LJRh0mYo=
github.com/kevinburke/ssh_config v1.2.0 h1:x584FjTGwHzMwvHx18PXxbBVzfnxogHaAReU4gf13a4=
@@ -645,8 +598,8 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
github.com/kitabisa/go-ci v1.0.3 h1:JmIUIvcercRQc/9x/v02ydCCqU4MadSHaNaOF8T2pGA=
github.com/kitabisa/go-ci v1.0.3/go.mod h1:e3wBSzaJbcifXrr/Gw2ZBLn44MmeqP5WySwXyHlCK/U=
github.com/klauspost/compress v1.4.1/go.mod h1:RyIbtBH6LamlWaDj8nUwkbUhJ87Yi3uG0guNDohfE1A=
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
@@ -685,17 +638,14 @@ github.com/logrusorgru/aurora v2.0.3+incompatible h1:tOpm7WcpBTn4fjmVfgpQq0EfczG
github.com/logrusorgru/aurora v2.0.3+incompatible/go.mod h1:7rIyQOR62GCctdiQpZ/zOJlFyk6y+94wXzv6RNZgaR4=
github.com/logrusorgru/aurora/v4 v4.0.0 h1:sRjfPpun/63iADiSvGGjgA1cAYegEWMPCJdUpJYn9JA=
github.com/logrusorgru/aurora/v4 v4.0.0/go.mod h1:lP0iIa2nrnT/qoFXcOZSrZQpJ1o6n2CUf/hyHi2Q4ZQ=
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 h1:wIONC+HMNRqmWBjuMxhatuSzHaljStc4gjDeKycxy0A=
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
github.com/lor00x/goldap v0.0.0-20240304151906-8d785c64d1c8 h1:z9RDOBcFcf3f2hSfKuoM3/FmJpt8M+w0fOy4wKneBmc=
github.com/lor00x/goldap v0.0.0-20240304151906-8d785c64d1c8/go.mod h1:37YR9jabpiIxsb8X9VCIx8qFOjTDIIrIHHODa8C4gz0=
github.com/lucasb-eyer/go-colorful v1.3.0 h1:2/yBRLdWBZKrf7gB40FoiKfAWYQ0lqNcbuQwVHXptag=
github.com/lucasb-eyer/go-colorful v1.3.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20250821153705-5981dea3221d h1:vFzYZc8yji+9DmNRhpEbs8VBK4CgV/DPfGzeVJSSp/8=
github.com/lufia/plan9stats v0.0.0-20250821153705-5981dea3221d/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg=
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/mackerelio/go-osstat v0.2.4 h1:qxGbdPkFo65PXOb/F/nhDKpF2nGmGaCFDLXoZjJTtUs=
github.com/mackerelio/go-osstat v0.2.4/go.mod h1:Zy+qzGdZs3A9cuIqmgbJvwbmLQH9dJvtio5ZjJTbdlQ=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
@@ -709,24 +659,22 @@ github.com/mattn/go-sqlite3 v1.14.28 h1:ThEiQrnbtumT+QMknw63Befp/ce/nUPgBPMlRFEu
github.com/mattn/go-sqlite3 v1.14.28/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4=
github.com/maypok86/otter/v2 v2.2.1 h1:hnGssisMFkdisYcvQ8L019zpYQcdtPse+g0ps2i7cfI=
github.com/maypok86/otter/v2 v2.2.1/go.mod h1:1NKY9bY+kB5jwCXBJfE59u+zAwOt6C7ni1FTlFFMqVs=
github.com/mholt/acmez v1.2.0 h1:1hhLxSgY5FvH5HCnGUuwbKY2VQVo8IU7rxXKSnZ7F30=
github.com/mholt/acmez v1.2.0/go.mod h1:VT9YwH1xgNX1kmYY89gY8xPJC84BFAisjo8Egigt4kE=
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
github.com/mholt/archives v0.1.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
github.com/microcosm-cc/bluemonday v1.0.27 h1:MpEUotklkwCSLeH+Qdx1VJgNqLlpY2KXwXFM08ygZfk=
github.com/microcosm-cc/bluemonday v1.0.27/go.mod h1:jFi9vgW+H7c3V0lb6nR74Ib/DIB5OBs92Dimizgw2cA=
github.com/microsoft/go-mssqldb v1.9.2 h1:nY8TmFMQOHpm2qVWo6y4I2mAmVdZqlGiMGAYt64Ibbs=
github.com/microsoft/go-mssqldb v1.9.2/go.mod h1:GBbW9ASTiDC+mpgWDGKdm3FnFLTUsLYN3iFL90lQ+PA=
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
github.com/miekg/dns v1.1.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
github.com/mikelolasagasti/xz v1.0.1 h1:Q2F2jX0RYJUG3+WsM+FJknv+6eVjsjXNDV0KJXZzkD0=
github.com/mikelolasagasti/xz v1.0.1/go.mod h1:muAirjiOUxPRXwm9HdDtB3uoRPrGnL85XHtokL9Hcgc=
github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
github.com/minio/minlz v1.0.1 h1:OUZUzXcib8diiX+JYxyRLIdomyZYzHct6EShOKtQY2A=
github.com/minio/minlz v1.0.1/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/minio/minlz v1.0.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc=
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
github.com/minio/sha256-simd v0.1.1/go.mod h1:B5e1o+1/KgNmWrSQK08Y6Z1Vb5pwIktudl0J58iy0KM=
@@ -734,20 +682,10 @@ github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=
github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=
github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ=
github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo=
github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw=
github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs=
github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU=
github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko=
github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs=
github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=
github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=
github.com/moby/sys/user v0.3.0 h1:9ni5DlcW5an3SvRSx4MouotOygvzaXbaSrc/wGDFWPo=
github.com/moby/sys/user v0.3.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs=
github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=
github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
@@ -759,8 +697,6 @@ github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8=
github.com/montanaflynn/stats v0.7.1 h1:etflOAAHORrCC44V+aR6Ftzort912ZU+YLiSTuV8eaE=
github.com/montanaflynn/stats v0.7.1/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=
github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/mreiferson/go-httpclient v0.0.0-20201222173833-5e475fde3a4d/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
@@ -769,8 +705,8 @@ github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/nwaples/rardecode/v2 v2.2.1 h1:DgHK/O/fkTQEKBJxBMC5d9IU8IgauifbpG78+rZJMnI=
github.com/nwaples/rardecode/v2 v2.2.1/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/nwaples/rardecode/v2 v2.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oasdiff/yaml v0.0.0-20250309154309-f31be36b4037 h1:G7ERwszslrBzRxj//JalHPu/3yz+De2J+4aLtSRlHiY=
@@ -793,10 +729,10 @@ github.com/onsi/gomega v1.34.1/go.mod h1:kU1QgUvBDLXBJq618Xvm2LUX6rSAfRaFRTcdOeD
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=
github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=
github.com/opencontainers/runc v1.2.8 h1:RnEICeDReapbZ5lZEgHvj7E9Q3Eex9toYmaGBsbvU5Q=
github.com/opencontainers/runc v1.2.8/go.mod h1:cC0YkmZcuvr+rtBZ6T7NBoVbMGNAdLa/21vIElJDOzI=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/opencontainers/runc v1.2.3 h1:fxE7amCzfZflJO2lHXf4y/y8M1BoAqp+FVmG19oYB80=
github.com/opencontainers/runc v1.2.3/go.mod h1:nSxcWUydXrsBZVYNSkTjoQ/N6rcyTtn+1SD5D4+kRIM=
github.com/openrdap/rdap v0.9.1 h1:Rv6YbanbiVPsKRvOLdUmlU1AL5+2OFuEFLjFN+mQsCM=
github.com/openrdap/rdap v0.9.1/go.mod h1:vKSiotbsENrjM/vaHXLddXbW8iQkBfa+ldEuYEjyLTQ=
github.com/orisano/pixelmatch v0.0.0-20220722002657-fb0b55479cde/go.mod h1:nZgzbfBr3hhjoZnS66nKrHmduYNpc34ny7RK4z5/HM0=
@@ -806,8 +742,8 @@ github.com/pelletier/go-toml/v2 v2.0.8 h1:0ctb6s9mE31h0/lhu+J6OPmVeDxJn+kYnJc2jZ
github.com/pelletier/go-toml/v2 v2.0.8/go.mod h1:vuYfssBdrU2XDZ9bYydBu6t+6a6PYNcZljzZR9VXg+4=
github.com/perimeterx/marshmallow v1.1.5 h1:a2LALqQ1BlHM8PZblsDdidgv1mWi1DgC2UmX50IvK2s=
github.com/perimeterx/marshmallow v1.1.5/go.mod h1:dsXbUu8CRzfYP5a87xpp0xq9S3u0Vchtcl8we9tYaXw=
github.com/pierrec/lz4/v4 v4.1.22 h1:cKFw6uJDK+/gfw5BcDL0JL5aBsAFdsIT18eRtLj7VIU=
github.com/pierrec/lz4/v4 v4.1.22/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pierrec/lz4/v4 v4.1.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4=
github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
@@ -819,22 +755,22 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU=
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/praetorian-inc/fingerprintx v1.1.15 h1:CVIxrIQARbmdk5h8E9tIJZbvFoY2sGLLG9rpFVfQqpA=
github.com/praetorian-inc/fingerprintx v1.1.15/go.mod h1:hqRroITBwKpP8BOGF+n/A+qv9wSF7OSVinmu5NCyOUI=
github.com/projectdiscovery/asnmap v1.1.1 h1:ImJiKIaACOT7HPx4Pabb5dksolzaFYsD1kID2iwsDqI=
github.com/projectdiscovery/asnmap v1.1.1/go.mod h1:QT7jt9nQanj+Ucjr9BqGr1Q2veCCKSAVyUzLXfEcQ60=
github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k0VbGJyft6LQ=
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
github.com/projectdiscovery/cdncheck v1.2.16 h1:m6sQh5VAWN2sVp3o8LSFtAfnv3V0Dpoqqw83ZwnrA2c=
github.com/projectdiscovery/cdncheck v1.2.16/go.mod h1:2OsjLn4x7VsM+ZNccGU185pd43U+0h5gDDStU0GtNl8=
github.com/projectdiscovery/cdncheck v1.1.26 h1:R6JxzU3ptGNrzzvS261xHWssDt9GlOCWXkuet1huOyA=
github.com/projectdiscovery/cdncheck v1.1.26/go.mod h1:dFEGsG0qAJY0AaRr2N1BY0OtZiTxS4kYeT5+OkF8t1U=
github.com/projectdiscovery/clistats v0.1.1 h1:8mwbdbwTU4aT88TJvwIzTpiNeow3XnAB72JIg66c8wE=
github.com/projectdiscovery/clistats v0.1.1/go.mod h1:4LtTC9Oy//RiuT1+76MfTg8Hqs7FQp1JIGBM3nHK6a0=
github.com/projectdiscovery/dsl v0.8.10 h1:7t8KMukLlNGiOPdESGCkpOxu9RAT3RLRBY0Z5CoZIgs=
github.com/projectdiscovery/dsl v0.8.10/go.mod h1:66WXaiVEOA2LlZuH81/izzybA3s++zX/nrKwgQV/2S0=
github.com/projectdiscovery/fastdialer v0.5.1 h1:/4PX1u80QfZ8+DdF9jdtz1in5eajJhw89/xouVc9/+c=
github.com/projectdiscovery/fastdialer v0.5.1/go.mod h1:34SS9VxrrmUhO67LYioxjrAWOQ5/kwgJNj9CiFgRJso=
github.com/projectdiscovery/dsl v0.5.0 h1:3HHY14FNmdwWXq3pi9dd8JjUHQzskZjLD/pZKVx5Vi4=
github.com/projectdiscovery/dsl v0.5.0/go.mod h1:Fr+zIQJfMNy+RTj5KFgozfvDaiQQEKMyrKXl75aGgxY=
github.com/projectdiscovery/fastdialer v0.4.4 h1:QeVbOnTMPhc/IOkkj2AP2q9hu5E1oCBPiLwEvPSR6A8=
github.com/projectdiscovery/fastdialer v0.4.4/go.mod h1:a0FNUOcmW6g6JhjaJ2+YkCpFFkQeCbetq/d9Zo4G3rQ=
github.com/projectdiscovery/fasttemplate v0.0.2 h1:h2cISk5xDhlJEinlBQS6RRx0vOlOirB2y3Yu4PJzpiA=
github.com/projectdiscovery/fasttemplate v0.0.2/go.mod h1:XYWWVMxnItd+r0GbjA1GCsUopMw1/XusuQxdyAIHMCw=
github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk=
@@ -845,52 +781,58 @@ github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb h1:rutG90
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb/go.mod h1:FLjF1DmZ+POoGEiIQdWuYVwS++C/GwpX8YaCsTSm1RY=
github.com/projectdiscovery/goflags v0.1.74 h1:n85uTRj5qMosm0PFBfsvOL24I7TdWRcWq/1GynhXS7c=
github.com/projectdiscovery/goflags v0.1.74/go.mod h1:UMc9/7dFz2oln+10tv6cy+7WZKTHf9UGhaNkF95emh4=
github.com/projectdiscovery/gologger v1.1.66 h1:dYXfCWWNNiC7AgIDBkIF+3sTGyQbrpncsQEFQwyKCOg=
github.com/projectdiscovery/gologger v1.1.66/go.mod h1:lm3vFKt52lXgo799dBtySe/1zRN1lMGUQuNb5YtwkYQ=
github.com/projectdiscovery/gologger v1.1.54 h1:WMzvJ8j/4gGfPKpCttSTaYCVDU1MWQSJnk3wU8/U6Ws=
github.com/projectdiscovery/gologger v1.1.54/go.mod h1:vza/8pe2OKOt+ujFWncngknad1XWr8EnLKlbcejOyUE=
github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M=
github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81 h1:yHh46pJovYbyiaHCV7oIDinFmy+Fyq36H1BowJgb0M0=
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81/go.mod h1:9lmGPBDGZVANzCGjQg+V32n8Y3Cgjo/4kT0E88lsVTI=
github.com/projectdiscovery/hmap v0.0.99 h1:XPfLnD3CUrMqVCIdpK9ozD7Xmp3simx3T+2j4WWhHnU=
github.com/projectdiscovery/hmap v0.0.99/go.mod h1:koyUJi83K5G3w35ZLFXOYZIyYJsO+6hQrgDDN1RBrVE=
github.com/projectdiscovery/httpx v1.7.4 h1:GUEklAZ71VKM0krmgck2Km5odJi5dwfo0euc2r88tj0=
github.com/projectdiscovery/httpx v1.7.4/go.mod h1:sPnFYIXh0RAdjb02vpUGOsP5j28VDZs5khjMbXwEUGQ=
github.com/projectdiscovery/gozero v0.0.3 h1:tsYkrSvWw4WdIUJyisd4MB1vRiw1X57TuVVk3p8Z3G8=
github.com/projectdiscovery/gozero v0.0.3/go.mod h1:MpJ37Dsh94gy2EKqaemdeh+CzduGVB2SDfhr6Upsjew=
github.com/projectdiscovery/hmap v0.0.92 h1:NHGDFVJzc7b8bDuIjss7SxX5LJla9py/RxC4lri+uBo=
github.com/projectdiscovery/hmap v0.0.92/go.mod h1:IKezuOhPPwrmPusmhLuwuGsD8+fniyy6jx4gFTOZOnI=
github.com/projectdiscovery/httpx v1.7.0 h1:s7ZgF2ZAt9pQYkBuY5F6SjyNYbYrl7eiIzOm/6hBZ/E=
github.com/projectdiscovery/httpx v1.7.0/go.mod h1:D0HLzSy+/G6w01FtRORGyfwVwWlUPAFIc8wyEj/lcVg=
github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/IyenyRBhkI5jiqI=
github.com/projectdiscovery/interactsh v1.2.4/go.mod h1:E/IVNZ80/WKz8zTwGJWQygxIbhlRmuzZFsZwcGSZTdc=
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb/go.mod h1:vmgC0DTFCfoCLp0RAfsfYTZZan0QMVs+cmTbH6blfjk=
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582 h1:eR+0HE//Ciyfwy3HC7fjRyKShSJHYoX2Pv7pPshjK/Q=
github.com/projectdiscovery/machineid v0.0.0-20250715113114-c77eb3567582/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8QuWs2puwy+2w=
github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA=
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE=
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM=
github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ=
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
github.com/projectdiscovery/networkpolicy v0.1.33 h1:bVgp+XpLEsQ7ZEJt3UaUqIwhI01MMdt7F2dfIKFQg/w=
github.com/projectdiscovery/networkpolicy v0.1.33/go.mod h1:YAPddAXUc/lhoU85AFdvgOQKx8Qh8r0vzSjexRWk6Yk=
github.com/projectdiscovery/ratelimit v0.0.82 h1:rtO5SQf5uQFu5zTahTaTcO06OxmG8EIF1qhdFPIyTak=
github.com/projectdiscovery/ratelimit v0.0.82/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
github.com/projectdiscovery/networkpolicy v0.1.18 h1:DAeP73SvcuT4evaohNS7BPELw+VtvcVt4PaTK3fC1qA=
github.com/projectdiscovery/networkpolicy v0.1.18/go.mod h1:2yWanKsU2oBZ75ch94IsEQy6hByFp+3oTiSyC6ew3TE=
github.com/projectdiscovery/ratelimit v0.0.81 h1:u6lW+rAhS/UO0amHTYmYLipPK8NEotA9521hdojBtgI=
github.com/projectdiscovery/ratelimit v0.0.81/go.mod h1:tK04WXHuC4i6AsFkByInODSNf45gd9sfaMHzmy2bAsA=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819014026-b6ac32486d12 h1:oS/0Oe6tv7SG2zoFBofwvoB3mfDb5Yk76np4zU0Mcg8=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819014026-b6ac32486d12/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819164203-5e0837ee74a5 h1:tulP7aVzZsWlXbWo6W78nVFqJ05c6zSS8dsos/cBaSY=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819164203-5e0837ee74a5/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819165347-d56e782c656d h1:zUptoE5gh4gyJsbcHfNUEKgdoieyAHW2hCfWXZ/Zye4=
github.com/projectdiscovery/ratelimit v0.0.82-0.20250819165347-d56e782c656d/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
github.com/projectdiscovery/rawhttp v0.1.90 h1:LOSZ6PUH08tnKmWsIwvwv1Z/4zkiYKYOSZ6n+8RFKtw=
github.com/projectdiscovery/rawhttp v0.1.90/go.mod h1:VZYAM25UI/wVB3URZ95ZaftgOnsbphxyAw/XnQRRz4Y=
github.com/projectdiscovery/rdap v0.9.0 h1:wPhHx5pQ2QI+WGhyNb2PjhTl0NtB39Nk7YFZ9cp8ZGA=
github.com/projectdiscovery/rdap v0.9.0/go.mod h1:zk4yrJFQ2Hy36Aqk+DvotYQxYAeALaCJ5ORySkff36Q=
github.com/projectdiscovery/retryabledns v1.0.112 h1:4iCiuo6jMnw/pdOZRzBQrbUOUu5tOeuvGupxVV8RDLw=
github.com/projectdiscovery/retryabledns v1.0.112/go.mod h1:xsJTKbo+KGqd7+88z1naEUFJybLH2yjB/zUyOweA7k0=
github.com/projectdiscovery/retryablehttp-go v1.3.1 h1:ds0dcKa3565pYdIJrLcwcbrb9cH6MojY2uHwGPoXubg=
github.com/projectdiscovery/retryablehttp-go v1.3.1/go.mod h1:ISMHtd5DrSSDenQ23Vz9cIyIj3XlccZzZhuWHUJTa60=
github.com/projectdiscovery/retryabledns v1.0.105 h1:G8ln01igkNTQ5xvMY5K4cx5XIfKGTwGH6aZxWxBKMqc=
github.com/projectdiscovery/retryabledns v1.0.105/go.mod h1:3EZKhRL1rokqYR4q5qKK1eLBEe8mSzgtzkMOJilO1Ok=
github.com/projectdiscovery/retryablehttp-go v1.0.119 h1:Lpjb6gCWpIvCCX8GultM8zlaQEmFOci1dS33k9Ll4gw=
github.com/projectdiscovery/retryablehttp-go v1.0.119/go.mod h1:x29gqkLERRzw0znJDu5ORhphBaVin8FtK0+jCvCx4os=
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
github.com/projectdiscovery/tlsx v1.2.2 h1:Y96QBqeD2anpzEtBl4kqNbwzXh2TrzJuXfgiBLvK+SE=
github.com/projectdiscovery/tlsx v1.2.2/go.mod h1:ZJl9F1sSl0sdwE+lR0yuNHVX4Zx6tCSTqnNxnHCFZB4=
github.com/projectdiscovery/uncover v1.2.0 h1:31tjYa0v8FB8Ch8hJTxb+2t63vsljdOo0OSFylJcX4M=
github.com/projectdiscovery/uncover v1.2.0/go.mod h1:ozqKb++p39Kmh1SmwIpbQ9p0aVGPXuwsb4/X2Kvx6ms=
github.com/projectdiscovery/useragent v0.0.106 h1:9fS08MRUUJvfBskTxcXY9TA4X1TwpH6iJ3P3YNaXNlo=
github.com/projectdiscovery/useragent v0.0.106/go.mod h1:9oVMjgd7CchIsyeweyigIPtW83gpiGf2NtR6UM5XK+o=
github.com/projectdiscovery/utils v0.8.0 h1:8d79OCs5xGDNXdKxMUKMY/lgQSUWJMYB1B2Sx+oiqkQ=
github.com/projectdiscovery/utils v0.8.0/go.mod h1:CU6tjtyTRxBrnNek+GPJplw4IIHcXNZNKO09kWgqTdg=
github.com/projectdiscovery/wappalyzergo v0.2.61 h1:TxiYJvXqReiscuWKtGKhFx3VxbVVjHOgECNX709AEX4=
github.com/projectdiscovery/wappalyzergo v0.2.61/go.mod h1:8FtSVcmPRZU0g1euBpdSYEBHIvB7Zz9MOb754ZqZmfU=
github.com/projectdiscovery/tlsx v1.1.9 h1:DhErhHCO2+toF5DEX7qe3pkwrIrYlex3F/mzHEUiZHg=
github.com/projectdiscovery/tlsx v1.1.9/go.mod h1:Jy+r38WyYjapQWaffiKGdYm4Ksdrt8BWtsqA2rUospU=
github.com/projectdiscovery/uncover v1.1.0 h1:UDp/qLZn78YZb6VPoOrfyP1vz+ojEx8VrTTyjjRt9UU=
github.com/projectdiscovery/uncover v1.1.0/go.mod h1:2rXINmMe/lmVAt2jn9CpAOs9An57/JEeLZobY3Z9kUs=
github.com/projectdiscovery/useragent v0.0.101 h1:8A+XOJ/nIH+WqW8ogLxJ/psemGp8ATQ2/GuKroJ/81E=
github.com/projectdiscovery/useragent v0.0.101/go.mod h1:RGoRw1BQ/lJnhYMbMpEKjyAAgCaDCr/+GsULo5yEJ2I=
github.com/projectdiscovery/utils v0.4.23 h1:fi6AVPIh2laomWO+Yy6G8YhvM4c2fDmQ/Viio6VZgyw=
github.com/projectdiscovery/utils v0.4.23/go.mod h1:2K2ymMPnp4/Zao5QulCDJzKjxdyZPsucQm6Fyo09JlA=
github.com/projectdiscovery/wappalyzergo v0.2.36 h1:g/E2gatdYcmLKk9R81vrkq4RdpACpYgN1fuyY3041eE=
github.com/projectdiscovery/wappalyzergo v0.2.36/go.mod h1:L4P6SZuaEgEE2eXbpf4OnSGxjWj9vn6xM15SD78niLA=
github.com/projectdiscovery/yamldoc-go v1.0.6 h1:GCEdIRlQjDux28xTXKszM7n3jlMf152d5nqVpVoetas=
github.com/projectdiscovery/yamldoc-go v1.0.6/go.mod h1:R5lWrNzP+7Oyn77NDVPnBsxx2/FyQZBBkIAaSaCQFxw=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
@@ -947,16 +889,12 @@ github.com/shirou/gopsutil v3.21.11+incompatible h1:+1+c1VGhc88SSonWP6foOcLhvnKl
github.com/shirou/gopsutil v3.21.11+incompatible/go.mod h1:5b4v6he4MtMOwMlS0TUMTu2PcXUg8+E1lC7eC3UO/RA=
github.com/shirou/gopsutil/v3 v3.24.5 h1:i0t8kL+kQTvpAYToeuiVk3TgDeKOFioZO3Ztz/iZ9pI=
github.com/shirou/gopsutil/v3 v3.24.5/go.mod h1:bsoOS1aStSs9ErQ1WWfxllSeS1K5D+U30r2NfcubMVk=
github.com/shirou/gopsutil/v4 v4.25.7 h1:bNb2JuqKuAu3tRlPv5piSmBZyMfecwQ+t/ILq+1JqVM=
github.com/shirou/gopsutil/v4 v4.25.7/go.mod h1:XV/egmwJtd3ZQjBpJVY5kndsiOO4IRqy9TQnmm6VP7U=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466 h1:17JxqqJY66GmZVHkmAsGEkcIu0oCe3AM420QDgGwZx0=
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466/go.mod h1:9dIRpgIY7hVhoqfe0/FcYp0bpInZaT7dc3BYOprrIUE=
github.com/sijms/go-ora/v2 v2.9.0 h1:+iQbUeTeCOFMb5BsOMgUhV8KWyrv9yjKpcK4x7+MFrg=
github.com/sijms/go-ora/v2 v2.9.0/go.mod h1:QgFInVi3ZWyqAiJwzBQA+nbKYKH77tdp1PYoCqhR2dU=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
@@ -972,41 +910,31 @@ github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1
github.com/smartystreets/goconvey v0.0.0-20190330032615-68dc04aab96a/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s=
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
github.com/sorairolake/lzip-go v0.3.8 h1:j5Q2313INdTA80ureWYRhX+1K78mUXfMoPZCw/ivWik=
github.com/sorairolake/lzip-go v0.3.8/go.mod h1:JcBqGMV0frlxwrsE9sMWXDjqn3EeVf0/54YPsw66qkU=
github.com/sorairolake/lzip-go v0.3.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI=
github.com/spaolacci/murmur3 v1.1.0/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
github.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=
github.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=
github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE=
github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.3/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9 h1:GXIyLuIJ5Qk46lI8WJ83qHBZKUI3zhmMmuoY9HICUIQ=
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9/go.mod h1:uQdBQGrE1fZ2EyOs0pLcCDd1bBV4rSThieuIIGhXZ50=
github.com/testcontainers/testcontainers-go v0.38.0 h1:d7uEapLcv2P8AvH8ahLqDMMxda2W9gQN1nRbHS28HBw=
github.com/testcontainers/testcontainers-go v0.38.0/go.mod h1:C52c9MoHpWO+C4aqmgSU+hxlR5jlEayWtgYrb8Pzz1w=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.37.0 h1:drGy4LJOVkIKpKGm1YKTfVzb1qRhN/konVpmuUphq0k=
github.com/testcontainers/testcontainers-go/modules/mongodb v0.37.0/go.mod h1:e9/4dGJfSZW59/kXGf/ksrEvA+BqP/daax0Usp2cpsM=
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
@@ -1020,9 +948,8 @@ github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
github.com/tidwall/lotsa v1.0.2/go.mod h1:X6NiU+4yHA3fE3Puvpnn1XMDrFZrE9JO2/w+UMuqgR8=
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/match v1.2.0 h1:0pt8FlkOwjN2fPt4bIl4BoNxb98gGHN2ObFEDkrfZnM=
github.com/tidwall/match v1.2.0/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
github.com/tidwall/pretty v1.2.1 h1:qjsOFOWWQl+N3RsoF5/ssm1pHmJJwhjlSbZ51I6wMl4=
github.com/tidwall/pretty v1.2.1/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
@@ -1032,12 +959,10 @@ github.com/tidwall/tinyqueue v0.1.1 h1:SpNEvEggbpyN5DIReaJ2/1ndroY8iyEGxPYxoSaym
github.com/tidwall/tinyqueue v0.1.1/go.mod h1:O/QNHwrnjqr6IHItYrzoHAKYhBkLI67Q096fQP5zMYw=
github.com/tim-ywliu/nested-logrus-formatter v1.3.2 h1:jugNJ2/CNCI79SxOJCOhwUHeN3O7/7/bj+ZRGOFlCSw=
github.com/tim-ywliu/nested-logrus-formatter v1.3.2/go.mod h1:oGPmcxZB65j9Wo7mCnQKSrKEJtVDqyjD666SGmyStXI=
github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4=
github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4=
github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso=
github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc h1:9lRDQMhESg+zvGYmW5DyG0UqvY96Bu5QYsTLvCHdrgo=
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc/go.mod h1:bciPuU6GHm1iF1pBvUfxfsH0Wmnc2VbpgvbI9ZWuIRs=
github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=
github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=
github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=
github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=
github.com/trivago/tgo v1.0.7 h1:uaWH/XIy9aWYWpjm2CU3RpcqZXmX2ysQ9/Go+d9gyrM=
github.com/trivago/tgo v1.0.7/go.mod h1:w4dpD+3tzNIIiIfkWWa85w5/B77tlvdZckQ+6PkFnhc=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
@@ -1047,28 +972,18 @@ github.com/twmb/murmur3 v1.1.6/go.mod h1:Qq/R7NUyOfr65zD+6Q5IHKsJLwP7exErjN6lyyq
github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4dU=
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY=
github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc=
github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo=
github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ=
github.com/vmihailenco/bufpool v0.1.11 h1:gOq2WmBrq0i2yW5QJ16ykccQ4wH9UyEsgLm6czKAd94=
github.com/vmihailenco/bufpool v0.1.11/go.mod h1:AFf/MOy3l2CFTKbxwt0mp2MwnqjNEs5H/UxrkA5jxTQ=
github.com/vmihailenco/msgpack/v5 v5.3.4 h1:qMKAwOV+meBw2Y8k9cVwAy7qErtYCwBzZ2ellBfvnqc=
github.com/vmihailenco/msgpack/v5 v5.3.4/go.mod h1:7xyJ9e+0+9SaZT0Wt1RGleJXzli6Q/V5KbhBonMG9jc=
github.com/vmihailenco/tagparser v0.1.2 h1:gnjoVuB/kljJ5wICEEOpx98oXMWPLj22G67Vbd1qPqc=
github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
github.com/vulncheck-oss/go-exploit v1.51.0 h1:HTmJ4Q94tbEDPb35mQZn6qMg4rT+Sw9n+L7g3Pjr+3o=
github.com/vulncheck-oss/go-exploit v1.51.0/go.mod h1:J28w0dLnA6DnCrnBm9Sbt6smX8lvztnnN2wCXy7No6c=
github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY=
github.com/weppos/publicsuffix-go v0.30.2/go.mod h1:/hGscit36Yt+wammfBBwdMdxBT8btsTt6KvwO9OvMyM=
github.com/weppos/publicsuffix-go v0.50.1 h1:elrBHeSkS/eIb169+DnLrknqmdP4AjT0Q0tEdytz1Og=
github.com/weppos/publicsuffix-go v0.50.1/go.mod h1:znn0JVXjcR5hpUl9pbEogwH6I710rA1AX0QQPT0bf+k=
github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39 h1:Bz/zVM/LoGZ9IztGBHrq2zlFQQbEG8dBYnxb4hamIHM=
github.com/weppos/publicsuffix-go v0.40.3-0.20250408071509-6074bbe7fd39/go.mod h1:2oFzEwGYI7lhiqG0YkkcKa6VcpjVinQbWxaPzytDmLA=
github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
@@ -1121,8 +1036,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark v1.7.13 h1:GPddIs617DnBLFFVJFgpo1aBfe/4xcvMc3SB5t/D0pA=
github.com/yuin/goldmark v1.7.13/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
github.com/yuin/goldmark v1.7.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
github.com/yuin/goldmark v1.7.8/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
github.com/yuin/goldmark-emoji v1.0.5 h1:EMVWyCGPlXJfUXBXpuMu+ii3TIaxbVBnEX9uaDC4cIk=
github.com/yuin/goldmark-emoji v1.0.5/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
@@ -1151,8 +1066,8 @@ github.com/zmap/zgrab2 v0.1.8/go.mod h1:5d8HSmUwvllx4q1qG50v/KXphkg45ZzWdaQtgTFn
github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8=
gitlab.com/gitlab-org/api/client-go v0.130.1 h1:1xF5C5Zq3sFeNg3PzS2z63oqrxifne3n/OnbI7nptRc=
gitlab.com/gitlab-org/api/client-go v0.130.1/go.mod h1:ZhSxLAWadqP6J9lMh40IAZOlOxBLPRh7yFOXR/bMJWM=
go.etcd.io/bbolt v1.4.0 h1:TU77id3TnN/zKr7CO/uk+fBCwF2jGcMuw2B/FMAzYIk=
go.etcd.io/bbolt v1.4.0/go.mod h1:AsD+OCi/qPN1giOX1aiLAha3o1U8rAz65bvN4j0sRuk=
go.etcd.io/bbolt v1.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
go.mongodb.org/mongo-driver v1.17.4 h1:jUorfmVzljjr0FLzYQsGP8cgN/qzzxlY9Vh0C9KFXVw=
go.mongodb.org/mongo-driver v1.17.4/go.mod h1:Hy04i7O2kC4RS06ZrhPRqj/u4DTYkFDAAccj+rVKqgQ=
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
@@ -1160,32 +1075,12 @@ go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0 h1:Hf9xI/XLML9ElpiHVDNwvqI0hIFlzV8dgIr35kV1kRU=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.62.0/go.mod h1:NfchwuyNoMcZ5MLHwPrODwUF1HWCXWrL31s8gSAdIKY=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0 h1:OeNbIYk/2C15ckl7glBlOBp5+WlYsOElzTNmiPW/x60=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.34.0/go.mod h1:7Bept48yIeqxP2OZ9/AqIpYS94h2or0aB4FypJTc8ZM=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4=
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
go.opentelemetry.io/otel/sdk/metric v1.37.0 h1:90lI228XrB9jCMuSdA0673aubgRobVZFhbjxHHspCPc=
go.opentelemetry.io/otel/sdk/metric v1.37.0/go.mod h1:cNen4ZWfiD37l5NhS+Keb5RXVWZWpRE+9WyVCpbo5ps=
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
go.opentelemetry.io/proto/otlp v1.8.0 h1:fRAZQDcAFHySxpJ1TwlA1cJ4tvcrw7nXl9xWWC8N5CE=
go.opentelemetry.io/proto/otlp v1.8.0/go.mod h1:tIeYOeNBU4cvmPqpaji1P+KbB4Oloai8wN4rWzRrFF0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
go.uber.org/zap v1.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
go4.org v0.0.0-20230225012048-214862532bf5 h1:nifaUDeh+rPaBCMPMQHZmvJf+QdpLFnuQPwx+LxVmtc=
go4.org v0.0.0-20230225012048-214862532bf5/go.mod h1:F57wTi5Lrj6WLyswp5EYV1ncrEbFGHD4hhz6S1ZYeaU=
goftp.io/server/v2 v2.0.1 h1:H+9UbCX2N206ePDSVNCjBftOKOgil6kQ5RAQNx5hJwE=
@@ -1207,7 +1102,6 @@ golang.org/x/crypto v0.0.0-20210220033148-5ea612d1eb83/go.mod h1:jdWPYTVW3xRLrWP
golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a/go.mod h1:P+XmwS30IXTQdn5tA2iutPOUgjI07+tq3H3K9MVA1s8=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.0.0-20211209193657-4570a0811e8b/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
@@ -1216,8 +1110,8 @@ golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1228,8 +1122,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621 h1:2id6c1/gto0kaHYyrixvknJ8tUK/Qs5IsmBtrc+FtgU=
golang.org/x/exp v0.0.0-20250911091902-df9299821621/go.mod h1:TwQYMMnGpvZyc+JpB/UAuTNIsVJifOlSkrZkhcvpVUk=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b h1:M2rDM6z3Fhozi9O7NWsxAkg/yqS/lQJ6PmkyIV3YP+o=
golang.org/x/exp v0.0.0-20250620022241-b7579e27df2b/go.mod h1:3//PLf8L/X+8b4vuAfHzxeRUl04Adcb341+IGKfnqS8=
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
@@ -1255,8 +1149,8 @@ golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.30.0 h1:fDEXFVZ/fmCKProc/yAXXUijritrDzahmwwefnjoPFk=
golang.org/x/mod v0.30.0/go.mod h1:lAsf5O2EvJeSFMiBxXDki7sCgAxEUcZHXoXMKT4GJKc=
golang.org/x/mod v0.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -1307,8 +1201,8 @@ golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1337,8 +1231,8 @@ golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4=
golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -1355,7 +1249,6 @@ golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190924154521-2837fb4f24fe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -1391,7 +1284,6 @@ golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220615213510-4f61da869c0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -1399,13 +1291,14 @@ golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
@@ -1419,8 +1312,8 @@ golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
golang.org/x/term v0.38.0 h1:PQ5pkm/rLO6HnxFR7N2lJHOZX6Kez5Y1gDSJla6jo7Q=
golang.org/x/term v0.38.0/go.mod h1:bSEAKrOT1W+VSu9TSCMtoGEOUcKxOKgl3LE5QEF/xVg=
golang.org/x/term v0.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -1437,13 +1330,13 @@ golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=
golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -1492,8 +1385,8 @@ golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1550,11 +1443,6 @@ google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7Fc
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
google.golang.org/genproto v0.0.0-20250122153221-138b5a5a4fd4 h1:Pw6WnI9W/LIdRxqK7T6XGugGbHIRl5Q7q3BssH6xk4s=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb h1:p31xT4yrYrSM/G4Sn2+TNUkVhFCbG9y8itM2S6Th950=
google.golang.org/genproto/googleapis/api v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:jbe3Bkdp+Dh2IrslsFCklNhweNTBgSYanP1UXhJDhKg=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb h1:TLPQVbx1GJ8VKZxz52VAxl1EBgKXXbTiU9Fc5fZeLn4=
google.golang.org/genproto/googleapis/rpc v0.0.0-20250303144028-a0af3efb3deb/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
@@ -1567,8 +1455,6 @@ google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKa
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A=
google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -1585,8 +1471,8 @@ google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqw
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.30.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
@@ -1596,6 +1482,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntN
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 h1:K4u1NprbDNvKPczKfHLbwdOWHTZ0zfv2ow71H1nRnFU=
gopkg.in/corvus-ch/zbase32.v1 v1.0.0/go.mod h1:T3oKkPOm4AV/bNXCNFUxRmlE9RUyBz/DSo0nK9U+c0Y=
gopkg.in/djherbis/times.v1 v1.3.0 h1:uxMS4iMtH6Pwsxog094W0FYldiNnfY/xba00vq6C2+o=
gopkg.in/djherbis/times.v1 v1.3.0/go.mod h1:AQlg6unIsrsCEdQYhTzERy542dz6SFdQFZFv6mUY0P8=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/ini.v1 v1.42.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
@@ -1614,8 +1502,8 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
-38
View File
@@ -1,38 +0,0 @@
id: fuzz-body
info:
name: fuzzing error sqli payloads in http req body
author: pdteam
severity: info
description: |
This template attempts to find SQL injection vulnerabilities by fuzzing http body
It automatically handles and parses json,xml,multipart form and x-www-form-urlencoded data
and performs fuzzing on the value of every key
http:
- pre-condition:
- type: dsl
dsl:
- method != "GET"
- method != "HEAD"
condition: and
payloads:
injection:
- "'"
- "\""
- ";"
fuzzing:
- part: body
type: postfix
mode: single
fuzz:
- '{{injection}}'
stop-at-first-match: true
matchers:
- type: word
words:
- "unrecognized token:"
- "null"
@@ -1,27 +0,0 @@
id: pw-echo
info:
name: PowerShell Echo Test
author: pdteam
severity: info
description: Tests PowerShell execution with an echo-like operation.
tags: test,powershell,echo
self-contained: true
code:
- engine:
- pwsh
- powershell
- powershell.exe
args:
- -ExecutionPolicy
- Bypass
pattern: "*.ps1"
source: |
Write-Output "test-output-success"
matchers:
- type: word
words:
- "test-output-success"
@@ -1,27 +0,0 @@
id: signed-python-code-in-virtual-env
info:
name: signed-python-code-in-virtual-env
author: pdteam
severity: info
tags: code
description: |
signed python code in virtual environment
code:
- engine:
- sh
- bash
sandbox:
working-dir: /tmp
image: python:3.14
source: |
#!/usr/bin/env python3
import sys
print("hello from python virtual code")
matchers:
- type: word
words:
- "hello from python virtual code"
@@ -1,25 +0,0 @@
id: signed-code-in-virtual-env
info:
name: signed-code-in-virtual-env
author: pdteam
severity: info
tags: code
description: |
signed code in virtual environment
code:
- engine:
- sh
- bash
sandbox:
working-dir: /tmp
image: ubuntu:latest
source: |
echo "hello from sh virtual code"
matchers:
- type: word
words:
- "hello from sh virtual code"
# digest: 4a0a00473045022100a2a71c423b72e600ac2d78209482b48591924157d49e2dfb7767445b0073e92b022009a6365e84247e268c7ffcb2f9ed424529dada6f087d5f21400ab0b2a822ca56:4a3eb6b4988d95847d4203be25ed1d46
@@ -1,27 +0,0 @@
id: constants-with-threads
info:
name: Constants with Threads
author: pdteam
severity: info
description: |
Test that constants are properly resolved when using threads mode.
constants:
api_key: "supersecretkey123"
api_version: "v2"
http:
- method: GET
path:
- "{{BaseURL}}/api/{{api_version}}"
threads: 5
headers:
X-API-Key: "{{api_key}}"
matchers:
- type: word
words:
- "supersecretkey123"
- "v2"
condition: and
@@ -1,30 +0,0 @@
id: race-with-variables
info:
name: Race Condition with Variables
author: pdteam
severity: info
description: |
Test that variables and constants are properly resolved in race mode.
variables:
random_id: "{{rand_base(8)}}"
constants:
api_key: "racekey123"
http:
- raw:
- |
GET /race HTTP/1.1
Host: {{Hostname}}
X-Request-Id: {{random_id}}
X-API-Key: {{api_key}}
race: true
race_count: 3
matchers:
- type: word
words:
- "racekey123"
@@ -1,38 +0,0 @@
id: variables-threads-previous
info:
name: Variables with Threads and Previous Request Data
author: pdteam
severity: info
description: |
Test that variables can reference data extracted from previous requests
when using threads mode (parallel execution).
variables:
auth_header: "Bearer {{extracted_token}}"
http:
- method: GET
path:
- "{{BaseURL}}/login"
extractors:
- type: regex
name: extracted_token
part: body
regex:
- 'token=([a-z0-9]+)'
group: 1
internal: true
- method: GET
path:
- "{{BaseURL}}/api"
threads: 5
headers:
Authorization: "{{auth_header}}"
matchers:
- type: word
words:
- "Bearer secret123"
@@ -1,28 +0,0 @@
id: multi-ports
info:
name: Multi Ports - Detection
author: pdteam
severity: info
description: |
Multi Ports template for testing
metadata:
max-request: 1
tags: js,detect,multi-ports,enum,network
javascript:
- pre-condition: |
isPortOpen(Host,Port);
code: |
var m = require("nuclei/ssh");
var c = m.SSHClient();
var response = c.ConnectSSHInfoMode(Host, Port);
Export(response);
args:
Host: "{{Host}}"
Port: "2222,22" # Port 22 should match
extractors:
- type: json
json:
- '.UserAuth'
@@ -1,23 +0,0 @@
id: mysql-connect
info:
name: MySQL Connect Test
author: pdteam
severity: high
javascript:
- pre-condition: |
isPortOpen(Host, Port)
code: |
const mysql = require('nuclei/mysql');
const client = new mysql.MySQLClient;
success = client.Connect(Host, Port, User, Pass);
args:
Host: "{{Host}}"
Port: "3306"
User: "root"
Pass: "secret"
matchers:
- type: dsl
dsl:
- "success == true"
@@ -1,23 +0,0 @@
id: javascript-no-port-args
info:
name: JavaScript Template Without Port Args
author: dwisiswant0
severity: info
description: |
Test backwards compatibility for JavaScript templates without Port in args.
Templates should execute even when Port arg is not explicitly specified.
javascript:
- code: |
// Simple JavaScript code that does not require Port
let result = "executed";
Export(result);
args:
TestArg: "test-value"
matchers:
- type: dsl
dsl:
- success == true
@@ -1,47 +0,0 @@
id: postgres-pass-brute
info:
name: PostgreSQL Password Bruteforce
author: pdteam
severity: high
description: |
This template bruteforces passwords for protected PostgreSQL instances.
If PostgreSQL is not protected with password, it is also matched.
metadata:
shodan-query: product:"PostgreSQL"
tags: js,network,postgresql,authentication
javascript:
- pre-condition: |
isPortOpen(Host,Port)
code: |
const postgres = require('nuclei/postgres');
const client = new postgres.PGClient;
success = client.Connect(Host, Port, User, Pass);
args:
Host: "{{Host}}"
Port: "5432"
User: "{{usernames}}"
Pass: "{{passwords}}"
attack: clusterbomb
payloads:
usernames:
- postgres
- admin
- root
passwords:
- ""
- postgres
- password
- admin
- root
stop-at-first-match: true
matchers:
- type: dsl
dsl:
- "success == true"
@@ -1,38 +0,0 @@
id: vnc-password-test
info:
name: VNC Password Authentication Test
author: pdteam
severity: high
description: |
Tests VNC authentication with correct and incorrect passwords.
metadata:
shodan-query: product:"vnc"
tags: js,network,vnc,authentication
javascript:
- pre-condition: |
isPortOpen(Host,Port)
code: |
let vnc = require('nuclei/vnc');
let client = new vnc.VNCClient();
client.Connect(Host, Port, Password);
args:
Host: "{{Host}}"
Port: "5900"
Password: "{{passwords}}"
payloads:
passwords:
- ""
- root
- password
- admin
- mysecret
stop-at-first-match: true
matchers:
- type: dsl
dsl:
- "success == true"
+1 -1
View File
@@ -1,3 +1,3 @@
## keys
the keys stored here especially `ci-private-key.pem` and `ci.crt` are used in integration tests to test template signing and verification functionality introduced in nuclei v3
the keys stored here especially `ci-private-key.pem` and `ci.crt` are used in integration tests to test template signing and verfication functionality introduced in nuclei v3
+10 -10
View File
@@ -19,7 +19,7 @@ import (
"github.com/projectdiscovery/retryablehttp-go"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
"github.com/projectdiscovery/utils/env"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
unitutils "github.com/projectdiscovery/utils/unit"
updateutils "github.com/projectdiscovery/utils/update"
urlutil "github.com/projectdiscovery/utils/url"
@@ -77,11 +77,11 @@ func NewUploadWriter(ctx context.Context, logger *gologger.Logger, creds *pdcpau
output.WithJson(true, true),
)
if err != nil {
return nil, errkit.Wrap(err, "could not create output writer")
return nil, errorutil.NewWithErr(err).Msgf("could not create output writer")
}
tmp, err := urlutil.Parse(creds.Server)
if err != nil {
return nil, errkit.Wrap(err, "could not parse server url")
return nil, errorutil.NewWithErr(err).Msgf("could not parse server url")
}
tmp.Path = uploadEndpoint
tmp.Update()
@@ -104,7 +104,7 @@ func NewUploadWriter(ctx context.Context, logger *gologger.Logger, creds *pdcpau
// SetScanID sets the scan id for the upload writer
func (u *UploadWriter) SetScanID(id string) error {
if !xidRegex.MatchString(id) {
gologger.Warning().Msgf("invalid asset id provided (unknown xid format): %s", id)
return fmt.Errorf("invalid scan id provided")
}
u.scanID = id
return nil
@@ -199,7 +199,7 @@ func (u *UploadWriter) autoCommit(ctx context.Context, r *io.PipeReader) {
// uploadChunk uploads a chunk of data to the server
func (u *UploadWriter) uploadChunk(buff *bytes.Buffer) error {
if err := u.upload(buff.Bytes()); err != nil {
return errkit.Wrap(err, "could not upload chunk")
return errorutil.NewWithErr(err).Msgf("could not upload chunk")
}
// if successful, reset the buffer
buff.Reset()
@@ -211,25 +211,25 @@ func (u *UploadWriter) uploadChunk(buff *bytes.Buffer) error {
func (u *UploadWriter) upload(data []byte) error {
req, err := u.getRequest(data)
if err != nil {
return errkit.Wrap(err, "could not create upload request")
return errorutil.NewWithErr(err).Msgf("could not create upload request")
}
resp, err := u.client.Do(req)
if err != nil {
return errkit.Wrap(err, "could not upload results")
return errorutil.NewWithErr(err).Msgf("could not upload results")
}
defer func() {
_ = resp.Body.Close()
}()
bin, err := io.ReadAll(resp.Body)
if err != nil {
return errkit.Wrap(err, "could not get id from response")
return errorutil.NewWithErr(err).Msgf("could not get id from response")
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("could not upload results got status code %v on %v", resp.StatusCode, resp.Request.URL.String())
}
var uploadResp uploadResponse
if err := json.Unmarshal(bin, &uploadResp); err != nil {
return errkit.Wrap(err, fmt.Sprintf("could not unmarshal response got %v", string(bin)))
return errorutil.NewWithErr(err).Msgf("could not unmarshal response got %v", string(bin))
}
if uploadResp.ID != "" && u.scanID == "" {
u.scanID = uploadResp.ID
@@ -254,7 +254,7 @@ func (u *UploadWriter) getRequest(bin []byte) (*retryablehttp.Request, error) {
}
req, err := retryablehttp.NewRequest(method, url, bytes.NewReader(bin))
if err != nil {
return nil, errkit.Wrap(err, "could not create cloud upload request")
return nil, errorutil.NewWithErr(err).Msgf("could not create cloud upload request")
}
// add pdtm meta params
req.Params.Merge(updateutils.GetpdtmParams(config.Version))
+4 -5
View File
@@ -7,7 +7,6 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
@@ -18,7 +17,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/utils/env"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
)
type AuthLazyFetchOptions struct {
@@ -33,7 +32,7 @@ func GetAuthTmplStore(opts *types.Options, catalog catalog.Catalog, execOpts *pr
for _, file := range opts.SecretsFile {
data, err := authx.GetTemplatePathsFromSecretFile(file)
if err != nil {
return nil, errkit.Wrap(err, "failed to get template paths from secrets file")
return nil, errorutil.NewWithErr(err).Msgf("failed to get template paths from secrets file")
}
tmpls = append(tmpls, data...)
}
@@ -59,7 +58,7 @@ func GetAuthTmplStore(opts *types.Options, catalog catalog.Catalog, execOpts *pr
cfg.StoreId = loader.AuthStoreId
store, err := loader.New(cfg)
if err != nil {
return nil, errkit.Wrap(err, "failed to initialize dynamic auth templates store")
return nil, errorutil.NewWithErr(err).Msgf("failed to initialize dynamic auth templates store")
}
return store, nil
}
@@ -69,7 +68,7 @@ func GetLazyAuthFetchCallback(opts *AuthLazyFetchOptions) authx.LazyFetchSecret
return func(d *authx.Dynamic) error {
tmpls := opts.TemplateStore.LoadTemplates([]string{d.TemplatePath})
if len(tmpls) == 0 {
return fmt.Errorf("%w for path: %s", disk.ErrNoTemplatesFound, d.TemplatePath)
return fmt.Errorf("no templates found for path: %s", d.TemplatePath)
}
if len(tmpls) > 1 {
return fmt.Errorf("multiple templates found for path: %s", d.TemplatePath)
+2 -2
View File
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file"
proxyutils "github.com/projectdiscovery/utils/proxy"
)
@@ -50,7 +50,7 @@ func loadProxyServers(options *types.Options) error {
}
proxyURL, err := url.Parse(aliveProxy)
if err != nil {
return errkit.Wrapf(err, "failed to parse proxy got %v", err)
return errorutil.WrapfWithNil(err, "failed to parse proxy got %v", err)
}
if options.ProxyInternal {
_ = os.Setenv(HTTP_PROXY_ENV, proxyURL.String())
+27 -23
View File
@@ -87,7 +87,7 @@ type Runner struct {
colorizer aurora.Aurora
issuesClient reporting.Client
browser *engine.Browser
rateLimiter *ratelimit.Limiter
rateLimiter *ratelimit.AutoLimiter
hostErrors hosterrorscache.CacheInterface
resumeCfg *types.ResumeCfg
pprofServer *pprofutil.PprofServer
@@ -125,6 +125,14 @@ func New(options *types.Options) (*Runner, error) {
}
}
// if template list or template display is enabled, enable all templates
if options.TemplateList || options.TemplateDisplay {
options.EnableCodeTemplates = true
options.EnableFileTemplates = true
options.EnableSelfContainedTemplates = true
options.EnableGlobalMatchersTemplates = true
}
// check for custom template updates and update if available
ctm, err := customtemplates.NewCustomTemplatesManager(options)
if err != nil {
@@ -246,23 +254,8 @@ func New(options *types.Options) (*Runner, error) {
os.Exit(0)
}
tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*")
if err != nil {
return nil, errors.Wrap(err, "could not create temporary directory")
}
runner.tmpDir = tmpDir
// Cleanup tmpDir only if initialization fails
// On successful initialization, Close() method will handle cleanup
cleanupOnError := true
defer func() {
if cleanupOnError && runner.tmpDir != "" {
_ = os.RemoveAll(runner.tmpDir)
}
}()
// create the input provider and load the inputs
inputProvider, err := provider.NewInputProvider(provider.InputOptions{Options: options, TempDir: runner.tmpDir})
inputProvider, err := provider.NewInputProvider(provider.InputOptions{Options: options})
if err != nil {
return nil, errors.Wrap(err, "could not create input provider")
}
@@ -391,10 +384,22 @@ func New(options *types.Options) (*Runner, error) {
if options.RateLimit > 0 && options.RateLimitDuration == 0 {
options.RateLimitDuration = time.Second
}
runner.rateLimiter = utils.GetRateLimiter(context.Background(), options.RateLimit, options.RateLimitDuration)
var ratelimiter *ratelimit.AutoLimiter
if options.RateLimit == 0 && options.RateLimitDuration == 0 {
ratelimiter = ratelimit.NewAutoLimiter(context.Background(), ratelimit.WithUnlimited())
} else {
ratelimiter = ratelimit.NewAutoLimiter(
context.Background(),
ratelimit.WithMaxCount(uint(options.RateLimit)),
ratelimit.WithDuration(options.RateLimitDuration),
)
}
runner.rateLimiter = ratelimiter
if tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*"); err == nil {
runner.tmpDir = tmpDir
}
// Initialization successful, disable cleanup on error
cleanupOnError = false
return runner, nil
}
@@ -577,9 +582,7 @@ func (r *Runner) RunEnumeration() error {
}
if len(r.options.SecretsFile) > 0 && !r.options.Validate {
// Clone options so GetAuthTmplStore can modify them without affecting the original
authOptions := r.options.Copy()
authTmplStore, err := GetAuthTmplStore(authOptions, r.catalog, executorOpts)
authTmplStore, err := GetAuthTmplStore(r.options, r.catalog, executorOpts)
if err != nil {
return errors.Wrap(err, "failed to load dynamic auth templates")
}
@@ -662,6 +665,7 @@ func (r *Runner) RunEnumeration() error {
}
store.Load()
// TODO: remove below functions after v3 or update warning messages
disk.PrintDeprecatedPathsMsgIfApplicable(r.options.Silent)
templates.PrintDeprecatedProtocolNameMsgIfApplicable(r.options.Silent, r.options.Verbose)
// add the hosts from the metadata queries of loaded templates into input provider
+1 -7
View File
@@ -44,13 +44,7 @@ func (r *Runner) listAvailableStoreTemplates(store *loader.Store) {
config.DefaultConfig.TemplateVersion,
config.DefaultConfig.TemplatesDirectory,
)
// order templates alphabetically by path
templates := store.Templates()
sort.Slice(templates, func(i, j int) bool {
return templates[i].Path < templates[j].Path
})
for _, tpl := range templates {
for _, tpl := range store.Templates() {
if hasExtraFlags(r.options) {
if r.options.TemplateDisplay {
colorize := !r.options.NoColor
+1 -1
View File
@@ -50,7 +50,7 @@ type NucleiExecutorOptions struct {
Progress progress.Progress
Catalog catalog.Catalog
IssuesClient reporting.Client
RateLimiter *ratelimit.Limiter
RateLimiter *ratelimit.AutoLimiter
Interactsh *interactsh.Client
ProjectFile *projectfile.ProjectFile
Browser *browserEngine.Browser
+1 -1
View File
@@ -37,7 +37,7 @@ import nuclei "github.com/projectdiscovery/nuclei/v3/lib"
## Advanced Example of using Nuclei Library/SDK
For Various use cases like batching etc. you might want to run nuclei in goroutines this can be done by using `nuclei.NewThreadSafeNucleiEngine`
For Various use cases like batching etc you might want to run nuclei in goroutines this can be done by using `nuclei.NewThreadSafeNucleiEngine`
```go
// create nuclei engine with options
+22 -32
View File
@@ -3,13 +3,11 @@ package nuclei
import (
"context"
"errors"
"os"
"time"
"github.com/projectdiscovery/goflags"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
"github.com/projectdiscovery/utils/errkit"
"github.com/projectdiscovery/ratelimit"
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
@@ -19,6 +17,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
pkgtypes "github.com/projectdiscovery/nuclei/v3/pkg/types"
)
@@ -103,7 +102,7 @@ type InteractshOpts interactsh.Options
func WithInteractshOptions(opts InteractshOpts) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithInteractshOptions")
return ErrOptionsNotSupported.Msgf("WithInteractshOptions")
}
optsPtr := &opts
e.interactshOpts = (*interactsh.Options)(optsPtr)
@@ -181,7 +180,7 @@ func WithGlobalRateLimitCtx(ctx context.Context, maxTokens int, duration time.Du
return func(e *NucleiEngine) error {
e.opts.RateLimit = maxTokens
e.opts.RateLimitDuration = duration
e.rateLimiter = utils.GetRateLimiter(ctx, e.opts.RateLimit, e.opts.RateLimitDuration)
e.rateLimiter = ratelimit.New(ctx, uint(e.opts.RateLimit), e.opts.RateLimitDuration)
return nil
}
}
@@ -195,10 +194,8 @@ type HeadlessOpts struct {
}
// EnableHeadless allows execution of headless templates
//
// Warning: enabling headless mode may open up attack surface due to browser
// usage and can be prone to exploitation by custom unverified templates if not
// properly configured.
// *Use With Caution*: Enabling headless mode may open up attack surface due to browser usage
// and can be prone to exploitation by custom unverified templates if not properly configured
func EnableHeadlessWithOpts(hopts *HeadlessOpts) NucleiSDKOptions {
return func(e *NucleiEngine) error {
e.opts.Headless = true
@@ -208,6 +205,14 @@ func EnableHeadlessWithOpts(hopts *HeadlessOpts) NucleiSDKOptions {
e.opts.ShowBrowser = hopts.ShowBrowser
e.opts.UseInstalledChrome = hopts.UseChrome
}
if engine.MustDisableSandbox() {
e.Logger.Warning().Msgf("The current platform and privileged user will run the browser without sandbox")
}
browser, err := engine.New(e.opts)
if err != nil {
return err
}
e.browserInstance = browser
return nil
}
}
@@ -224,7 +229,7 @@ type StatsOptions struct {
func EnableStatsWithOpts(opts StatsOptions) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "EnableStatsWithOpts")
return ErrOptionsNotSupported.Msgf("EnableStatsWithOpts")
}
if opts.Interval == 0 {
opts.Interval = 5 //sec
@@ -252,7 +257,7 @@ type VerbosityOptions struct {
func WithVerbosity(opts VerbosityOptions) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithVerbosity")
return ErrOptionsNotSupported.Msgf("WithVerbosity")
}
e.opts.Verbose = opts.Verbose
e.opts.Silent = opts.Silent
@@ -285,7 +290,7 @@ type NetworkConfig struct {
func WithNetworkConfig(opts NetworkConfig) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithNetworkConfig")
return ErrOptionsNotSupported.Msgf("WithNetworkConfig")
}
e.opts.NoHostErrors = opts.DisableMaxHostErr
e.opts.MaxHostError = opts.MaxHostError
@@ -316,7 +321,7 @@ func WithNetworkConfig(opts NetworkConfig) NucleiSDKOptions {
func WithProxy(proxy []string, proxyInternalRequests bool) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithProxy")
return ErrOptionsNotSupported.Msgf("WithProxy")
}
e.opts.Proxy = proxy
e.opts.ProxyInternal = proxyInternalRequests
@@ -341,7 +346,7 @@ type OutputWriter output.Writer
func UseOutputWriter(writer OutputWriter) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "UseOutputWriter")
return ErrOptionsNotSupported.Msgf("UseOutputWriter")
}
e.customWriter = writer
return nil
@@ -356,7 +361,7 @@ type StatsWriter progress.Progress
func UseStatsWriter(writer StatsWriter) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "UseStatsWriter")
return ErrOptionsNotSupported.Msgf("UseStatsWriter")
}
e.customProgress = writer
return nil
@@ -370,7 +375,7 @@ func UseStatsWriter(writer StatsWriter) NucleiSDKOptions {
func WithTemplateUpdateCallback(disableTemplatesAutoUpgrade bool, callback func(newVersion string)) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithTemplateUpdateCallback")
return ErrOptionsNotSupported.Msgf("WithTemplateUpdateCallback")
}
e.disableTemplatesAutoUpgrade = disableTemplatesAutoUpgrade
e.onUpdateAvailableCallback = callback
@@ -382,7 +387,7 @@ func WithTemplateUpdateCallback(disableTemplatesAutoUpgrade bool, callback func(
func WithSandboxOptions(allowLocalFileAccess bool, restrictLocalNetworkAccess bool) NucleiSDKOptions {
return func(e *NucleiEngine) error {
if e.mode == threadSafe {
return errkit.Wrap(ErrOptionsNotSupported, "WithSandboxOptions")
return ErrOptionsNotSupported.Msgf("WithSandboxOptions")
}
e.opts.AllowLocalFileAccess = allowLocalFileAccess
e.opts.RestrictLocalNetworkAccess = restrictLocalNetworkAccess
@@ -553,18 +558,3 @@ func WithOptions(opts *pkgtypes.Options) NucleiSDKOptions {
return nil
}
}
// WithTemporaryDirectory allows setting a parent directory for SDK-managed temporary files.
// A temporary directory will be created inside the provided directory and cleaned up on engine close.
// If not set, a temporary directory will be automatically created in the system temp location.
// The parent directory is assumed to exist.
func WithTemporaryDirectory(parentDir string) NucleiSDKOptions {
return func(e *NucleiEngine) error {
tmpDir, err := os.MkdirTemp(parentDir, "nuclei-tmp-*")
if err != nil {
return err
}
e.tmpDir = tmpDir
return nil
}
}
+1
View File
@@ -1,4 +1,5 @@
//go:build !race
// +build !race
package nuclei_test
+13 -11
View File
@@ -12,8 +12,8 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
"github.com/projectdiscovery/utils/errkit"
"github.com/projectdiscovery/ratelimit"
errorutil "github.com/projectdiscovery/utils/errors"
"github.com/rs/xid"
)
@@ -53,7 +53,11 @@ func createEphemeralObjects(ctx context.Context, base *NucleiEngine, opts *types
if opts.RateLimit > 0 && opts.RateLimitDuration == 0 {
opts.RateLimitDuration = time.Second
}
u.executerOpts.RateLimiter = utils.GetRateLimiter(ctx, opts.RateLimit, opts.RateLimitDuration)
if opts.RateLimit == 0 && opts.RateLimitDuration == 0 {
u.executerOpts.RateLimiter = ratelimit.NewUnlimited(ctx)
} else {
u.executerOpts.RateLimiter = ratelimit.New(ctx, uint(opts.RateLimit), opts.RateLimitDuration)
}
u.engine = core.New(opts)
u.engine.SetExecuterOptions(u.executerOpts)
return u, nil
@@ -87,12 +91,10 @@ type ThreadSafeNucleiEngine struct {
func NewThreadSafeNucleiEngineCtx(ctx context.Context, opts ...NucleiSDKOptions) (*ThreadSafeNucleiEngine, error) {
defaultOptions := types.DefaultOptions()
defaultOptions.ExecutionId = xid.New().String()
// default options
e := &NucleiEngine{
opts: defaultOptions,
mode: threadSafe,
ctx: ctx,
Logger: defaultOptions.Logger,
opts: defaultOptions,
mode: threadSafe,
}
for _, option := range opts {
if err := option(e); err != nil {
@@ -122,7 +124,7 @@ func (e *ThreadSafeNucleiEngine) GlobalResultCallback(callback func(event *outpu
}
// ExecuteNucleiWithOptsCtx executes templates on targets and calls callback on each result(only if results are found)
// This method can be called concurrently and it will use some global resources but can be run parallelly
// This method can be called concurrently and it will use some global resources but can be runned parallelly
// by invoking this method with different options and targets
// Note: Not all options are thread-safe. this method will throw error if you try to use non-thread-safe options
func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOptsCtx(ctx context.Context, targets []string, opts ...NucleiSDKOptions) error {
@@ -145,13 +147,13 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOptsCtx(ctx context.Context, t
// load templates
workflowLoader, err := workflow.NewLoader(unsafeOpts.executerOpts)
if err != nil {
return errkit.Wrapf(err, "Could not create workflow loader: %s", err)
return errorutil.New("Could not create workflow loader: %s\n", err)
}
unsafeOpts.executerOpts.WorkflowLoader = workflowLoader
store, err := loader.New(loader.NewConfig(tmpEngine.opts, e.eng.catalog, unsafeOpts.executerOpts))
if err != nil {
return errkit.Wrapf(err, "Could not create loader client: %s", err)
return errorutil.New("Could not create loader client: %s\n", err)
}
store.Load()
+10 -23
View File
@@ -5,7 +5,6 @@ import (
"bytes"
"context"
"io"
"os"
"sync"
"github.com/projectdiscovery/gologger"
@@ -19,7 +18,6 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/output"
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
@@ -30,7 +28,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/ratelimit"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
"github.com/rs/xid"
)
@@ -39,13 +37,13 @@ type NucleiSDKOptions func(e *NucleiEngine) error
var (
// ErrNotImplemented is returned when a feature is not implemented
ErrNotImplemented = errkit.New("Not implemented")
ErrNotImplemented = errorutil.New("Not implemented")
// ErrNoTemplatesAvailable is returned when no templates are available to execute
ErrNoTemplatesAvailable = errkit.New("No templates available")
ErrNoTemplatesAvailable = errorutil.New("No templates available")
// ErrNoTargetsAvailable is returned when no targets are available to scan
ErrNoTargetsAvailable = errkit.New("No targets available")
ErrNoTargetsAvailable = errorutil.New("No targets available")
// ErrOptionsNotSupported is returned when an option is not supported in thread safe mode
ErrOptionsNotSupported = errkit.New("Option not supported in thread safe mode")
ErrOptionsNotSupported = errorutil.NewWithFmt("Option %v not supported in thread safe mode")
)
type engineMode uint
@@ -94,22 +92,19 @@ type NucleiEngine struct {
// Logger instance for the engine
Logger *gologger.Logger
// Temporary directory for SDK-managed template files
tmpDir string
}
// LoadAllTemplates loads all nuclei template based on given options
func (e *NucleiEngine) LoadAllTemplates() error {
workflowLoader, err := workflow.NewLoader(e.executerOpts)
if err != nil {
return errkit.Wrapf(err, "Could not create workflow loader: %s", err)
return errorutil.New("Could not create workflow loader: %s\n", err)
}
e.executerOpts.WorkflowLoader = workflowLoader
e.store, err = loader.New(loader.NewConfig(e.opts, e.catalog, e.executerOpts))
if err != nil {
return errkit.Wrapf(err, "Could not create loader client: %s", err)
return errorutil.New("Could not create loader client: %s\n", err)
}
e.store.Load()
e.templatesLoaded = true
@@ -236,12 +231,6 @@ func (e *NucleiEngine) closeInternal() {
if e.httpxClient != nil {
_ = e.httpxClient.Close()
}
if e.tmpDir != "" {
_ = os.RemoveAll(e.tmpDir)
}
if e.opts != nil {
generators.ClearOptionsPayloadMap(e.opts)
}
}
// Close all resources used by nuclei engine
@@ -324,12 +313,10 @@ func NewNucleiEngineCtx(ctx context.Context, options ...NucleiSDKOptions) (*Nucl
// default options
defaultOptions := types.DefaultOptions()
defaultOptions.ExecutionId = xid.New().String()
e := &NucleiEngine{
opts: defaultOptions,
mode: singleInstance,
ctx: ctx,
Logger: defaultOptions.Logger,
opts: defaultOptions,
mode: singleInstance,
ctx: ctx,
}
for _, option := range options {
if err := option(e); err != nil {
+19 -38
View File
@@ -3,7 +3,6 @@ package nuclei
import (
"context"
"fmt"
"os"
"strings"
"sync"
"time"
@@ -13,6 +12,7 @@ import (
"github.com/logrusorgru/aurora"
"github.com/pkg/errors"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/gologger/levels"
"github.com/projectdiscovery/httpx/common/httpx"
"github.com/projectdiscovery/nuclei/v3/internal/runner"
@@ -29,7 +29,6 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
@@ -97,11 +96,13 @@ func (e *NucleiEngine) applyRequiredDefaults(ctx context.Context) {
// init
func (e *NucleiEngine) init(ctx context.Context) error {
// Update logger ref (if it was changed by [WithLogger])
// (Logger is already initialized)
if e.opts.Logger != e.Logger {
// Set a default logger if one isn't provided in the options
if e.opts.Logger != nil {
e.Logger = e.opts.Logger
} else {
e.opts.Logger = &gologger.Logger{}
}
e.Logger = e.opts.Logger
if e.opts.Verbose {
e.Logger.SetMaxLevel(levels.LevelVerbose)
@@ -165,44 +166,24 @@ func (e *NucleiEngine) init(ctx context.Context) error {
return err
}
if e.opts.Headless {
if engine.MustDisableSandbox() {
e.Logger.Warning().Msgf("The current platform and privileged user will run the browser without sandbox")
}
browser, err := engine.New(e.opts)
if err != nil {
return err
}
e.browserInstance = browser
}
if e.catalog == nil {
e.catalog = disk.NewCatalog(config.DefaultConfig.TemplatesDirectory)
}
if e.tmpDir == "" {
tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*")
if err != nil {
return err
}
e.tmpDir = tmpDir
}
e.executerOpts = &protocols.ExecutorOptions{
Output: e.customWriter,
Options: e.opts,
Progress: e.customProgress,
Catalog: e.catalog,
IssuesClient: e.rc,
RateLimiter: e.rateLimiter,
Interactsh: e.interactshClient,
Colorizer: aurora.NewAurora(true),
ResumeCfg: types.NewResumeCfg(),
Browser: e.browserInstance,
Parser: e.parser,
InputHelper: input.NewHelper(),
TemporaryDirectory: e.tmpDir,
Logger: e.opts.Logger,
Output: e.customWriter,
Options: e.opts,
Progress: e.customProgress,
Catalog: e.catalog,
IssuesClient: e.rc,
RateLimiter: e.rateLimiter,
Interactsh: e.interactshClient,
Colorizer: aurora.NewAurora(true),
ResumeCfg: types.NewResumeCfg(),
Browser: e.browserInstance,
Parser: e.parser,
InputHelper: input.NewHelper(),
Logger: e.opts.Logger,
}
if e.opts.ShouldUseHostError() && e.hostErrCache != nil {
e.executerOpts.HostErrorsCache = e.hostErrCache
-20
View File
@@ -35,23 +35,3 @@ func TestContextCancelNucleiEngine(t *testing.T) {
}
defer ne.Close()
}
func TestHeadlessOptionInitialization(t *testing.T) {
ne, err := nuclei.NewNucleiEngineCtx(
context.Background(),
nuclei.EnableHeadlessWithOpts(&nuclei.HeadlessOpts{
PageTimeout: 20,
ShowBrowser: false,
UseChrome: false,
HeadlessOptions: []string{},
}),
)
require.NoError(t, err, "could not create nuclei engine with headless options")
require.NotNil(t, ne, "nuclei engine should not be nil")
// Verify logger is initialized
require.NotNil(t, ne.Logger, "logger should be initialized")
defer ne.Close()
}
-21
View File
@@ -59,11 +59,6 @@
"title": "engine",
"description": "Engine"
},
"sandbox": {
"$ref": "#/$defs/code.Sandbox",
"title": "sandbox",
"description": "Sandbox"
},
"pre-condition": {
"type": "string",
"title": "pre-condition for the request",
@@ -91,22 +86,6 @@
"additionalProperties": false,
"type": "object"
},
"code.Sandbox": {
"properties": {
"working-dir": {
"type": "string",
"title": "working-dir",
"description": "Working directory"
},
"image": {
"type": "string",
"title": "image",
"description": "Image"
}
},
"additionalProperties": false,
"type": "object"
},
"dns.DNSRequestTypeHolder": {
"type": "string",
"enum": [
+15 -31
View File
@@ -3,12 +3,12 @@ package authx
import (
"fmt"
"strings"
"sync/atomic"
"sync"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
sliceutil "github.com/projectdiscovery/utils/slice"
)
@@ -30,8 +30,8 @@ type Dynamic struct {
Input string `json:"input" yaml:"input"` // (optional) target for the dynamic secret
Extracted map[string]interface{} `json:"-" yaml:"-"` // extracted values from the dynamic secret
fetchCallback LazyFetchSecret `json:"-" yaml:"-"`
fetched *atomic.Bool `json:"-" yaml:"-"` // atomic flag to check if the secret has been fetched
fetching *atomic.Bool `json:"-" yaml:"-"` // atomic flag to prevent recursive fetch calls
m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch
fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched
error error `json:"-" yaml:"-"` // error if any
}
@@ -53,7 +53,7 @@ func (d *Dynamic) GetDomainAndDomainRegex() ([]string, []string) {
func (d *Dynamic) UnmarshalJSON(data []byte) error {
if d == nil {
return errkit.New("cannot unmarshal into nil Dynamic struct")
return errorutil.New("cannot unmarshal into nil Dynamic struct")
}
// Use an alias type (auxiliary) to avoid a recursive call in this method.
@@ -70,13 +70,12 @@ func (d *Dynamic) UnmarshalJSON(data []byte) error {
// Validate validates the dynamic secret
func (d *Dynamic) Validate() error {
d.fetched = &atomic.Bool{}
d.fetching = &atomic.Bool{}
d.m = &sync.Mutex{}
if d.TemplatePath == "" {
return errkit.New(" template-path is required for dynamic secret")
return errorutil.New(" template-path is required for dynamic secret")
}
if len(d.Variables) == 0 {
return errkit.New("variables are required for dynamic secret")
return errorutil.New("variables are required for dynamic secret")
}
if d.Secret != nil {
@@ -98,7 +97,9 @@ func (d *Dynamic) Validate() error {
func (d *Dynamic) SetLazyFetchCallback(callback LazyFetchSecret) {
d.fetchCallback = func(d *Dynamic) error {
err := callback(d)
d.fetched = true
if err != nil {
d.error = err
return err
}
if len(d.Extracted) == 0 {
@@ -183,15 +184,9 @@ func (d *Dynamic) applyValuesToSecret(secret *Secret) error {
// GetStrategy returns the auth strategies for the dynamic secret
func (d *Dynamic) GetStrategies() []AuthStrategy {
if d.fetched.Load() {
if d.error != nil {
return nil
}
} else {
// Try to fetch if not already fetched
if !d.fetched {
_ = d.Fetch(true)
}
if d.error != nil {
return nil
}
@@ -208,23 +203,12 @@ func (d *Dynamic) GetStrategies() []AuthStrategy {
// Fetch fetches the dynamic secret
// if isFatal is true, it will stop the execution if the secret could not be fetched
func (d *Dynamic) Fetch(isFatal bool) error {
if d.fetched.Load() {
return d.error
d.m.Lock()
defer d.m.Unlock()
if d.fetched {
return nil
}
// Try to set fetching flag atomically
if !d.fetching.CompareAndSwap(false, true) {
// Already fetching, return current error
return d.error
}
// We're the only one fetching, call the callback
d.error = d.fetchCallback(d)
// Mark as fetched and clear fetching flag
d.fetched.Store(true)
d.fetching.Store(false)
if d.error != nil && isFatal {
gologger.Fatal().Msgf("Could not fetch dynamic secret: %s\n", d.error)
}
+5 -9
View File
@@ -8,7 +8,7 @@ import (
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
"github.com/projectdiscovery/utils/generic"
stringsutil "github.com/projectdiscovery/utils/strings"
"gopkg.in/yaml.v3"
@@ -55,7 +55,7 @@ type Secret struct {
Type string `json:"type" yaml:"type"`
Domains []string `json:"domains" yaml:"domains"`
DomainsRegex []string `json:"domains-regex" yaml:"domains-regex"`
Headers []KV `json:"headers" yaml:"headers"` // Headers preserve exact casing (useful for case-sensitive APIs)
Headers []KV `json:"headers" yaml:"headers"`
Cookies []Cookie `json:"cookies" yaml:"cookies"`
Params []KV `json:"params" yaml:"params"`
Username string `json:"username" yaml:"username"` // can be either email or username
@@ -148,7 +148,7 @@ func (s *Secret) Validate() error {
}
type KV struct {
Key string `json:"key" yaml:"key"` // Header key (preserves exact casing)
Key string `json:"key" yaml:"key"`
Value string `json:"value" yaml:"value"`
}
@@ -237,9 +237,7 @@ func GetAuthDataFromYAML(data []byte) (*Authx, error) {
var auth Authx
err := yaml.Unmarshal(data, &auth)
if err != nil {
errorErr := errkit.FromError(err)
errorErr.Msgf("could not unmarshal yaml")
return nil, errorErr
return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal yaml")
}
return &auth, nil
}
@@ -249,9 +247,7 @@ func GetAuthDataFromJSON(data []byte) (*Authx, error) {
var auth Authx
err := json.Unmarshal(data, &auth)
if err != nil {
errorErr := errkit.FromError(err)
errorErr.Msgf("could not unmarshal json")
return nil, errorErr
return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal json")
}
return &auth, nil
}
+2 -6
View File
@@ -21,19 +21,15 @@ func NewHeadersAuthStrategy(data *Secret) *HeadersAuthStrategy {
}
// Apply applies the headers auth strategy to the request
// NOTE: This preserves exact header casing (e.g., barAuthToken stays as barAuthToken)
// This is useful for APIs that require case-sensitive header names
func (s *HeadersAuthStrategy) Apply(req *http.Request) {
for _, header := range s.Data.Headers {
req.Header[header.Key] = []string{header.Value}
req.Header.Set(header.Key, header.Value)
}
}
// ApplyOnRR applies the headers auth strategy to the retryable request
// NOTE: This preserves exact header casing (e.g., barAuthToken stays as barAuthToken)
// This is useful for APIs that require case-sensitive header names
func (s *HeadersAuthStrategy) ApplyOnRR(req *retryablehttp.Request) {
for _, header := range s.Data.Headers {
req.Header[header.Key] = []string{header.Value}
req.Header.Set(header.Key, header.Value)
}
}
@@ -12,8 +12,6 @@ info:
# static secrets
static:
# for header based auth session
# NOTE: Headers preserve exact casing (e.g., x-pdcp-key stays as x-pdcp-key)
# This is useful for APIs that require case-sensitive header names
- type: header
domains:
- api.projectdiscovery.io
@@ -22,8 +20,6 @@ static:
headers:
- key: x-pdcp-key
value: <api-key-here>
- key: barAuthToken
value: <auth-token-here>
# for query based auth session
- type: Query
+4 -8
View File
@@ -7,7 +7,7 @@ import (
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
urlutil "github.com/projectdiscovery/utils/url"
)
@@ -30,20 +30,16 @@ func NewFileAuthProvider(path string, callback authx.LazyFetchSecret) (AuthProvi
return nil, ErrNoSecrets
}
if len(store.Dynamic) > 0 && callback == nil {
return nil, errkit.New("lazy fetch callback is required for dynamic secrets")
return nil, errorutil.New("lazy fetch callback is required for dynamic secrets")
}
for _, secret := range store.Secrets {
if err := secret.Validate(); err != nil {
errorErr := errkit.FromError(err)
errorErr.Msgf("invalid secret in file: %s", path)
return nil, errorErr
return nil, errorutil.NewWithErr(err).Msgf("invalid secret in file: %s", path)
}
}
for i, dynamic := range store.Dynamic {
if err := dynamic.Validate(); err != nil {
errorErr := errkit.FromError(err)
errorErr.Msgf("invalid dynamic in file: %s", path)
return nil, errorErr
return nil, errorutil.NewWithErr(err).Msgf("invalid dynamic in file: %s", path)
}
dynamic.SetLazyFetchCallback(callback)
store.Dynamic[i] = dynamic
+1 -2
View File
@@ -31,7 +31,7 @@ const (
CLIConfigFileName = "config.yaml"
ReportingConfigFilename = "reporting-config.yaml"
// Version is the current version of nuclei
Version = `v3.6.2`
Version = `v3.4.8`
// Directory Names of custom templates
CustomS3TemplatesDirName = "s3"
CustomGitHubTemplatesDirName = "github"
@@ -40,7 +40,6 @@ const (
BinaryName = "nuclei"
FallbackConfigFolderName = ".nuclei-config"
NucleiConfigDirEnv = "NUCLEI_CONFIG_DIR"
NucleiTemplatesDirEnv = "NUCLEI_TEMPLATES_DIR"
)
// IsOutdatedVersion compares two versions and returns true
+2 -1
View File
@@ -2,6 +2,7 @@ package config
import (
"os"
"runtime/debug"
"github.com/projectdiscovery/gologger"
"gopkg.in/yaml.v2"
@@ -17,7 +18,7 @@ type IgnoreFile struct {
func ReadIgnoreFile() IgnoreFile {
file, err := os.Open(DefaultConfig.GetIgnoreFilePath())
if err != nil {
gologger.Error().Msgf("Could not read nuclei-ignore file: %s\n", err)
gologger.Error().Msgf("Could not read nuclei-ignore file: %s\n%s\n", err, string(debug.Stack()))
return IgnoreFile{}
}
defer func() {
+13 -13
View File
@@ -13,7 +13,7 @@ import (
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
"github.com/projectdiscovery/utils/env"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
fileutil "github.com/projectdiscovery/utils/file"
folderutil "github.com/projectdiscovery/utils/folder"
)
@@ -140,13 +140,13 @@ func (c *Config) UpdateNucleiIgnoreHash() error {
if fileutil.FileExists(ignoreFilePath) {
bin, err := os.ReadFile(ignoreFilePath)
if err != nil {
return errkit.Newf("could not read nuclei ignore file: %v", err)
return errorutil.NewWithErr(err).Msgf("could not read nuclei ignore file")
}
c.NucleiIgnoreHash = fmt.Sprintf("%x", md5.Sum(bin))
// write config to disk
return c.WriteTemplatesConfig()
}
return errkit.New("ignore file not found: could not update nuclei ignore hash")
return errorutil.NewWithTag("config", "ignore file not found: could not update nuclei ignore hash")
}
// GetConfigDir returns the nuclei configuration directory
@@ -200,8 +200,8 @@ func (c *Config) GetNewAdditions() []string {
if err != nil {
return arr
}
for v := range strings.FieldsSeq(string(bin)) {
if IsTemplateWithRoot(v, c.TemplatesDirectory) {
for _, v := range strings.Fields(string(bin)) {
if IsTemplate(v) {
arr = append(arr, v)
}
}
@@ -257,7 +257,7 @@ func (c *Config) SetTemplatesVersion(version string) error {
c.TemplateVersion = version
// write config to disk
if err := c.WriteTemplatesConfig(); err != nil {
return errkit.Newf("could not write nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
return errorutil.NewWithErr(err).Msgf("could not write nuclei config file at %s", c.getTemplatesConfigFilePath())
}
return nil
}
@@ -265,15 +265,15 @@ func (c *Config) SetTemplatesVersion(version string) error {
// ReadTemplatesConfig reads the nuclei templates config file
func (c *Config) ReadTemplatesConfig() error {
if !fileutil.FileExists(c.getTemplatesConfigFilePath()) {
return errkit.Newf("nuclei config file at %s does not exist", c.getTemplatesConfigFilePath())
return errorutil.NewWithTag("config", "nuclei config file at %s does not exist", c.getTemplatesConfigFilePath())
}
var cfg *Config
bin, err := os.ReadFile(c.getTemplatesConfigFilePath())
if err != nil {
return errkit.Newf("could not read nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
return errorutil.NewWithErr(err).Msgf("could not read nuclei config file at %s", c.getTemplatesConfigFilePath())
}
if err := json.Unmarshal(bin, &cfg); err != nil {
return errkit.Newf("could not unmarshal nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
return errorutil.NewWithErr(err).Msgf("could not unmarshal nuclei config file at %s", c.getTemplatesConfigFilePath())
}
// apply config
c.TemplatesDirectory = cfg.TemplatesDirectory
@@ -292,10 +292,10 @@ func (c *Config) WriteTemplatesConfig() error {
}
bin, err := json.Marshal(c)
if err != nil {
return errkit.Newf("failed to marshal nuclei config: %v", err)
return errorutil.NewWithErr(err).Msgf("failed to marshal nuclei config")
}
if err = os.WriteFile(c.getTemplatesConfigFilePath(), bin, 0600); err != nil {
return errkit.Newf("failed to write nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
return errorutil.NewWithErr(err).Msgf("failed to write nuclei config file at %s", c.getTemplatesConfigFilePath())
}
return nil
}
@@ -319,7 +319,7 @@ func (c *Config) getTemplatesConfigFilePath() string {
func (c *Config) createConfigDirIfNotExists() error {
if !fileutil.FolderExists(c.configDir) {
if err := fileutil.CreateFolder(c.configDir); err != nil {
return errkit.Newf("could not create nuclei config directory at %s: %v", c.configDir, err)
return errorutil.NewWithErr(err).Msgf("could not create nuclei config directory at %s", c.configDir)
}
}
return nil
@@ -350,7 +350,7 @@ func (c *Config) IsDebugArgEnabled(arg string) bool {
// parseDebugArgs from string
func (c *Config) parseDebugArgs(data string) {
// use space as separator instead of commas
// use space as seperator instead of commas
tmp := strings.Fields(data)
for _, v := range tmp {
key := v
+11 -86
View File
@@ -12,10 +12,7 @@ import (
stringsutil "github.com/projectdiscovery/utils/strings"
)
var (
knownConfigFiles = []string{"cves.json", "contributors.json", "TEMPLATES-STATS.json"}
knownMiscDirectories = []string{".git", ".github", "helpers"}
)
var knownConfigFiles = []string{"cves.json", "contributors.json", "TEMPLATES-STATS.json"}
// TemplateFormat
type TemplateFormat uint8
@@ -26,25 +23,6 @@ const (
Unknown
)
// GetKnownConfigFiles returns known config files.
func GetKnownConfigFiles() []string {
return knownConfigFiles
}
// GetKnownMiscDirectories returns known misc directories with trailing slashes.
//
// The trailing slash ensures that directory matching is explicit and avoids
// falsely match files with similar names (e.g. "helpers" matching
// "some-helpers.yaml"), since [IsTemplate] checks against normalized full paths.
func GetKnownMiscDirectories() []string {
trailedSlashDirs := make([]string, 0, len(knownMiscDirectories))
for _, dir := range knownMiscDirectories {
trailedSlashDirs = append(trailedSlashDirs, dir+string(os.PathSeparator))
}
return trailedSlashDirs
}
// GetTemplateFormatFromExt returns template format
func GetTemplateFormatFromExt(filePath string) TemplateFormat {
fileExt := strings.ToLower(filepath.Ext(filePath))
@@ -63,54 +41,13 @@ func GetSupportTemplateFileExtensions() []string {
return []string{extensions.YAML, extensions.JSON}
}
// IsTemplate returns true if the file is a template based on its path.
// It used by goflags and other places to filter out non-template files.
func IsTemplate(fpath string) bool {
return IsTemplateWithRoot(fpath, "")
}
// IsTemplateWithRoot returns true if the file is a template based on its path
// and root directory. If rootDir is provided, it checks for excluded
// directories relative to the root.
func IsTemplateWithRoot(fpath, rootDir string) bool {
fpath = filepath.FromSlash(fpath)
fname := filepath.Base(fpath)
fext := strings.ToLower(filepath.Ext(fpath))
if stringsutil.ContainsAny(fname, GetKnownConfigFiles()...) {
// IsTemplate is a callback function used by goflags to decide if given file should be read
// if it is not a nuclei-template file only then file is read
func IsTemplate(filename string) bool {
if stringsutil.ContainsAny(filename, knownConfigFiles...) {
return false
}
var pathToCheck string
if rootDir != "" {
if filepath.IsAbs(fpath) {
rel, err := filepath.Rel(rootDir, fpath)
if err == nil && !strings.HasPrefix(rel, "..") {
pathToCheck = rel
} else {
pathToCheck = fpath
}
} else {
pathToCheck = fpath
}
} else {
pathToCheck = fpath
}
// Only check components if pathToCheck is NOT absolute
// This avoids false positives on parent directories for absolute paths
if !filepath.IsAbs(pathToCheck) {
parts := strings.Split(pathToCheck, string(os.PathSeparator))
for _, p := range parts {
for _, excluded := range knownMiscDirectories {
if strings.EqualFold(p, excluded) {
return false
}
}
}
}
return stringsutil.EqualFoldAny(fext, GetSupportTemplateFileExtensions()...)
return stringsutil.EqualFoldAny(filepath.Ext(filename), GetSupportTemplateFileExtensions()...)
}
type template struct {
@@ -154,21 +91,11 @@ func GetNucleiTemplatesIndex() (map[string]string, error) {
if err == nil {
for _, v := range records {
if len(v) >= 2 {
templateID := v[0]
templatePath := v[1]
// Normalize path for consistent comparison (handles Windows path issues)
normalizedPath := filepath.Clean(templatePath)
// Validate that the file actually exists (prevents stale entries from deleted files on Windows)
if fileutil.FileExists(normalizedPath) {
index[templateID] = normalizedPath
}
index[v[0]] = v[1]
}
}
// Close file handle before returning
_ = f.Close()
return index, nil
}
_ = f.Close()
}
DefaultConfig.Logger.Error().Msgf("failed to read index file creating new one: %v", err)
}
@@ -184,18 +111,16 @@ func GetNucleiTemplatesIndex() (map[string]string, error) {
DefaultConfig.Logger.Verbose().Msgf("failed to walk path=%v err=%v", path, err)
return nil
}
if d.IsDir() || !IsTemplateWithRoot(path, DefaultConfig.TemplatesDirectory) || stringsutil.ContainsAny(path, ignoreDirs...) {
if d.IsDir() || !IsTemplate(path) || stringsutil.ContainsAny(path, ignoreDirs...) {
return nil
}
// Normalize path for consistent comparison (handles Windows path issues)
normalizedPath := filepath.Clean(path)
// get template id from file
id, err := getTemplateID(normalizedPath)
id, err := getTemplateID(path)
if err != nil || id == "" {
DefaultConfig.Logger.Verbose().Msgf("failed to get template id from file=%v got id=%v err=%v", normalizedPath, id, err)
DefaultConfig.Logger.Verbose().Msgf("failed to get template id from file=%v got id=%v err=%v", path, id, err)
return nil
}
index[id] = normalizedPath
index[id] = path
return nil
})
return index, err
-100
View File
@@ -1,100 +0,0 @@
package config
import (
"os"
"path/filepath"
"testing"
osutils "github.com/projectdiscovery/utils/os"
"github.com/stretchr/testify/require"
)
func toAbs(p string) string {
if osutils.IsWindows() {
// Infer the drive letter from the Windows system path
// SystemRoot or WINDIR typically points to something like "C:\Windows"
systemRoot := os.Getenv("SystemRoot")
if systemRoot == "" {
systemRoot = os.Getenv("WINDIR")
}
// Extract volume name (e.g., "C:") from the system path
volumeName := filepath.VolumeName(systemRoot)
if volumeName == "" {
// Fallback to C: if we can't determine the volume
volumeName = "C:"
}
return filepath.FromSlash(volumeName + p)
}
return filepath.FromSlash(p)
}
func TestIsTemplate(t *testing.T) {
tests := []struct {
name string
fpath string
rootDir string
want bool
}{
{
name: "valid template",
fpath: "dns/cname.yaml",
rootDir: "",
want: true,
},
{
name: "excluded directory relative",
fpath: "helpers/data.txt",
rootDir: "",
want: false,
},
{
name: "excluded directory .git",
fpath: ".git/config",
rootDir: "",
want: false,
},
{
name: "absolute path with excluded parent dir (bug fix)",
fpath: toAbs("/path/to/somewhere/that/has/helpers/dir/nuclei/nuclei-templates/dns/cname.yaml"),
rootDir: toAbs("/path/to/somewhere/that/has/helpers/dir/nuclei/nuclei-templates"),
want: true,
},
{
name: "absolute path with excluded dir inside root",
fpath: toAbs("/path/to/somewhere/that/has/helpers/dir/nuclei/nuclei-templates/helpers/data.txt"),
rootDir: toAbs("/path/to/somewhere/that/has/helpers/dir/nuclei/nuclei-templates"),
want: false,
},
{
name: "absolute path without root (skip check)",
fpath: toAbs("/opt/helpers/foo.yaml"),
rootDir: "",
want: true,
},
{
name: "valid template with different extension",
fpath: "dns/cname.json",
rootDir: "",
want: true,
},
{
name: "invalid extension",
fpath: "dns/cname.txt",
rootDir: "",
want: false,
},
{
name: "excluded config file",
fpath: "cves.json",
rootDir: "",
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := IsTemplateWithRoot(tt.fpath, tt.rootDir)
require.Equal(t, tt.want, got, "IsTemplateWithRoot(%q, %q)", tt.fpath, tt.rootDir)
})
}
}
+7 -1
View File
@@ -38,7 +38,13 @@ func NewFSCatalog(fs fs.FS, directory string) *DiskCatalog {
// It is used to read template and payload files based on catalog responses.
func (d *DiskCatalog) OpenFile(filename string) (io.ReadCloser, error) {
if d.templatesFS == nil {
return os.Open(filename)
file, err := os.Open(filename)
if err != nil {
if file, errx := os.Open(BackwardsCompatiblePaths(d.templatesDirectory, filename)); errx == nil {
return file, nil
}
}
return file, err
}
return d.templatesFS.Open(filename)
-7
View File
@@ -1,7 +0,0 @@
package disk
import "errors"
var (
ErrNoTemplatesFound = errors.New("no templates found")
)
+88 -45
View File
@@ -1,7 +1,6 @@
package disk
import (
"fmt"
"io/fs"
"os"
"path/filepath"
@@ -66,34 +65,41 @@ func (c *DiskCatalog) GetTemplatesPath(definitions []string) ([]string, map[stri
// or folders provided as in.
func (c *DiskCatalog) GetTemplatePath(target string) ([]string, error) {
processed := make(map[string]struct{})
// Template input includes a wildcard
if strings.Contains(target, "*") {
matches, findErr := c.findGlobPathMatches(target, processed)
if findErr != nil {
return nil, errors.Wrap(findErr, "could not find glob matches")
}
if len(matches) == 0 {
return nil, errors.Errorf("no templates found for path")
}
return matches, nil
}
// try to handle deprecated template paths
absPath := target
if c.templatesFS == nil {
absPath = BackwardsCompatiblePaths(c.templatesDirectory, target)
if absPath != target && strings.TrimPrefix(absPath, c.templatesDirectory+string(filepath.Separator)) != target {
if config.DefaultConfig.LogAllEvents {
config.DefaultConfig.Logger.Print().Msgf("[%v] requested Template path %s is deprecated, please update to %s\n", aurora.Yellow("WRN").String(), target, absPath)
}
deprecatedPathsCounter++
}
var err error
target, err = c.convertPathToAbsolute(target)
absPath, err = c.convertPathToAbsolute(absPath)
if err != nil {
return nil, errors.Wrapf(err, "could not find template file")
}
}
if strings.Contains(target, "*") {
globMatches, err := c.findGlobPathMatches(target, processed)
if err != nil {
return nil, errors.Wrap(err, "could not globbing path")
}
if len(globMatches) > 0 {
return globMatches, nil
} else {
return globMatches, fmt.Errorf("%w in path %q", ErrNoTemplatesFound, target)
}
}
// `target` is either a file or a directory
match, file, err := c.findFileMatches(target, processed)
// Template input is either a file or a directory
match, file, err := c.findFileMatches(absPath, processed)
if err != nil {
return nil, errors.Wrap(err, "could not find file")
}
if file {
if match != "" {
return []string{match}, nil
@@ -103,15 +109,13 @@ func (c *DiskCatalog) GetTemplatePath(target string) ([]string, error) {
// Recursively walk down the Templates directory and run all
// the template file checks
matches, err := c.findDirectoryMatches(target, processed)
matches, err := c.findDirectoryMatches(absPath, processed)
if err != nil {
return nil, errors.Wrap(err, "could not find directory matches")
}
if len(matches) == 0 {
return nil, fmt.Errorf("%w in path %q", ErrNoTemplatesFound, target)
return nil, errors.Errorf("no templates found in path %s", absPath)
}
return matches, nil
}
@@ -131,29 +135,72 @@ func (c *DiskCatalog) convertPathToAbsolute(t string) (string, error) {
// findGlobPathMatches returns the matched files from a glob path
func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]struct{}) ([]string, error) {
// to support globbing on old paths we use brute force to find matches with exit on first match
// trim templateDir if any
relPath := strings.TrimPrefix(absPath, c.templatesDirectory)
// trim leading slash if any
if c.templatesFS != nil {
// fs.FS always uses forward slashes
relPath = strings.TrimPrefix(relPath, "/")
} else {
relPath = strings.TrimPrefix(relPath, string(os.PathSeparator))
relPath = strings.TrimPrefix(relPath, string(os.PathSeparator))
OldPathsResolver := func(inputGlob string) []string {
templateDir := c.templatesDirectory
if c.templatesDirectory == "" {
templateDir = "./"
}
if c.templatesFS == nil {
matches, _ := fs.Glob(os.DirFS(filepath.Join(templateDir, "http")), inputGlob)
if len(matches) != 0 {
return matches
}
// condition to support network cve related globs
matches, _ = fs.Glob(os.DirFS(filepath.Join(templateDir, "network")), inputGlob)
return matches
} else {
sub, err := fs.Sub(c.templatesFS, filepath.Join(templateDir, "http"))
if err != nil {
return nil
}
matches, _ := fs.Glob(sub, inputGlob)
if len(matches) != 0 {
return matches
}
// condition to support network cve related globs
sub, err = fs.Sub(c.templatesFS, filepath.Join(templateDir, "network"))
if err != nil {
return nil
}
matches, _ = fs.Glob(sub, inputGlob)
return matches
}
}
var err error
var matched []string
var matches []string
if c.templatesFS != nil {
matches, err = fs.Glob(c.templatesFS, relPath)
if c.templatesFS == nil {
var err error
matches, err = filepath.Glob(relPath)
if len(matches) != 0 {
matched = append(matched, matches...)
} else {
matched = append(matched, OldPathsResolver(relPath)...)
}
if err != nil && len(matched) == 0 {
return nil, errors.Errorf("wildcard found, but unable to glob: %s\n", err)
}
} else {
matches, err = filepath.Glob(absPath)
var err error
matches, err = fs.Glob(c.templatesFS, relPath)
if len(matches) != 0 {
matched = append(matched, matches...)
} else {
matched = append(matched, OldPathsResolver(relPath)...)
}
if err != nil && len(matched) == 0 {
return nil, errors.Errorf("wildcard found, but unable to glob: %s\n", err)
}
}
if err != nil {
return nil, err
}
results := make([]string, 0, len(matches))
for _, match := range matches {
if _, ok := processed[match]; !ok {
@@ -161,7 +208,6 @@ func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]s
results = append(results, match)
}
}
return results, nil
}
@@ -211,7 +257,7 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
if err != nil {
return nil
}
if !d.IsDir() && config.IsTemplateWithRoot(path, absPath) {
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
if _, ok := processed[path]; !ok {
results = append(results, path)
processed[path] = struct{}{}
@@ -235,7 +281,7 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
if err != nil {
return nil
}
if !d.IsDir() && config.IsTemplateWithRoot(path, absPath) {
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
if _, ok := processed[path]; !ok {
results = append(results, path)
processed[path] = struct{}{}
@@ -248,11 +294,8 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
return results, err
}
// PrintDeprecatedPathsMsgIfApplicable prints a warning message if any
// deprecated paths are found. Unless mode is silent warning message is printed.
//
// Deprecated: No longer used since the official Nuclei Templates repository
// have restructured this a long time ago.
// PrintDeprecatedPathsMsgIfApplicable prints a warning message if any deprecated paths are found
// Unless mode is silent warning message is printed
func PrintDeprecatedPathsMsgIfApplicable(isSilent bool) {
if !updateutils.IsOutdated("v9.4.3", config.DefaultConfig.TemplateVersion) {
return
-3
View File
@@ -72,9 +72,6 @@ func (c *DiskCatalog) tryResolve(fullPath string) (string, error) {
// BackwardsCompatiblePaths returns new paths for all old/legacy template paths
// Note: this is a temporary function and will be removed in the future release
//
// Deprecated: No longer used since the official Nuclei Templates repository
// have restructured this a long time ago.
func BackwardsCompatiblePaths(templateDir string, oldPath string) string {
// TODO: remove this function in the future release
// 1. all http related paths are now moved at path /http
-344
View File
@@ -1,344 +0,0 @@
package index
import (
"path/filepath"
"slices"
"strings"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
)
// Filter represents filtering criteria for template metadata.
//
// Inclusion fields (e.g., Authors, Tags, IDs, Severities, ProtocolTypes) use
// AND logic across different filter types and OR logic within each type.
// Exclusion fields (e.g., ExcludeTags, ExcludeIDs, ExcludeSeverities,
// ExcludeProtocolTypes) take precedence over inclusion fields. Additionally,
// IncludeTemplates and IncludeTags can force inclusion of templates even if
// they match exclusion criteria.
type Filter struct {
// Authors to include.
Authors []string
// Tags to include.
Tags []string
// ExcludeTags to exclude (takes precedence over Tags).
ExcludeTags []string
// IncludeTags to force include even if excluded.
IncludeTags []string
// IDs to include (supports wildcards, OR logic).
IDs []string
// ExcludeIDs to exclude (supports wildcards).
ExcludeIDs []string
// IncludeTemplates paths to force include even if excluded.
IncludeTemplates []string
// ExcludeTemplates paths to exclude.
ExcludeTemplates []string
// Severities to include.
Severities []severity.Severity
// ExcludeSeverities to exclude.
ExcludeSeverities []severity.Severity
// ProtocolTypes to include.
ProtocolTypes []types.ProtocolType
// ExcludeProtocolTypes to exclude.
ExcludeProtocolTypes []types.ProtocolType
}
// Matches checks if metadata matches the filter criteria.
func (f *Filter) Matches(m *Metadata) bool {
if f.isForcedInclude(m) {
return true
}
if f.isExcluded(m) {
return false
}
if !f.matchesIncludes(m) {
return false
}
return true
}
// isForcedInclude checks if template is forced to be included.
func (f *Filter) isForcedInclude(m *Metadata) bool {
if len(f.IncludeTemplates) > 0 {
for _, includePath := range f.IncludeTemplates {
if matchesPath(m.FilePath, includePath) {
return true
}
}
}
if len(f.IncludeTags) > 0 {
if slices.ContainsFunc(f.IncludeTags, m.HasTag) {
return true
}
}
return false
}
// isExcluded checks if template should be excluded.
func (f *Filter) isExcluded(m *Metadata) bool {
if len(f.ExcludeTemplates) > 0 {
for _, excludePath := range f.ExcludeTemplates {
if matchesPath(m.FilePath, excludePath) {
return true
}
}
}
if len(f.ExcludeTags) > 0 {
if slices.ContainsFunc(f.ExcludeTags, m.HasTag) {
return true
}
}
if len(f.ExcludeIDs) > 0 {
for _, excludeID := range f.ExcludeIDs {
if matchesID(m.ID, excludeID) {
return true
}
}
}
if len(f.ExcludeSeverities) > 0 {
if slices.ContainsFunc(f.ExcludeSeverities, m.MatchesSeverity) {
return true
}
}
if len(f.ExcludeProtocolTypes) > 0 {
if slices.ContainsFunc(f.ExcludeProtocolTypes, m.MatchesProtocol) {
return true
}
}
return false
}
// matchesIncludes checks if metadata matches include filters.
//
// Returns true if no include filters are specified, or if all specified filter
// types match.
func (f *Filter) matchesIncludes(m *Metadata) bool {
if len(f.Authors) > 0 {
if !slices.ContainsFunc(f.Authors, m.HasAuthor) {
return false
}
}
if len(f.Tags) > 0 {
if !slices.ContainsFunc(f.Tags, m.HasTag) {
return false
}
}
if len(f.IDs) > 0 {
matched := false
for _, id := range f.IDs {
if matchesID(m.ID, id) {
matched = true
break
}
}
if !matched {
return false
}
}
if len(f.Severities) > 0 {
if !slices.ContainsFunc(f.Severities, m.MatchesSeverity) {
return false
}
}
if len(f.ProtocolTypes) > 0 {
if !slices.ContainsFunc(f.ProtocolTypes, m.MatchesProtocol) {
return false
}
}
return true
}
// matchesID checks if template ID matches pattern (supports wildcards).
func matchesID(templateID, pattern string) bool {
// Convert to lowercase for case-insensitive matching
templateID = strings.ToLower(templateID)
pattern = strings.ToLower(pattern)
if templateID == pattern {
return true
}
matched, _ := filepath.Match(pattern, templateID)
return matched
}
// matchesPath checks if template path matches pattern.
func matchesPath(templatePath, pattern string) bool {
templatePath = filepath.Clean(templatePath)
pattern = filepath.Clean(pattern)
if templatePath == pattern {
return true
}
if strings.HasPrefix(templatePath, pattern+string(filepath.Separator)) {
return true
}
matched, _ := filepath.Match(pattern, templatePath)
return matched
}
// FilterFunc is a function that filters metadata.
type FilterFunc func(*Metadata) bool
// UnmarshalFilter creates a Filter from nuclei options.
func UnmarshalFilter(
authors, tags, excludeTags, includeTags []string,
ids, excludeIDs []string,
includeTemplates, excludeTemplates []string,
severities, excludeSeverities []string,
protocolTypes, excludeProtocolTypes []string,
) (*Filter, error) {
filter := &Filter{
Authors: authors,
Tags: tags,
ExcludeTags: excludeTags,
IncludeTags: includeTags,
IDs: ids,
ExcludeIDs: excludeIDs,
IncludeTemplates: includeTemplates,
ExcludeTemplates: excludeTemplates,
}
for _, sev := range severities {
holder := &severity.Holder{}
if err := holder.UnmarshalYAML(func(v interface{}) error {
*v.(*string) = sev
return nil
}); err == nil {
filter.Severities = append(filter.Severities, holder.Severity)
}
}
for _, sev := range excludeSeverities {
holder := &severity.Holder{}
if err := holder.UnmarshalYAML(func(v interface{}) error {
*v.(*string) = sev
return nil
}); err == nil {
filter.ExcludeSeverities = append(filter.ExcludeSeverities, holder.Severity)
}
}
for _, pt := range protocolTypes {
holder := &types.TypeHolder{}
if err := holder.UnmarshalYAML(func(v interface{}) error {
*v.(*string) = pt
return nil
}); err == nil && holder.ProtocolType != types.InvalidProtocol {
filter.ProtocolTypes = append(filter.ProtocolTypes, holder.ProtocolType)
}
}
for _, pt := range excludeProtocolTypes {
holder := &types.TypeHolder{}
if err := holder.UnmarshalYAML(func(v interface{}) error {
*v.(*string) = pt
return nil
}); err == nil && holder.ProtocolType != types.InvalidProtocol {
filter.ExcludeProtocolTypes = append(filter.ExcludeProtocolTypes, holder.ProtocolType)
}
}
return filter, nil
}
// UnmarshalFilterFunc creates a FilterFunc from filter criteria.
func UnmarshalFilterFunc(filter *Filter) FilterFunc {
if filter == nil {
return func(*Metadata) bool { return true }
}
return filter.Matches
}
// IsEmpty returns true if filter has no criteria set.
func (f *Filter) IsEmpty() bool {
return len(f.Authors) == 0 &&
len(f.Tags) == 0 &&
len(f.ExcludeTags) == 0 &&
len(f.IncludeTags) == 0 &&
len(f.IDs) == 0 &&
len(f.ExcludeIDs) == 0 &&
len(f.IncludeTemplates) == 0 &&
len(f.ExcludeTemplates) == 0 &&
len(f.Severities) == 0 &&
len(f.ExcludeSeverities) == 0 &&
len(f.ProtocolTypes) == 0 &&
len(f.ExcludeProtocolTypes) == 0
}
// String returns a human-readable representation of the filter.
func (f *Filter) String() string {
var parts []string
if len(f.Authors) > 0 {
parts = append(parts, "authors="+strings.Join(f.Authors, ","))
}
if len(f.Tags) > 0 {
parts = append(parts, "tags="+strings.Join(f.Tags, ","))
}
if len(f.ExcludeTags) > 0 {
parts = append(parts, "exclude-tags="+strings.Join(f.ExcludeTags, ","))
}
if len(f.IDs) > 0 {
parts = append(parts, "ids="+strings.Join(f.IDs, ","))
}
if len(f.Severities) > 0 {
sevs := make([]string, len(f.Severities))
for i, s := range f.Severities {
sevs[i] = s.String()
}
parts = append(parts, "severities="+strings.Join(sevs, ","))
}
if len(f.ProtocolTypes) > 0 {
pts := make([]string, len(f.ProtocolTypes))
for i, p := range f.ProtocolTypes {
pts[i] = p.String()
}
parts = append(parts, "types="+strings.Join(pts, ","))
}
if len(parts) == 0 {
return "filter=<nil>"
}
return "filter(" + strings.Join(parts, ", ") + ")"
}
-407
View File
@@ -1,407 +0,0 @@
package index
import (
"os"
"path/filepath"
"testing"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
"github.com/stretchr/testify/require"
)
func TestFilterMatches(t *testing.T) {
metadata := &Metadata{
ID: "test-template-1",
FilePath: "/templates/cves/2021/CVE-2021-1234.yaml",
Name: "Test CVE Template",
Authors: []string{"pdteam", "geeknik"},
Tags: []string{"cve", "rce", "apache"},
Severity: "critical",
ProtocolType: "http",
}
t.Run("Empty filter matches all", func(t *testing.T) {
filter := &Filter{}
require.True(t, filter.Matches(metadata))
require.True(t, filter.IsEmpty())
})
t.Run("Author filter - match", func(t *testing.T) {
filter := &Filter{Authors: []string{"pdteam"}}
require.True(t, filter.Matches(metadata))
})
t.Run("Author filter - no match", func(t *testing.T) {
filter := &Filter{Authors: []string{"unknown"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Multiple authors - OR logic", func(t *testing.T) {
filter := &Filter{Authors: []string{"unknown", "geeknik"}}
require.True(t, filter.Matches(metadata))
})
t.Run("Tag filter - match", func(t *testing.T) {
filter := &Filter{Tags: []string{"cve"}}
require.True(t, filter.Matches(metadata))
})
t.Run("Tag filter - no match", func(t *testing.T) {
filter := &Filter{Tags: []string{"xss"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Exclude tags - match", func(t *testing.T) {
filter := &Filter{ExcludeTags: []string{"rce"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Include tags overrides exclude", func(t *testing.T) {
filter := &Filter{
ExcludeTags: []string{"rce"},
IncludeTags: []string{"cve"},
}
require.True(t, filter.Matches(metadata))
})
t.Run("ID filter - exact match", func(t *testing.T) {
filter := &Filter{IDs: []string{"test-template-1"}}
require.True(t, filter.Matches(metadata))
})
t.Run("ID filter - wildcard match", func(t *testing.T) {
filter := &Filter{IDs: []string{"test-*"}}
require.True(t, filter.Matches(metadata))
})
t.Run("ID filter - no match", func(t *testing.T) {
filter := &Filter{IDs: []string{"other-*"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Exclude ID - exact match", func(t *testing.T) {
filter := &Filter{ExcludeIDs: []string{"test-template-1"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Exclude ID - wildcard match", func(t *testing.T) {
filter := &Filter{ExcludeIDs: []string{"test-*"}}
require.False(t, filter.Matches(metadata))
})
t.Run("Severity filter - match", func(t *testing.T) {
filter := &Filter{Severities: []severity.Severity{severity.Critical}}
require.True(t, filter.Matches(metadata))
})
t.Run("Severity filter - no match", func(t *testing.T) {
filter := &Filter{Severities: []severity.Severity{severity.High, severity.Medium}}
require.False(t, filter.Matches(metadata))
})
t.Run("Exclude severity - match", func(t *testing.T) {
filter := &Filter{ExcludeSeverities: []severity.Severity{severity.Critical}}
require.False(t, filter.Matches(metadata))
})
t.Run("Protocol type filter - match", func(t *testing.T) {
filter := &Filter{ProtocolTypes: []types.ProtocolType{types.HTTPProtocol}}
require.True(t, filter.Matches(metadata))
})
t.Run("Protocol type filter - no match", func(t *testing.T) {
filter := &Filter{ProtocolTypes: []types.ProtocolType{types.DNSProtocol}}
require.False(t, filter.Matches(metadata))
})
t.Run("Exclude protocol type - match", func(t *testing.T) {
filter := &Filter{ExcludeProtocolTypes: []types.ProtocolType{types.HTTPProtocol}}
require.False(t, filter.Matches(metadata))
})
t.Run("Include templates - path match", func(t *testing.T) {
filter := &Filter{
ExcludeTags: []string{"cve"},
IncludeTemplates: []string{"/templates/cves/"},
}
require.True(t, filter.Matches(metadata))
})
t.Run("Exclude templates - path match", func(t *testing.T) {
filter := &Filter{
ExcludeTemplates: []string{"/templates/cves/"},
}
require.False(t, filter.Matches(metadata))
})
t.Run("Complex filter - all match", func(t *testing.T) {
filter := &Filter{
Authors: []string{"pdteam"},
Tags: []string{"cve"},
Severities: []severity.Severity{severity.Critical},
ProtocolTypes: []types.ProtocolType{types.HTTPProtocol},
}
require.True(t, filter.Matches(metadata))
})
t.Run("Complex filter - AND logic across types", func(t *testing.T) {
filter := &Filter{
Authors: []string{"pdteam"}, // matches
Tags: []string{"xss"}, // doesn't match
Severities: []severity.Severity{severity.Critical}, // matches
}
// With AND logic across filter types, doesn't match because tags don't match
// even though author and severity match
require.False(t, filter.Matches(metadata))
})
t.Run("Complex filter - no match at all", func(t *testing.T) {
filter := &Filter{
Authors: []string{"unknown"}, // doesn't match
Tags: []string{"xss"}, // doesn't match
Severities: []severity.Severity{severity.Low}, // doesn't match
}
require.False(t, filter.Matches(metadata))
})
}
func TestMatchesPath(t *testing.T) {
tests := []struct {
name string
path string
pattern string
expected bool
}{
{"exact match", "/templates/cves/2021/test.yaml", "/templates/cves/2021/test.yaml", true},
{"directory prefix", "/templates/cves/2021/test.yaml", "/templates/cves", true},
{"directory with slash", "/templates/cves/2021/test.yaml", "/templates/cves/", true},
{"no match", "/templates/cves/2021/test.yaml", "/templates/exploits", false},
{"wildcard match", "/templates/cves/2021/test.yaml", "/templates/*/2021/*.yaml", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := matchesPath(tt.path, tt.pattern)
require.Equal(t, tt.expected, result)
})
}
}
func TestMatchesID(t *testing.T) {
tests := []struct {
name string
id string
pattern string
expected bool
}{
{"exact match", "CVE-2021-1234", "CVE-2021-1234", true},
{"wildcard prefix", "CVE-2021-1234", "CVE-*", true},
{"wildcard suffix", "CVE-2021-1234", "*-1234", true},
{"wildcard middle", "CVE-2021-1234", "CVE-*-1234", true},
{"no match", "CVE-2021-1234", "CVE-2022-*", false},
{"partial no match", "CVE-2021-1234", "CVE-2021-12", false},
{"case insensitive exact", "cve-2021-1234", "CVE-2021-1234", true},
{"case insensitive wildcard", "CVE-2021-1234", "cve-*", true},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := matchesID(tt.id, tt.pattern)
require.Equal(t, tt.expected, result)
})
}
}
func TestUnmarshalFilter(t *testing.T) {
filter, err := UnmarshalFilter(
[]string{"author1", "author2"},
[]string{"tag1", "tag2"},
[]string{"exclude-tag"},
[]string{"include-tag"},
[]string{"id1", "id2*"},
[]string{"exclude-id*"},
[]string{"/include/path"},
[]string{"/exclude/path"},
[]string{"critical", "high"},
[]string{"info"},
[]string{"http", "dns"},
[]string{"file"},
)
require.NoError(t, err)
require.NotNil(t, filter)
require.Equal(t, []string{"author1", "author2"}, filter.Authors)
require.Equal(t, []string{"tag1", "tag2"}, filter.Tags)
require.Equal(t, []string{"exclude-tag"}, filter.ExcludeTags)
require.Equal(t, []string{"include-tag"}, filter.IncludeTags)
require.Equal(t, []string{"id1", "id2*"}, filter.IDs)
require.Equal(t, []string{"exclude-id*"}, filter.ExcludeIDs)
require.Equal(t, []string{"/include/path"}, filter.IncludeTemplates)
require.Equal(t, []string{"/exclude/path"}, filter.ExcludeTemplates)
require.Len(t, filter.Severities, 2)
require.Contains(t, filter.Severities, severity.Critical)
require.Contains(t, filter.Severities, severity.High)
require.Len(t, filter.ExcludeSeverities, 1)
require.Contains(t, filter.ExcludeSeverities, severity.Info)
require.Len(t, filter.ProtocolTypes, 2)
require.Contains(t, filter.ProtocolTypes, types.HTTPProtocol)
require.Contains(t, filter.ProtocolTypes, types.DNSProtocol)
require.Len(t, filter.ExcludeProtocolTypes, 1)
require.Contains(t, filter.ExcludeProtocolTypes, types.FileProtocol)
}
func TestIndexFilter(t *testing.T) {
tmpDir := t.TempDir()
idx, err := NewIndex(tmpDir)
require.NoError(t, err)
// Create test templates and metadata
templates := []struct {
id string
path string
authors []string
tags []string
severity string
protocol string
}{
{"cve-2021-1", "/templates/cves/CVE-2021-1.yaml", []string{"pdteam"}, []string{"cve", "rce"}, "critical", "http"},
{"cve-2021-2", "/templates/cves/CVE-2021-2.yaml", []string{"pdteam"}, []string{"cve", "xss"}, "high", "http"},
{"exploit-1", "/templates/exploits/exploit-1.yaml", []string{"geeknik"}, []string{"exploit"}, "medium", "dns"},
{"info-1", "/templates/info/info-1.yaml", []string{"author1"}, []string{"info"}, "info", "http"},
}
for _, tmpl := range templates {
tmpFile := filepath.Join(tmpDir, filepath.Base(tmpl.path))
err := os.WriteFile(tmpFile, []byte("id: "+tmpl.id), 0644)
require.NoError(t, err)
metadata := &Metadata{
ID: tmpl.id,
FilePath: tmpFile,
Authors: tmpl.authors,
Tags: tmpl.tags,
Severity: tmpl.severity,
ProtocolType: tmpl.protocol,
}
idx.Set(tmpl.path, metadata)
}
t.Run("No filter returns all", func(t *testing.T) {
results := idx.Filter(nil)
require.Len(t, results, 4)
})
t.Run("Filter by author", func(t *testing.T) {
filter := &Filter{Authors: []string{"pdteam"}}
results := idx.Filter(filter)
require.Len(t, results, 2)
})
t.Run("Filter by tag", func(t *testing.T) {
filter := &Filter{Tags: []string{"cve"}}
results := idx.Filter(filter)
require.Len(t, results, 2)
})
t.Run("Filter by severity", func(t *testing.T) {
filter := &Filter{Severities: []severity.Severity{severity.Critical}}
results := idx.Filter(filter)
require.Len(t, results, 1)
})
t.Run("Filter by protocol type", func(t *testing.T) {
filter := &Filter{ProtocolTypes: []types.ProtocolType{types.HTTPProtocol}}
results := idx.Filter(filter)
require.Len(t, results, 3)
})
t.Run("Exclude by severity", func(t *testing.T) {
filter := &Filter{ExcludeSeverities: []severity.Severity{severity.Info}}
results := idx.Filter(filter)
require.Len(t, results, 3)
})
t.Run("Exclude by tag", func(t *testing.T) {
filter := &Filter{ExcludeTags: []string{"info"}}
results := idx.Filter(filter)
require.Len(t, results, 3)
})
t.Run("Complex filter", func(t *testing.T) {
filter := &Filter{
Tags: []string{"cve"},
Severities: []severity.Severity{severity.Critical, severity.High},
ExcludeSeverities: []severity.Severity{severity.Info},
}
results := idx.Filter(filter)
require.Len(t, results, 2)
})
t.Run("Count with filter", func(t *testing.T) {
filter := &Filter{Tags: []string{"cve"}}
count := idx.Count(filter)
require.Equal(t, 2, count)
})
t.Run("Count without filter", func(t *testing.T) {
count := idx.Count(nil)
require.Equal(t, 4, count)
})
}
func TestIndexFilterFunc(t *testing.T) {
tmpDir := t.TempDir()
idx, err := NewIndex(tmpDir)
require.NoError(t, err)
// Add test metadata
for i := 0; i < 5; i++ {
metadata := &Metadata{
ID: "test-" + string(rune('a'+i)),
FilePath: "/tmp/test.yaml",
Severity: "high",
}
if i%2 == 0 {
metadata.Tags = []string{"even"}
} else {
metadata.Tags = []string{"odd"}
}
idx.Set("/tmp/test-"+string(rune('a'+i))+".yaml", metadata)
}
t.Run("Custom filter function", func(t *testing.T) {
results := idx.FilterFunc(func(m *Metadata) bool {
return m.HasTag("even")
})
require.Len(t, results, 3) // 0, 2, 4
})
t.Run("Nil filter function returns all", func(t *testing.T) {
results := idx.FilterFunc(nil)
require.Len(t, results, 5)
})
}
func TestFilterString(t *testing.T) {
filter := &Filter{
Authors: []string{"author1", "author2"},
Tags: []string{"tag1"},
Severities: []severity.Severity{severity.Critical, severity.High},
ProtocolTypes: []types.ProtocolType{types.HTTPProtocol},
}
str := filter.String()
require.Contains(t, str, "authors=")
require.Contains(t, str, "tags=")
require.Contains(t, str, "severities=")
require.Contains(t, str, "types=")
emptyFilter := &Filter{}
require.Equal(t, "filter=<nil>", emptyFilter.String())
}
-352
View File
@@ -1,352 +0,0 @@
package index
import (
"encoding/gob"
"maps"
"os"
"path/filepath"
"sync"
"github.com/maypok86/otter/v2"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
folderutil "github.com/projectdiscovery/utils/folder"
)
const (
// IndexFileName is the name of the persistent cache file.
IndexFileName = "index.gob"
// IndexVersion is the schema version for cache invalidation on breaking
// changes.
IndexVersion = 1
// DefaultMaxSize is the default maximum number of templates to cache.
DefaultMaxSize = 50000
// DefaultMaxWeight is the default maximum weight of the cache.
DefaultMaxWeight = DefaultMaxSize * 800 // ~40MB assuming ~800B/entry
)
// Index represents a cache for template metadata.
type Index struct {
cache *otter.Cache[string, *Metadata]
cacheFile string
mu sync.RWMutex
version int
}
// cacheSnapshot represents the serialized cache structure.
type cacheSnapshot struct {
Version int `gob:"version"`
Data map[string]*Metadata `gob:"data"`
}
// NewIndex creates a new template metadata cache with the given options.
func NewIndex(cacheDir string) (*Index, error) {
if cacheDir == "" {
cacheDir = folderutil.AppCacheDirOrDefault(".nuclei-cache", config.BinaryName)
}
if err := os.MkdirAll(cacheDir, 0755); err != nil {
return nil, err
}
cacheFile := filepath.Join(cacheDir, IndexFileName)
// NOTE(dwisiswant0): Build cache with adaptive sizing based on memory cost.
opts := &otter.Options[string, *Metadata]{
MaximumWeight: uint64(DefaultMaxWeight),
Weigher: func(key string, value *Metadata) uint32 {
if value == nil {
return uint32(len(key))
}
weight := len(key)
weight += len(value.ID)
weight += len(value.FilePath)
weight += 24 // ModTime is time.Time (24B)
weight += len(value.Name)
weight += len(value.Severity)
weight += len(value.ProtocolType)
weight += len(value.TemplateVerifier)
for _, author := range value.Authors {
weight += len(author)
}
for _, tag := range value.Tags {
weight += len(tag)
}
return uint32(weight)
},
}
cache, err := otter.New(opts)
if err != nil {
return nil, err
}
c := &Index{
cache: cache,
cacheFile: cacheFile,
version: IndexVersion,
}
return c, nil
}
// NewDefaultIndex creates a index with default settings in the default cache
// directory.
func NewDefaultIndex() (*Index, error) {
return NewIndex("")
}
// Get retrieves metadata for a template path, validating freshness via mtime.
func (i *Index) Get(path string) (*Metadata, bool) {
i.mu.RLock()
defer i.mu.RUnlock()
metadata, found := i.cache.GetIfPresent(path)
if !found {
return nil, false
}
if !metadata.IsValid() {
go i.Delete(path)
return nil, false
}
return metadata, true
}
// Set stores metadata for a template path.
//
// The caller is responsible for ensuring the metadata is valid and contains
// the correct checksum before calling this method.
// Use [SetFromTemplate] for automatic extraction and checksum computation.
//
// Returns the metadata and whether it was successfully cached (false if evicted).
func (i *Index) Set(path string, metadata *Metadata) (*Metadata, bool) {
i.mu.Lock()
defer i.mu.Unlock()
return i.cache.Set(path, metadata)
}
// SetFromTemplate extracts metadata from a parsed template and stores it.
//
// Returns the metadata and whether it was successfully cached. The metadata is
// always returned (even on checksum failure) for immediate filtering use.
// Returns false if the metadata was not cached (e.g., set, evicted).
func (i *Index) SetFromTemplate(path string, tpl *templates.Template) (*Metadata, bool) {
metadata := NewMetadataFromTemplate(path, tpl)
info, err := os.Stat(path)
if err != nil {
return metadata, false
}
metadata.ModTime = info.ModTime()
if i.cache == nil {
return metadata, false
}
return i.Set(path, metadata)
}
// Has checks if metadata exists for a path without validation.
func (i *Index) Has(path string) bool {
i.mu.RLock()
defer i.mu.RUnlock()
_, found := i.cache.GetIfPresent(path)
return found
}
// Delete removes metadata for a path.
func (i *Index) Delete(path string) {
i.mu.Lock()
defer i.mu.Unlock()
i.cache.Invalidate(path)
}
// Size returns the number of cached entries.
func (i *Index) Size() int {
i.mu.RLock()
defer i.mu.RUnlock()
return i.cache.EstimatedSize()
}
// Clear removes all cached entries.
func (i *Index) Clear() {
i.mu.Lock()
defer i.mu.Unlock()
i.cache.InvalidateAll()
}
// Save persists the cache to disk using gob encoding.
func (i *Index) Save() error {
i.mu.RLock()
defer i.mu.RUnlock()
snapshot := &cacheSnapshot{
Version: i.version,
Data: make(map[string]*Metadata),
}
maps.Insert(snapshot.Data, i.cache.All())
// NOTE(dwisiswant0): write to temp for atomic op.
tmpFile := i.cacheFile + ".tmp"
file, err := os.Create(tmpFile)
if err != nil {
return err
}
encoder := gob.NewEncoder(file)
if err := encoder.Encode(snapshot); err != nil {
_ = file.Close()
_ = os.Remove(tmpFile)
return err
}
if err := file.Close(); err != nil {
_ = os.Remove(tmpFile)
return err
}
if err := os.Rename(tmpFile, i.cacheFile); err != nil {
_ = os.Remove(tmpFile)
return err
}
return nil
}
// Load loads the cache from disk using gob decoding.
func (i *Index) Load() error {
file, err := os.Open(i.cacheFile)
if err != nil {
if os.IsNotExist(err) {
return nil
}
return err
}
defer func() { _ = file.Close() }()
var snapshot cacheSnapshot
decoder := gob.NewDecoder(file)
if err := decoder.Decode(&snapshot); err != nil {
_ = file.Close()
_ = os.Remove(i.cacheFile)
return nil
}
if snapshot.Version != i.version {
_ = file.Close()
_ = os.Remove(i.cacheFile)
return nil
}
i.mu.Lock()
defer i.mu.Unlock()
for key, value := range snapshot.Data {
i.cache.Set(key, value)
}
return nil
}
// Filter returns all template paths that match the given filter criteria.
func (i *Index) Filter(filter *Filter) []string {
if filter == nil || filter.IsEmpty() {
return i.All()
}
i.mu.RLock()
defer i.mu.RUnlock()
var matched []string
for path, metadata := range i.cache.All() {
if filter.Matches(metadata) {
matched = append(matched, path)
}
}
return matched
}
// FilterFunc returns all template paths that match the given filter function.
func (i *Index) FilterFunc(fn FilterFunc) []string {
if fn == nil {
return i.All()
}
i.mu.RLock()
defer i.mu.RUnlock()
var matched []string
for path, metadata := range i.cache.All() {
if fn(metadata) {
matched = append(matched, path)
}
}
return matched
}
// All returns all template paths in the index.
func (i *Index) All() []string {
i.mu.RLock()
defer i.mu.RUnlock()
paths := make([]string, 0, i.cache.EstimatedSize())
for path := range i.cache.All() {
paths = append(paths, path)
}
return paths
}
// GetAll returns all metadata entries in the index.
func (i *Index) GetAll() map[string]*Metadata {
i.mu.RLock()
defer i.mu.RUnlock()
result := maps.Collect(i.cache.All())
return result
}
// Count returns the number of templates matching the filter.
func (i *Index) Count(filter *Filter) int {
if filter == nil || filter.IsEmpty() {
return i.Size()
}
i.mu.RLock()
defer i.mu.RUnlock()
count := 0
for _, metadata := range i.cache.All() {
if filter.Matches(metadata) {
count++
}
}
return count
}
-737
View File
@@ -1,737 +0,0 @@
package index
import (
"fmt"
"os"
"path/filepath"
"testing"
"time"
"github.com/projectdiscovery/nuclei/v3/pkg/model"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/stringslice"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/code"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
"github.com/stretchr/testify/require"
)
func TestNewIndex(t *testing.T) {
t.Run("with custom directory", func(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err, "Failed to create cache with custom directory")
require.NotNil(t, cache, "Cache should not be nil")
require.Equal(t, filepath.Join(tmpDir, IndexFileName), cache.cacheFile)
require.Equal(t, IndexVersion, cache.version)
})
t.Run("with default directory", func(t *testing.T) {
cache, err := NewDefaultIndex()
require.NoError(t, err, "Failed to create cache with default directory")
require.NotNil(t, cache, "Cache should not be nil")
})
}
func TestCacheBasicOperations(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
metadata := &Metadata{
ID: "concurrent-test",
FilePath: "/tmp/concurrent.yaml",
}
t.Run("Set and Has", func(t *testing.T) {
cache.Set(metadata.FilePath, metadata)
require.Equal(t, 1, cache.Size(), "Cache size should be 1 after Set")
require.True(t, cache.Has(metadata.FilePath), "Cache should contain the path after Set")
require.False(t, cache.Has("/nonexistent"), "Cache should not contain nonexistent path")
})
t.Run("Get with validation", func(t *testing.T) {
// Get should fail validation for nonexistent file
retrieved, found := cache.Get(metadata.FilePath)
require.False(t, found, "Get should fail validation for nonexistent file")
require.Nil(t, retrieved, "Retrieved metadata should be nil for invalid entry")
})
t.Run("Delete", func(t *testing.T) {
cache.Set(metadata.FilePath, metadata)
require.True(t, cache.Has(metadata.FilePath), "Cache should contain path before Delete")
cache.Delete(metadata.FilePath)
require.False(t, cache.Has(metadata.FilePath), "Cache should not contain path after Delete")
})
t.Run("Clear", func(t *testing.T) {
cache.Set(metadata.FilePath, metadata)
cache.Set("/tmp/test2.yaml", &Metadata{ID: "test2", FilePath: "/tmp/test2.yaml"})
require.True(t, cache.Size() > 0, "Cache should have entries before Clear")
cache.Clear()
require.Equal(t, 0, cache.Size(), "Cache should be empty after Clear")
})
}
func TestCachePersistence(t *testing.T) {
tmpDir := t.TempDir()
metadata1 := &Metadata{
ID: "persist-test-1",
FilePath: "/tmp/persist1.yaml",
Name: "Persistence Test 1",
Authors: []string{"tester"},
Tags: []string{"test"},
Severity: "medium",
ProtocolType: "dns",
}
metadata2 := &Metadata{
ID: "persist-test-2",
FilePath: "/tmp/persist2.yaml",
Name: "Persistence Test 2",
Authors: []string{"tester2"},
Tags: []string{"cve"},
Severity: "critical",
ProtocolType: "http",
}
t.Run("Save and Load", func(t *testing.T) {
// Create cache and add entries
cache1, err := NewIndex(tmpDir)
require.NoError(t, err)
cache1.Set(metadata1.FilePath, metadata1)
cache1.Set(metadata2.FilePath, metadata2)
require.Equal(t, 2, cache1.Size())
// Save to disk
err = cache1.Save()
require.NoError(t, err, "Failed to save cache")
// Verify cache file exists
cacheFile := filepath.Join(tmpDir, IndexFileName)
stat, err := os.Stat(cacheFile)
require.NoError(t, err, "Cache file should exist")
require.Greater(t, stat.Size(), int64(0), "Cache file should not be empty")
// Create new cache and load
cache2, err := NewIndex(tmpDir)
require.NoError(t, err)
require.Equal(t, 0, cache2.Size(), "New cache should be empty before Load")
err = cache2.Load()
require.NoError(t, err, "Failed to load cache")
// Verify data was loaded
require.Equal(t, 2, cache2.Size(), "Loaded cache should have 2 entries")
require.True(t, cache2.Has(metadata1.FilePath), "Loaded cache should contain first entry")
require.True(t, cache2.Has(metadata2.FilePath), "Loaded cache should contain second entry")
})
t.Run("Load non-existent cache", func(t *testing.T) {
emptyDir := t.TempDir()
cache, err := NewIndex(emptyDir)
require.NoError(t, err)
// Loading non-existent cache should not error
err = cache.Load()
require.NoError(t, err, "Loading non-existent cache should not error")
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading non-existent file")
})
t.Run("Atomic save", func(t *testing.T) {
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
cache.Set(metadata1.FilePath, metadata1)
err = cache.Save()
require.NoError(t, err)
// Verify no .tmp file left behind
tmpFile := filepath.Join(tmpDir, IndexFileName+".tmp")
_, err = os.Stat(tmpFile)
require.True(t, os.IsNotExist(err), "Temporary file should not exist after save")
// Verify actual cache file exists
cacheFile := filepath.Join(tmpDir, IndexFileName)
_, err = os.Stat(cacheFile)
require.NoError(t, err, "Cache file should exist")
})
}
func TestIndexVersionMismatch(t *testing.T) {
tmpDir := t.TempDir()
// Create cache with current version
cache1, err := NewIndex(tmpDir)
require.NoError(t, err)
metadata := &Metadata{
ID: "version-test",
FilePath: "/tmp/version.yaml",
}
cache1.Set(metadata.FilePath, metadata)
// Save with current version
err = cache1.Save()
require.NoError(t, err)
// Manually modify version and save again
cache1.version = 999
err = cache1.Save()
require.NoError(t, err)
// Try to load with different version
cache2, err := NewIndex(tmpDir)
require.NoError(t, err)
// Load should succeed but cache should be empty (version mismatch)
err = cache2.Load()
require.NoError(t, err, "Load should not error on version mismatch")
require.Equal(t, 0, cache2.Size(), "Cache should be empty after version mismatch")
}
func TestCacheCorruptedFile(t *testing.T) {
tmpDir := t.TempDir()
cacheFile := filepath.Join(tmpDir, IndexFileName)
// Create corrupted cache file
err := os.WriteFile(cacheFile, []byte("corrupted data that is not valid gob"), 0644)
require.NoError(t, err)
// Try to load corrupted cache
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
err = cache.Load()
require.NoError(t, err, "Load should not error on corrupted cache")
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading corrupted file")
// Corrupted file should be removed
_, err = os.Stat(cacheFile)
require.True(t, os.IsNotExist(err), "Corrupted cache file should be removed")
}
func TestMetadataValidation(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "test.yaml")
t.Run("Valid metadata", func(t *testing.T) {
// Create a test file
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
require.NoError(t, err)
info, err := os.Stat(tmpFile)
require.NoError(t, err)
// Create metadata with correct checksum
metadata := &Metadata{
ID: "test",
FilePath: tmpFile,
ModTime: info.ModTime(),
}
// Should be valid
require.True(t, metadata.IsValid(), "Metadata should be valid for unchanged file")
})
t.Run("Invalid metadata after file modification", func(t *testing.T) {
// Create the test file first to ensure it exists in this subtest
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
require.NoError(t, err)
// Set file ModTime to past to ensure modification is detectable
oldTime := time.Now().Add(-2 * time.Second)
err = os.Chtimes(tmpFile, oldTime, oldTime)
require.NoError(t, err)
info, err := os.Stat(tmpFile)
require.NoError(t, err)
metadata := &Metadata{
ID: "test",
FilePath: tmpFile,
ModTime: info.ModTime(),
}
// Modify file
err = os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Modified"), 0644)
require.NoError(t, err)
// Should now be invalid
require.False(t, metadata.IsValid(), "Metadata should be invalid after file modification")
})
t.Run("Invalid metadata for deleted file", func(t *testing.T) {
// Create the test file first to ensure it exists in this subtest
err := os.WriteFile(tmpFile, []byte("id: test\ninfo:\n name: Test"), 0644)
require.NoError(t, err)
info, err := os.Stat(tmpFile)
require.NoError(t, err)
metadata := &Metadata{
ID: "test",
FilePath: tmpFile,
ModTime: info.ModTime(),
}
// Delete file
err = os.Remove(tmpFile)
require.NoError(t, err)
// Should be invalid
require.False(t, metadata.IsValid(), "Metadata should be invalid for deleted file")
})
}
func TestSetFromTemplate(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "extract.yaml")
// Create a test file
err := os.WriteFile(tmpFile, []byte("id: extract-test"), 0644)
require.NoError(t, err)
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
t.Run("Basic metadata extraction", func(t *testing.T) {
template := &templates.Template{
ID: "extract-test",
Info: model.Info{
Name: "Extract Test Template",
Authors: stringslice.StringSlice{Value: "author1,author2"},
Tags: stringslice.StringSlice{Value: "tag1,tag2"},
Description: "Test description",
SeverityHolder: severity.Holder{
Severity: severity.High,
},
},
SelfContained: true,
Verified: true,
TemplateVerifier: "test-verifier",
}
metadata, ok := cache.SetFromTemplate(tmpFile, template)
require.True(t, ok, "Failed to set metadata from template")
require.NotNil(t, metadata, "Metadata should not be nil")
// Verify core fields
require.Equal(t, "extract-test", metadata.ID)
require.Equal(t, tmpFile, metadata.FilePath)
// Verify Info fields
require.Equal(t, "Extract Test Template", metadata.Name)
require.Equal(t, []string{"author1,author2"}, metadata.Authors)
require.Equal(t, []string{"tag1,tag2"}, metadata.Tags)
require.Equal(t, "high", metadata.Severity)
// Verify flags
require.True(t, metadata.Verified)
require.Equal(t, "test-verifier", metadata.TemplateVerifier)
})
t.Run("HTTP protocol detection", func(t *testing.T) {
// Create a separate test file for this test
httpFile := filepath.Join(tmpDir, "http-test.yaml")
err := os.WriteFile(httpFile, []byte("id: http-test"), 0644)
require.NoError(t, err)
template := &templates.Template{
ID: "http-test",
Info: model.Info{
Name: "HTTP Test",
Authors: stringslice.StringSlice{Value: "tester"},
SeverityHolder: severity.Holder{
Severity: severity.Medium,
},
},
RequestsHTTP: []*http.Request{{Method: http.HTTPMethodTypeHolder{MethodType: http.HTTPGet}}},
}
metadata, ok := cache.SetFromTemplate(httpFile, template)
require.True(t, ok)
require.NotNil(t, metadata)
require.Equal(t, "http", metadata.ProtocolType)
})
t.Run("Extract with missing file", func(t *testing.T) {
template := &templates.Template{
ID: "missing-test",
Info: model.Info{
Name: "Missing File Test",
Authors: stringslice.StringSlice{Value: "tester"},
SeverityHolder: severity.Holder{
Severity: severity.Low,
},
},
}
metadata, ok := cache.SetFromTemplate("/nonexistent/file.yaml", template)
require.False(t, ok, "Should return false for nonexistent file")
require.NotNil(t, metadata, "Metadata should still be returned")
})
}
func TestMetadataMatchingHelpers(t *testing.T) {
metadata := &Metadata{
Tags: []string{"cve", "rce", "apache"},
Authors: []string{"pdteam", "geeknik"},
Severity: "critical",
ProtocolType: "http",
}
t.Run("HasTag", func(t *testing.T) {
require.True(t, metadata.HasTag("cve"))
require.True(t, metadata.HasTag("rce"))
require.True(t, metadata.HasTag("apache"))
require.False(t, metadata.HasTag("xxe"))
require.False(t, metadata.HasTag(""))
})
t.Run("HasAuthor", func(t *testing.T) {
require.True(t, metadata.HasAuthor("pdteam"))
require.True(t, metadata.HasAuthor("geeknik"))
require.False(t, metadata.HasAuthor("unknown"))
require.False(t, metadata.HasAuthor(""))
})
t.Run("MatchesSeverity", func(t *testing.T) {
require.True(t, metadata.MatchesSeverity(severity.Critical))
require.False(t, metadata.MatchesSeverity(severity.High))
require.False(t, metadata.MatchesSeverity(severity.Medium))
require.False(t, metadata.MatchesSeverity(severity.Low))
require.False(t, metadata.MatchesSeverity(severity.Info))
})
t.Run("MatchesProtocol", func(t *testing.T) {
require.True(t, metadata.MatchesProtocol(types.HTTPProtocol))
require.False(t, metadata.MatchesProtocol(types.DNSProtocol))
require.False(t, metadata.MatchesProtocol(types.FileProtocol))
require.False(t, metadata.MatchesProtocol(types.NetworkProtocol))
})
t.Run("Empty metadata", func(t *testing.T) {
emptyMetadata := &Metadata{}
require.False(t, emptyMetadata.HasTag("any"))
require.False(t, emptyMetadata.HasAuthor("any"))
})
}
func TestCacheConcurrency(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
// Test concurrent writes
t.Run("Concurrent Set", func(t *testing.T) {
done := make(chan bool)
for i := 0; i < 10; i++ {
go func(id int) {
metadata := &Metadata{
ID: string(rune('a' + id)),
FilePath: filepath.Join("/tmp", string(rune('a'+id))+".yaml"),
}
cache.Set(metadata.FilePath, metadata)
done <- true
}(i)
}
// Wait for all goroutines
for i := 0; i < 10; i++ {
<-done
}
require.Equal(t, 10, cache.Size(), "All concurrent writes should succeed")
})
// Test concurrent reads
t.Run("Concurrent Has", func(t *testing.T) {
metadata := &Metadata{
ID: "concurrent-test",
FilePath: "/tmp/concurrent.yaml",
}
cache.Set(metadata.FilePath, metadata)
done := make(chan bool)
for i := 0; i < 20; i++ {
go func() {
_ = cache.Has(metadata.FilePath)
done <- true
}()
}
// Wait for all goroutines
for i := 0; i < 20; i++ {
<-done
}
})
}
func TestCacheSize(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
require.Equal(t, 0, cache.Size(), "New cache should have size 0")
// Add entries
for i := 0; i < 5; i++ {
metadata := &Metadata{
ID: string(rune('a' + i)),
FilePath: filepath.Join("/tmp", string(rune('a'+i))+".yaml"),
}
cache.Set(metadata.FilePath, metadata)
}
require.Equal(t, 5, cache.Size(), "Cache should have size 5 after adding 5 entries")
// Delete entries
cache.Delete(filepath.Join("/tmp", "a.yaml"))
cache.Delete(filepath.Join("/tmp", "b.yaml"))
require.Equal(t, 3, cache.Size(), "Cache should have size 3 after deleting 2 entries")
// Clear cache
cache.Clear()
require.Equal(t, 0, cache.Size(), "Cache should have size 0 after Clear")
}
func TestCacheGetWithValidFile(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
// Create a real file for testing validation
tmpFile := filepath.Join(tmpDir, "test.yaml")
err = os.WriteFile(tmpFile, []byte("id: test"), 0644)
require.NoError(t, err)
info, err := os.Stat(tmpFile)
require.NoError(t, err)
metadata := &Metadata{
ID: "test",
FilePath: tmpFile,
ModTime: info.ModTime(),
Name: "Test Template",
}
// Set and get should work with valid file
cache.Set(metadata.FilePath, metadata)
retrieved, found := cache.Get(metadata.FilePath)
require.True(t, found, "Should find entry with valid file")
require.NotNil(t, retrieved, "Retrieved metadata should not be nil")
require.Equal(t, metadata.ID, retrieved.ID)
}
func TestCacheSaveErrorHandling(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
metadata := &Metadata{
ID: "test",
FilePath: filepath.Join("/tmp", "test.yaml"),
}
cache.Set(metadata.FilePath, metadata)
// Create a directory where the temp file would be created to force an error
// The Save method creates a file at cacheFile + ".tmp"
conflictPath := filepath.Join(tmpDir, IndexFileName+".tmp")
err = os.Mkdir(conflictPath, 0755)
require.NoError(t, err)
err = cache.Save()
require.Error(t, err, "Save should fail when temp file cannot be created")
}
func TestNewCacheWithInvalidDirectory(t *testing.T) {
// Try to create cache in a file path (should fail)
tmpFile := filepath.Join(t.TempDir(), "file.txt")
err := os.WriteFile(tmpFile, []byte("test"), 0644)
require.NoError(t, err)
cache, err := NewIndex(tmpFile)
require.Error(t, err, "NewCache should fail when path is a file")
require.Nil(t, cache, "Cache should be nil on error")
}
func TestCacheLoadCorruptedRemoval(t *testing.T) {
tmpDir := t.TempDir()
cacheFile := filepath.Join(tmpDir, IndexFileName)
// Create corrupted cache file with invalid gob data
err := os.WriteFile(cacheFile, []byte("this is not valid gob encoding at all!"), 0644)
require.NoError(t, err)
// Verify file exists before Load
_, err = os.Stat(cacheFile)
require.NoError(t, err, "Corrupted file should exist")
// Load should not error but should remove corrupted file
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
err = cache.Load()
require.NoError(t, err, "Load should not return error for corrupted file")
// Verify corrupted file was removed
_, err = os.Stat(cacheFile)
require.True(t, os.IsNotExist(err), "Corrupted file should be removed")
require.Equal(t, 0, cache.Size(), "Cache should be empty after loading corrupted file")
}
func TestMetadataExtractionWithNilClassification(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "test.yaml")
err := os.WriteFile(tmpFile, []byte("id: test"), 0644)
require.NoError(t, err)
template := &templates.Template{
ID: "nil-classification",
Info: model.Info{
Name: "Template without classification",
Authors: stringslice.StringSlice{Value: "tester"},
SeverityHolder: severity.Holder{
Severity: severity.Medium,
},
Classification: nil, // Explicitly nil
},
}
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
metadata, ok := cache.SetFromTemplate(tmpFile, template)
require.True(t, ok)
require.NotNil(t, metadata)
}
func TestCachePersistenceWithLargeDataset(t *testing.T) {
tmpDir := t.TempDir()
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
// Add 100 entries to test bulk operations
for i := 0; i < 100; i++ {
metadata := &Metadata{
ID: fmt.Sprintf("template-%d", i),
FilePath: filepath.Join("/tmp", fmt.Sprintf("template-%d.yaml", i)),
Name: fmt.Sprintf("Template %d", i),
Authors: []string{fmt.Sprintf("author%d", i)},
Tags: []string{"tag1", "tag2", "tag3"},
Severity: "high",
}
cache.Set(metadata.FilePath, metadata)
}
require.Equal(t, 100, cache.Size(), "Cache should contain 100 entries")
// Save to disk
err = cache.Save()
require.NoError(t, err)
// Load into new cache
cache2, err := NewIndex(tmpDir)
require.NoError(t, err)
err = cache2.Load()
require.NoError(t, err)
require.Equal(t, 100, cache2.Size(), "Loaded cache should contain 100 entries")
// Verify a sample entry
found := cache2.Has(filepath.Join("/tmp", "template-50.yaml"))
require.True(t, found, "Should find sample entry")
}
func TestMetadataHelperMethods(t *testing.T) {
metadata := &Metadata{
ID: "helper-test",
Tags: []string{},
Authors: []string{},
Severity: "",
ProtocolType: "",
}
t.Run("Empty tags", func(t *testing.T) {
require.False(t, metadata.HasTag("anytag"))
})
t.Run("Empty authors", func(t *testing.T) {
require.False(t, metadata.HasAuthor("anyauthor"))
})
t.Run("Empty severity", func(t *testing.T) {
require.False(t, metadata.MatchesSeverity(severity.Critical))
})
t.Run("Empty protocol", func(t *testing.T) {
require.False(t, metadata.MatchesProtocol(types.HTTPProtocol))
})
}
func TestMultipleProtocolsDetection(t *testing.T) {
tmpDir := t.TempDir()
tmpFile := filepath.Join(tmpDir, "multi.yaml")
err := os.WriteFile(tmpFile, []byte("id: multi"), 0644)
require.NoError(t, err)
// Template with multiple protocol types
template := &templates.Template{
ID: "multi-protocol",
Info: model.Info{
Name: "Multi Protocol Template",
Authors: stringslice.StringSlice{Value: "tester"},
SeverityHolder: severity.Holder{
Severity: severity.High,
},
},
RequestsHTTP: []*http.Request{{Method: http.HTTPMethodTypeHolder{MethodType: http.HTTPGet}}},
RequestsHeadless: []*headless.Request{{}},
RequestsCode: []*code.Request{{}},
}
cache, err := NewIndex(tmpDir)
require.NoError(t, err)
metadata, ok := cache.SetFromTemplate(tmpFile, template)
require.True(t, ok)
require.NotNil(t, metadata)
require.Equal(t, "http", metadata.ProtocolType, "Primary protocol should be http")
}
func TestNewMetadataFromTemplate(t *testing.T) {
tmpl := &templates.Template{
ID: "test-template",
Info: model.Info{
Name: "Test Template",
Authors: stringslice.StringSlice{Value: []string{"author"}},
Tags: stringslice.StringSlice{Value: []string{"tag"}},
SeverityHolder: severity.Holder{
Severity: severity.Low,
},
},
Verified: true,
TemplateVerifier: "verifier",
}
path := "/tmp/test.yaml"
metadata := NewMetadataFromTemplate(path, tmpl)
require.Equal(t, tmpl.ID, metadata.ID)
require.Equal(t, path, metadata.FilePath)
require.Equal(t, tmpl.Info.Name, metadata.Name)
require.Equal(t, tmpl.Info.Authors.ToSlice(), metadata.Authors)
require.Equal(t, tmpl.Info.Tags.ToSlice(), metadata.Tags)
require.Equal(t, tmpl.Info.SeverityHolder.Severity.String(), metadata.Severity)
require.Equal(t, tmpl.Type().String(), metadata.ProtocolType)
require.Equal(t, tmpl.Verified, metadata.Verified)
require.Equal(t, tmpl.TemplateVerifier, metadata.TemplateVerifier)
}
-104
View File
@@ -1,104 +0,0 @@
package index
import (
"os"
"slices"
"time"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
)
// Metadata contains lightweight metadata extracted from a template.
type Metadata struct {
// ID is the unique identifier of the template.
ID string `gob:"id"`
// FilePath is the path to the template file.
FilePath string `gob:"file_path"`
// ModTime is the modification time of the template file.
ModTime time.Time `gob:"mod_time"`
// Name is the name of the template.
Name string `gob:"name"`
// Authors are the authors of the template.
Authors []string `gob:"authors"`
// Tags are the tags associated with the template.
Tags []string `gob:"tags"`
// Severity is the severity level of the template.
Severity string `gob:"severity"`
// ProtocolType is the primary protocol type of the template.
ProtocolType string `gob:"protocol_type"`
// Verified indicates whether the template is verified.
Verified bool `gob:"verified"`
// TemplateVerifier is the verifier used for the template.
TemplateVerifier string `gob:"verifier,omitempty"`
// NOTE(dwisiswant0): Consider adding more fields here in the future to
// enhance filtering caps w/o loading full templates, such as:
// `has_{code,headless,file}` to indicate presence of protocol-based
// requests, and/or classification fields (CVE, CWE, CVSS, EPSS), if needed.
//
// For maintainers: when adding new fields, don't forget to update the
// Weigher logic in [NewIndex] to account for the new fields in cache weight
// calculation, because it affects cache eviction behavior. Also, consider
// the impact on existing cached data and whether a [IndexVersion] bump is
// needed.
}
// NewMetadataFromTemplate creates a new metadata object from a template.
func NewMetadataFromTemplate(path string, tpl *templates.Template) *Metadata {
return &Metadata{
ID: tpl.ID,
FilePath: path,
Name: tpl.Info.Name,
Authors: tpl.Info.Authors.ToSlice(),
Tags: tpl.Info.Tags.ToSlice(),
Severity: tpl.Info.SeverityHolder.Severity.String(),
ProtocolType: tpl.Type().String(),
Verified: tpl.Verified,
TemplateVerifier: tpl.TemplateVerifier,
}
}
// IsValid checks if the cached metadata is still valid by comparing the file
// modification time.
func (m *Metadata) IsValid() bool {
info, err := os.Stat(m.FilePath)
if err != nil {
return false
}
return m.ModTime.Equal(info.ModTime())
}
// MatchesSeverity checks if the metadata matches the given severity.
func (m *Metadata) MatchesSeverity(sev severity.Severity) bool {
return m.Severity == sev.String()
}
// MatchesProtocol checks if the metadata matches the given protocol type.
func (m *Metadata) MatchesProtocol(protocolType types.ProtocolType) bool {
return m.ProtocolType == protocolType.String()
}
// HasTag checks if the metadata contains the given tag.
func (m *Metadata) HasTag(tag string) bool {
return slices.Contains(m.Tags, tag)
}
// HasAuthor checks if the metadata contains the given author.
func (m *Metadata) HasAuthor(author string) bool {
return slices.Contains(m.Authors, author)
}
+15 -15
View File
@@ -14,7 +14,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/types"
"github.com/projectdiscovery/retryablehttp-go"
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
"github.com/projectdiscovery/utils/errkit"
errorutil "github.com/projectdiscovery/utils/errors"
)
const (
@@ -33,27 +33,27 @@ type AITemplateResponse struct {
func getAIGeneratedTemplates(prompt string, options *types.Options) ([]string, error) {
prompt = strings.TrimSpace(prompt)
if len(prompt) < 5 {
return nil, errkit.Newf("Prompt is too short. Please provide a more descriptive prompt")
return nil, errorutil.New("Prompt is too short. Please provide a more descriptive prompt")
}
if len(prompt) > 3000 {
return nil, errkit.Newf("Prompt is too long. Please limit to 3000 characters")
return nil, errorutil.New("Prompt is too long. Please limit to 3000 characters")
}
template, templateID, err := generateAITemplate(prompt)
if err != nil {
return nil, errkit.Newf("Failed to generate template: %v", err)
return nil, errorutil.New("Failed to generate template: %v", err)
}
pdcpTemplateDir := filepath.Join(config.DefaultConfig.GetTemplateDir(), "pdcp")
if err := os.MkdirAll(pdcpTemplateDir, 0755); err != nil {
return nil, errkit.Newf("Failed to create pdcp template directory: %v", err)
return nil, errorutil.New("Failed to create pdcp template directory: %v", err)
}
templateFile := filepath.Join(pdcpTemplateDir, templateID+".yaml")
err = os.WriteFile(templateFile, []byte(template), 0644)
if err != nil {
return nil, errkit.Newf("Failed to generate template: %v", err)
return nil, errorutil.New("Failed to generate template: %v", err)
}
options.Logger.Info().Msgf("Generated template available at: https://cloud.projectdiscovery.io/templates/%s", templateID)
@@ -91,22 +91,22 @@ func generateAITemplate(prompt string) (string, string, error) {
}
jsonBody, err := json.Marshal(reqBody)
if err != nil {
return "", "", errkit.Newf("Failed to marshal request body: %v", err)
return "", "", errorutil.New("Failed to marshal request body: %v", err)
}
req, err := http.NewRequest(http.MethodPost, aiTemplateGeneratorAPIEndpoint, bytes.NewBuffer(jsonBody))
if err != nil {
return "", "", errkit.Newf("Failed to create HTTP request: %v", err)
return "", "", errorutil.New("Failed to create HTTP request: %v", err)
}
ph := pdcpauth.PDCPCredHandler{}
creds, err := ph.GetCreds()
if err != nil {
return "", "", errkit.Newf("Failed to get PDCP credentials: %v", err)
return "", "", errorutil.New("Failed to get PDCP credentials: %v", err)
}
if creds == nil {
return "", "", errkit.Newf("PDCP API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
return "", "", errorutil.New("PDCP API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
}
req.Header.Set("Content-Type", "application/json")
@@ -114,28 +114,28 @@ func generateAITemplate(prompt string) (string, string, error) {
resp, err := retryablehttp.DefaultClient().Do(req)
if err != nil {
return "", "", errkit.Newf("Failed to send HTTP request: %v", err)
return "", "", errorutil.New("Failed to send HTTP request: %v", err)
}
defer func() {
_ = resp.Body.Close()
}()
if resp.StatusCode == http.StatusUnauthorized {
return "", "", errkit.Newf("Invalid API Key or API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
return "", "", errorutil.New("Invalid API Key or API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
}
if resp.StatusCode != http.StatusOK {
body, _ := io.ReadAll(resp.Body)
return "", "", errkit.Newf("API returned status code %d: %s", resp.StatusCode, string(body))
return "", "", errorutil.New("API returned status code %d: %s", resp.StatusCode, string(body))
}
var result AITemplateResponse
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return "", "", errkit.Newf("Failed to decode API response: %v", err)
return "", "", errorutil.New("Failed to decode API response: %v", err)
}
if result.TemplateID == "" || result.Completion == "" {
return "", "", errkit.Newf("Failed to generate template")
return "", "", errorutil.New("Failed to generate template")
}
return result.Completion, result.TemplateID, nil
+78 -298
View File
@@ -7,14 +7,13 @@ import (
"os"
"sort"
"strings"
"sync"
"github.com/logrusorgru/aurora"
"github.com/pkg/errors"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/index"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader/filter"
"github.com/projectdiscovery/nuclei/v3/pkg/keys"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
@@ -25,8 +24,7 @@ import (
"github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
"github.com/projectdiscovery/retryablehttp-go"
"github.com/projectdiscovery/utils/errkit"
mapsutil "github.com/projectdiscovery/utils/maps"
errorutil "github.com/projectdiscovery/utils/errors"
sliceutil "github.com/projectdiscovery/utils/slice"
stringsutil "github.com/projectdiscovery/utils/strings"
syncutil "github.com/projectdiscovery/utils/sync"
@@ -77,6 +75,7 @@ type Config struct {
type Store struct {
id string // id of the store (optional)
tagFilter *templates.TagFilter
pathFilter *filter.PathFilter
config *Config
finalTemplates []string
finalWorkflows []string
@@ -88,19 +87,6 @@ type Store struct {
logger *gologger.Logger
// parserCacheOnce is used to cache the parser cache result
parserCacheOnce func() *templates.Cache
// metadataIndex is the template metadata cache
metadataIndex *index.Index
// indexFilter is the cached filter for metadata matching
indexFilter *index.Filter
// saveTemplatesIndexOnce is used to ensure we only save the metadata index
// once
saveMetadataIndexOnce func()
// NotFoundCallback is called for each not found template
// This overrides error handling for not found templates
NotFoundCallback func(template string) bool
@@ -138,10 +124,17 @@ func NewConfig(options *types.Options, catalog catalog.Catalog, executerOpts *pr
// New creates a new template store based on provided configuration
func New(cfg *Config) (*Store, error) {
// tagFilter only for IncludeConditions (advanced filtering).
// All other filtering (tags, authors, severities, IDs, protocols, paths) is
// handled by [index.Filter].
tagFilter, err := templates.NewTagFilter(&templates.TagFilterConfig{
Tags: cfg.Tags,
ExcludeTags: cfg.ExcludeTags,
Authors: cfg.Authors,
Severities: cfg.Severities,
ExcludeSeverities: cfg.ExcludeSeverities,
IncludeTags: cfg.IncludeTags,
IncludeIds: cfg.IncludeIds,
ExcludeIds: cfg.ExcludeIds,
Protocols: cfg.Protocols,
ExcludeProtocols: cfg.ExcludeProtocols,
IncludeConditions: cfg.IncludeConditions,
})
if err != nil {
@@ -149,43 +142,20 @@ func New(cfg *Config) (*Store, error) {
}
store := &Store{
id: cfg.StoreId,
config: cfg,
tagFilter: tagFilter,
id: cfg.StoreId,
config: cfg,
tagFilter: tagFilter,
pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{
IncludedTemplates: cfg.IncludeTemplates,
ExcludedTemplates: cfg.ExcludeTemplates,
}, cfg.Catalog),
finalTemplates: cfg.Templates,
finalWorkflows: cfg.Workflows,
logger: cfg.Logger,
}
store.parserCacheOnce = sync.OnceValue(func() *templates.Cache {
if cfg.ExecutorOptions == nil || cfg.ExecutorOptions.Parser == nil {
return nil
}
if parser, ok := cfg.ExecutorOptions.Parser.(*templates.Parser); ok {
return parser.Cache()
}
return nil
})
// Initialize metadata index and filter (load from disk & cache for reuse)
store.metadataIndex = store.loadTemplatesIndex()
store.indexFilter = store.buildIndexFilter()
store.saveMetadataIndexOnce = sync.OnceFunc(func() {
if store.metadataIndex == nil {
return
}
if err := store.metadataIndex.Save(); err != nil {
store.logger.Warning().Msgf("Could not save metadata cache: %v", err)
} else {
store.logger.Verbose().Msgf("Saved %d templates to metadata cache", store.metadataIndex.Size())
}
})
// Do a check to see if we have URLs in templates flag, if so
// we need to process them separately and remove them from the initial list
// we need to processs them separately and remove them from the initial list
var templatesFinal []string
for _, template := range cfg.Templates {
// TODO: Add and replace this with urlutil.IsURL() helper
@@ -205,7 +175,6 @@ func New(cfg *Config) (*Store, error) {
templatesFinal = append(templatesFinal, v) // something went wrong, treat it as a file
}
}
cfg.TemplateURLs = remoteTemplates
store.finalTemplates = templatesFinal
@@ -215,7 +184,6 @@ func New(cfg *Config) (*Store, error) {
if err != nil {
return store, err
}
store.finalTemplates = append(store.finalTemplates, remoteTemplates...)
store.finalWorkflows = append(store.finalWorkflows, remoteWorkflows...)
}
@@ -251,18 +219,14 @@ func handleTemplatesEditorURLs(input string) string {
if err != nil {
return input
}
if !strings.HasSuffix(parsed.Hostname(), "cloud.projectdiscovery.io") {
return input
}
if strings.HasSuffix(parsed.Path, ".yaml") {
return input
}
parsed.Path = fmt.Sprintf("%s.yaml", parsed.Path)
finalURL := parsed.String()
return finalURL
}
@@ -272,21 +236,17 @@ func handleTemplatesEditorURLs(input string) string {
func (store *Store) ReadTemplateFromURI(uri string, remote bool) ([]byte, error) {
if stringsutil.HasPrefixAny(uri, httpPrefix, httpsPrefix) && remote {
uri = handleTemplatesEditorURLs(uri)
remoteTemplates, _, err := getRemoteTemplatesAndWorkflows([]string{uri}, nil, store.config.RemoteTemplateDomainList)
if err != nil || len(remoteTemplates) == 0 {
return nil, errkit.Wrapf(err, "Could not load template %s: got %v", uri, remoteTemplates)
return nil, errorutil.NewWithErr(err).Msgf("Could not load template %s: got %v", uri, remoteTemplates)
}
resp, err := retryablehttp.Get(remoteTemplates[0])
if err != nil {
return nil, err
}
defer func() {
_ = resp.Body.Close()
}()
return io.ReadAll(resp.Body)
} else {
return os.ReadFile(uri)
@@ -325,106 +285,19 @@ func init() {
templateIDPathMap = make(map[string]string)
}
// buildIndexFilter creates an [index.Filter] from the store configuration.
// This filter handles all basic filtering (paths, tags, authors, severities,
// IDs, protocols). Advanced IncludeConditions filtering is handled separately
// by tagFilter.
func (store *Store) buildIndexFilter() *index.Filter {
includeTemplates, _ := store.config.Catalog.GetTemplatesPath(store.config.IncludeTemplates)
excludeTemplates, _ := store.config.Catalog.GetTemplatesPath(store.config.ExcludeTemplates)
return &index.Filter{
Authors: store.config.Authors,
Tags: store.config.Tags,
ExcludeTags: store.config.ExcludeTags,
IncludeTags: store.config.IncludeTags,
IDs: store.config.IncludeIds,
ExcludeIDs: store.config.ExcludeIds,
IncludeTemplates: includeTemplates,
ExcludeTemplates: excludeTemplates,
Severities: []severity.Severity(store.config.Severities),
ExcludeSeverities: []severity.Severity(store.config.ExcludeSeverities),
ProtocolTypes: []templateTypes.ProtocolType(store.config.Protocols),
ExcludeProtocolTypes: []templateTypes.ProtocolType(store.config.ExcludeProtocols),
}
}
func (store *Store) loadTemplatesIndex() *index.Index {
var metadataIdx *index.Index
idx, err := index.NewDefaultIndex()
if err != nil {
store.logger.Warning().Msgf("Could not create metadata cache: %v", err)
} else {
metadataIdx = idx
if err := metadataIdx.Load(); err != nil {
store.logger.Warning().Msgf("Could not load metadata cache: %v", err)
}
}
return metadataIdx
}
// LoadTemplatesOnlyMetadata loads only the metadata of the templates
func (store *Store) LoadTemplatesOnlyMetadata() error {
defer store.saveMetadataIndexOnce()
templatePaths, errs := store.config.Catalog.GetTemplatesPath(store.finalTemplates)
store.logErroredTemplates(errs)
indexFilter := store.indexFilter
filteredTemplatePaths := store.pathFilter.Match(templatePaths)
validPaths := make(map[string]struct{})
for _, templatePath := range templatePaths {
if store.metadataIndex != nil {
if metadata, found := store.metadataIndex.Get(templatePath); found {
if !indexFilter.Matches(metadata) {
continue
}
if store.tagFilter != nil {
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog)
if !loaded {
if err != nil && strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
stats.Increment(templates.TemplatesExcludedStats)
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
}
}
continue
}
}
validPaths[templatePath] = struct{}{}
continue
}
}
for templatePath := range filteredTemplatePaths {
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog)
if loaded {
templatesCache := store.parserCacheOnce()
if templatesCache != nil {
if template, _, _ := templatesCache.Has(templatePath); template != nil {
var metadata *index.Metadata
if store.metadataIndex != nil {
metadata, _ = store.metadataIndex.SetFromTemplate(templatePath, template)
} else {
metadata = index.NewMetadataFromTemplate(templatePath, template)
}
if !indexFilter.Matches(metadata) {
continue
}
validPaths[templatePath] = struct{}{}
continue
}
}
if loaded || store.pathFilter.MatchIncluded(templatePath) {
validPaths[templatePath] = struct{}{}
}
if err != nil {
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
stats.Increment(templates.TemplatesExcludedStats)
@@ -433,47 +306,43 @@ func (store *Store) LoadTemplatesOnlyMetadata() error {
}
continue
}
store.logger.Warning().Msg(err.Error())
}
}
templatesCache := store.parserCacheOnce()
if templatesCache == nil {
parserItem, ok := store.config.ExecutorOptions.Parser.(*templates.Parser)
if !ok {
return errors.New("invalid parser")
}
loadedTemplateIDs := mapsutil.NewSyncLockMap[string, struct{}]()
caps := templates.Capabilities{
Headless: store.config.ExecutorOptions.Options.Headless,
Code: store.config.ExecutorOptions.Options.EnableCodeTemplates,
DAST: store.config.ExecutorOptions.Options.DAST,
SelfContained: store.config.ExecutorOptions.Options.EnableSelfContainedTemplates,
File: store.config.ExecutorOptions.Options.EnableFileTemplates,
}
isListOrDisplay := store.config.ExecutorOptions.Options.TemplateList ||
store.config.ExecutorOptions.Options.TemplateDisplay
templatesCache := parserItem.Cache()
for templatePath := range validPaths {
template, _, _ := templatesCache.Has(templatePath)
if template == nil {
if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
continue
}
if !isListOrDisplay && !template.IsEnabledFor(caps) {
if len(template.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
continue
}
if loadedTemplateIDs.Has(template.ID) {
store.logger.Debug().Msgf("Skipping duplicate template ID '%s' from path '%s'", template.ID, templatePath)
if template.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
continue
}
_ = loadedTemplateIDs.Set(template.ID, struct{}{})
template.Path = templatePath
store.templates = append(store.templates, template)
if template.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
continue
}
if template.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
continue
}
if template != nil {
template.Path = templatePath
store.templates = append(store.templates, template)
}
}
return nil
}
@@ -482,24 +351,15 @@ func (store *Store) LoadTemplatesOnlyMetadata() error {
func (store *Store) ValidateTemplates() error {
templatePaths, errs := store.config.Catalog.GetTemplatesPath(store.finalTemplates)
store.logErroredTemplates(errs)
workflowPaths, errs := store.config.Catalog.GetTemplatesPath(store.finalWorkflows)
store.logErroredTemplates(errs)
templatePathsMap := make(map[string]struct{}, len(templatePaths))
for _, path := range templatePaths {
templatePathsMap[path] = struct{}{}
}
filteredTemplatePaths := store.pathFilter.Match(templatePaths)
filteredWorkflowPaths := store.pathFilter.Match(workflowPaths)
workflowPathsMap := make(map[string]struct{}, len(workflowPaths))
for _, path := range workflowPaths {
workflowPathsMap[path] = struct{}{}
}
if store.areTemplatesValid(templatePathsMap) && store.areWorkflowsValid(workflowPathsMap) {
if store.areTemplatesValid(filteredTemplatePaths) && store.areWorkflowsValid(filteredWorkflowPaths) {
return nil
}
return errors.New("errors occurred during template validation")
}
@@ -517,7 +377,6 @@ func (store *Store) areTemplatesValid(filteredTemplatePaths map[string]struct{})
func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string]struct{}, isWorkflow bool, load func(templatePath string, tagFilter *templates.TagFilter) (bool, error)) bool {
areTemplatesValid := true
parsedCache := store.parserCacheOnce()
for templatePath := range filteredTemplatePaths {
if _, err := load(templatePath, store.tagFilter); err != nil {
@@ -527,26 +386,13 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
}
}
var template *templates.Template
var err error
if parsedCache != nil {
if cachedTemplate, _, cacheErr := parsedCache.Has(templatePath); cacheErr == nil && cachedTemplate != nil {
template = cachedTemplate
template, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
if err != nil {
if isParsingError(store, "Error occurred parsing template %s: %s\n", templatePath, err) {
areTemplatesValid = false
continue
}
}
if template == nil {
template, err = templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
if err != nil {
if isParsingError(store, "Error occurred parsing template %s: %s\n", templatePath, err) {
areTemplatesValid = false
continue
}
}
}
if template == nil {
} else if template == nil {
// NOTE(dwisiswant0): possibly global matchers template.
// This could definitely be handled better, for example by returning an
// `ErrGlobalMatchersTemplate` during `templates.Parse` and checking it
@@ -569,12 +415,10 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
// areTemplatesValid = false
store.logger.Warning().Msgf("Found duplicate template ID during validation '%s' => '%s': %s\n", templatePath, existingTemplatePath, template.ID)
}
if !isWorkflow && template.HasWorkflows() {
if !isWorkflow && len(template.Workflows) > 0 {
continue
}
}
if isWorkflow {
if !areWorkflowTemplatesValid(store, template.Workflows) {
areTemplatesValid = false
@@ -582,7 +426,6 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
}
}
}
return areTemplatesValid
}
@@ -591,7 +434,6 @@ func areWorkflowTemplatesValid(store *Store, workflows []*workflows.WorkflowTemp
if !areWorkflowTemplatesValid(store, workflow.Subtemplates) {
return false
}
_, err := store.config.Catalog.GetTemplatePath(workflow.Template)
if err != nil {
if isParsingError(store, "Error occurred loading template %s: %s\n", workflow.Template, err) {
@@ -599,7 +441,6 @@ func areWorkflowTemplatesValid(store *Store, workflows []*workflows.WorkflowTemp
}
}
}
return true
}
@@ -607,13 +448,10 @@ func isParsingError(store *Store, message string, template string, err error) bo
if errors.Is(err, templates.ErrExcluded) {
return false
}
if errors.Is(err, templates.ErrCreateTemplateExecutor) {
return false
}
store.logger.Error().Msgf(message, template, err)
return true
}
@@ -626,14 +464,14 @@ func (store *Store) LoadTemplates(templatesList []string) []*templates.Template
func (store *Store) LoadWorkflows(workflowsList []string) []*templates.Template {
includedWorkflows, errs := store.config.Catalog.GetTemplatesPath(workflowsList)
store.logErroredTemplates(errs)
workflowPathMap := store.pathFilter.Match(includedWorkflows)
loadedWorkflows := make([]*templates.Template, 0, len(includedWorkflows))
for _, workflowPath := range includedWorkflows {
loadedWorkflows := make([]*templates.Template, 0, len(workflowPathMap))
for workflowPath := range workflowPathMap {
loaded, err := store.config.ExecutorOptions.Parser.LoadWorkflow(workflowPath, store.config.Catalog)
if err != nil {
store.logger.Warning().Msgf("Could not load workflow %s: %s\n", workflowPath, err)
}
if loaded {
parsed, err := templates.Parse(workflowPath, store.preprocessor, store.config.ExecutorOptions)
if err != nil {
@@ -643,31 +481,19 @@ func (store *Store) LoadWorkflows(workflowsList []string) []*templates.Template
}
}
}
return loadedWorkflows
}
// LoadTemplatesWithTags takes a list of templates and extra tags
// returning templates that match.
func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templates.Template {
defer store.saveMetadataIndexOnce()
indexFilter := store.indexFilter
includedTemplates, errs := store.config.Catalog.GetTemplatesPath(templatesList)
store.logErroredTemplates(errs)
templatePathMap := store.pathFilter.Match(includedTemplates)
loadedTemplates := sliceutil.NewSyncSlice[*templates.Template]()
loadedTemplateIDs := mapsutil.NewSyncLockMap[string, struct{}]()
loadTemplate := func(tmpl *templates.Template) {
if loadedTemplateIDs.Has(tmpl.ID) {
store.logger.Debug().Msgf("Skipping duplicate template ID '%s' from path '%s'", tmpl.ID, tmpl.Path)
return
}
_ = loadedTemplateIDs.Set(tmpl.ID, struct{}{})
loadedTemplates.Append(tmpl)
// increment signed/unsigned counters
if tmpl.Verified {
@@ -681,66 +507,28 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
}
}
typesOpts := store.config.ExecutorOptions.Options
concurrency := typesOpts.TemplateLoadingConcurrency
if concurrency <= 0 {
concurrency = types.DefaultTemplateLoadingConcurrency
}
wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(concurrency))
wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(50))
if errWg != nil {
panic("could not create wait group")
}
if typesOpts.ExecutionId == "" {
typesOpts.ExecutionId = xid.New().String()
if store.config.ExecutorOptions.Options.ExecutionId == "" {
store.config.ExecutorOptions.Options.ExecutionId = xid.New().String()
}
dialers := protocolstate.GetDialersWithId(typesOpts.ExecutionId)
dialers := protocolstate.GetDialersWithId(store.config.ExecutorOptions.Options.ExecutionId)
if dialers == nil {
panic("dialers with executionId " + typesOpts.ExecutionId + " not found")
panic("dialers with executionId " + store.config.ExecutorOptions.Options.ExecutionId + " not found")
}
for _, templatePath := range includedTemplates {
for templatePath := range templatePathMap {
wgLoadTemplates.Add()
go func(templatePath string) {
defer wgLoadTemplates.Done()
var (
metadata *index.Metadata
metadataCached bool
)
if store.metadataIndex != nil {
if cachedMetadata, found := store.metadataIndex.Get(templatePath); found {
metadata = cachedMetadata
if !indexFilter.Matches(metadata) {
return
}
// NOTE(dwisiswant0): else, tagFilter probably exists (for
// IncludeConditions), which still need to check via
// LoadTemplate.
metadataCached = true
}
}
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, tags, store.config.Catalog)
if loaded {
if loaded || store.pathFilter.MatchIncluded(templatePath) {
parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
if parsed != nil && !metadataCached {
if store.metadataIndex != nil {
metadata, _ = store.metadataIndex.SetFromTemplate(templatePath, parsed)
} else {
metadata = index.NewMetadataFromTemplate(templatePath, parsed)
}
if metadata != nil && !indexFilter.Matches(metadata) {
return
}
}
if err != nil {
// exclude templates not compatible with offline matching from total runtime warning stats
if !errors.Is(err, templates.ErrIncompatibleWithOfflineMatching) {
@@ -748,18 +536,18 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
}
store.logger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)
} else if parsed != nil {
if !parsed.Verified && typesOpts.DisableUnsignedTemplates {
if !parsed.Verified && store.config.ExecutorOptions.Options.DisableUnsignedTemplates {
// skip unverified templates when prompted to
stats.Increment(templates.SkippedUnsignedStats)
return
}
if parsed.SelfContained && !typesOpts.EnableSelfContainedTemplates {
if parsed.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
stats.Increment(templates.ExcludedSelfContainedStats)
return
}
if parsed.HasFileRequest() && !typesOpts.EnableFileTemplates {
if parsed.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
stats.Increment(templates.ExcludedFileStats)
return
}
@@ -771,32 +559,25 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
}
// DAST only templates
// Skip DAST filter when loading auth templates
if store.ID() != AuthStoreId && typesOpts.DAST {
if store.ID() != AuthStoreId && store.config.ExecutorOptions.Options.DAST {
// check if the template is a DAST template
// also allow global matchers template to be loaded
if parsed.IsFuzzableRequest() || parsed.IsGlobalMatchersTemplate() {
if parsed.HasHeadlessRequest() && !typesOpts.Headless {
stats.Increment(templates.ExcludedHeadlessTmplStats)
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
}
} else {
loadTemplate(parsed)
}
if parsed.IsFuzzing() || parsed.Options.GlobalMatchers != nil && parsed.Options.GlobalMatchers.HasMatchers() {
loadTemplate(parsed)
}
} else if parsed.HasHeadlessRequest() && !typesOpts.Headless {
} else if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
// donot include headless template in final list if headless flag is not set
stats.Increment(templates.ExcludedHeadlessTmplStats)
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
}
} else if parsed.HasCodeRequest() && !typesOpts.EnableCodeTemplates {
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
// donot include 'Code' protocol custom template in final list if code flag is not set
stats.Increment(templates.ExcludedCodeTmplStats)
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
}
} else if parsed.HasCodeRequest() && !parsed.Verified && !parsed.HasWorkflows() {
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
// donot include unverified 'Code' protocol custom template in final list
stats.Increment(templates.SkippedCodeTmplTamperedStats)
// these will be skipped so increment skip counter
@@ -804,7 +585,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
}
} else if parsed.IsFuzzableRequest() && !typesOpts.DAST {
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
stats.Increment(templates.ExludedDastTmplStats)
if config.DefaultConfig.LogAllEvents {
store.logger.Print().Msgf("[%v] -dast flag is required for DAST template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
@@ -842,11 +623,10 @@ func IsHTTPBasedProtocolUsed(store *Store) bool {
templates := append(store.Templates(), store.Workflows()...)
for _, template := range templates {
if template.HasHTTPRequest() || template.HasHeadlessRequest() {
if len(template.RequestsHTTP) > 0 || len(template.RequestsHeadless) > 0 {
return true
}
if template.HasWorkflows() {
if len(template.Workflows) > 0 {
if workflowContainsProtocol(template.Workflows) {
return true
}
-243
View File
@@ -1,243 +0,0 @@
package loader_test
import (
"testing"
"github.com/projectdiscovery/gologger"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/loader"
"github.com/projectdiscovery/nuclei/v3/pkg/loader/workflow"
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
)
func BenchmarkStoreValidateTemplates(b *testing.B) {
options := testutils.DefaultOptions.Copy()
options.Logger = &gologger.Logger{}
testutils.Init(options)
catalog := disk.NewCatalog(config.DefaultConfig.TemplatesDirectory)
executerOpts := testutils.NewMockExecuterOptions(options, nil)
executerOpts.Parser = templates.NewParser()
workflowLoader, err := workflow.NewLoader(executerOpts)
if err != nil {
b.Fatalf("could not create workflow loader: %s", err)
}
executerOpts.WorkflowLoader = workflowLoader
loaderCfg := loader.NewConfig(options, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.ValidateTemplates()
}
}
func BenchmarkLoadTemplates(b *testing.B) {
options := testutils.DefaultOptions.Copy()
options.Logger = &gologger.Logger{}
options.ExecutionId = "bench-load-templates"
testutils.Init(options)
catalog := disk.NewCatalog(config.DefaultConfig.TemplatesDirectory)
executerOpts := testutils.NewMockExecuterOptions(options, nil)
executerOpts.Parser = templates.NewParser()
workflowLoader, err := workflow.NewLoader(executerOpts)
if err != nil {
b.Fatalf("could not create workflow loader: %s", err)
}
executerOpts.WorkflowLoader = workflowLoader
b.Run("NoFilter", func(b *testing.B) {
loaderCfg := loader.NewConfig(options, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("FilterBySeverityCritical", func(b *testing.B) {
opts := options.Copy()
opts.Severities = severity.Severities{severity.Critical}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("FilterBySeverityHighCritical", func(b *testing.B) {
opts := options.Copy()
opts.Severities = severity.Severities{severity.High, severity.Critical}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("FilterByAuthor", func(b *testing.B) {
opts := options.Copy()
opts.Authors = []string{"pdteam"}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("FilterByTags", func(b *testing.B) {
opts := options.Copy()
opts.Tags = []string{"cve", "rce"}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("FilterByProtocol", func(b *testing.B) {
opts := options.Copy()
opts.Protocols = templateTypes.ProtocolTypes{templateTypes.HTTPProtocol}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
b.Run("ComplexFilter", func(b *testing.B) {
opts := options.Copy()
opts.Severities = severity.Severities{severity.High, severity.Critical}
opts.Authors = []string{"pdteam"}
opts.Tags = []string{"cve"}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplates([]string{config.DefaultConfig.TemplatesDirectory})
}
})
}
func BenchmarkLoadTemplatesOnlyMetadata(b *testing.B) {
options := testutils.DefaultOptions.Copy()
options.Logger = &gologger.Logger{}
options.ExecutionId = "bench-metadata"
testutils.Init(options)
catalog := disk.NewCatalog(config.DefaultConfig.TemplatesDirectory)
executerOpts := testutils.NewMockExecuterOptions(options, nil)
executerOpts.Parser = templates.NewParser()
workflowLoader, err := workflow.NewLoader(executerOpts)
if err != nil {
b.Fatalf("could not create workflow loader: %s", err)
}
executerOpts.WorkflowLoader = workflowLoader
b.Run("WithoutFilter", func(b *testing.B) {
loaderCfg := loader.NewConfig(options, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
// Pre-warm the cache
_ = store.LoadTemplatesOnlyMetadata()
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplatesOnlyMetadata()
}
})
b.Run("WithSeverityFilter", func(b *testing.B) {
opts := options.Copy()
opts.Severities = severity.Severities{severity.Critical}
loaderCfg := loader.NewConfig(opts, catalog, executerOpts)
store, err := loader.New(loaderCfg)
if err != nil {
b.Fatalf("could not create store: %s", err)
}
// Pre-warm the cache
_ = store.LoadTemplatesOnlyMetadata()
b.ResetTimer()
b.ReportAllocs()
for b.Loop() {
_ = store.LoadTemplatesOnlyMetadata()
}
})
}
+1 -1
View File
@@ -97,7 +97,7 @@ func getRemoteContent(URL string, remoteTemplateDomainList []string, contentType
_ = response.Body.Close()
}()
if response.StatusCode < 200 || response.StatusCode > 299 {
return RemoteContent{Error: fmt.Errorf("get \"%s\": unexpected status %d", URL, response.StatusCode)}
return RemoteContent{Error: fmt.Errorf("get \"%s\": unexpect status %d", URL, response.StatusCode)}
}
scanner := bufio.NewScanner(response.Body)
Loaded 100 of 298 files, more files were not shown because too many files have changed in this diff. Show more