mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 10:10:18 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b3706070ab | ||
|
|
755c9785b1 | ||
|
|
c73906b405 | ||
|
|
8891d70d42 | ||
|
|
ebfd2f2992 | ||
|
|
e74355344d | ||
|
|
b49beef554 | ||
|
|
cf3b5bf449 | ||
|
|
b63a23bd5c | ||
|
|
7fb1fe7bf2 | ||
|
|
3e93996471 | ||
|
|
8e535f625d | ||
|
|
93fb595fc1 | ||
|
|
df74ff3edf | ||
|
|
fe090d7c10 | ||
|
|
4cbc1bd75c | ||
|
|
09c47c32bd | ||
|
|
fb4fe015f0 | ||
|
|
924e2a7a25 | ||
|
|
3d60c9fdbe | ||
|
|
a6cddf86ec | ||
|
|
8dfd666223 | ||
|
|
56b6d42723 | ||
|
|
2c46f2c2ef | ||
|
|
0e498bcd76 | ||
|
|
f6af5216ba | ||
|
|
d9452011fc | ||
|
|
2b9c985818 | ||
|
|
2449d5628f | ||
|
|
e865ca496d | ||
|
|
6384399375 | ||
|
|
c994bba249 | ||
|
|
9eede47dbd | ||
|
|
7f56dc27c1 | ||
|
|
3edd5d956f | ||
|
|
3447f09c9f | ||
|
|
9ec2e995d0 | ||
|
|
f181a691b2 | ||
|
|
9102f333a9 | ||
|
|
3dab87bf77 | ||
|
|
7e151de8a3 | ||
|
|
1a8f4d40d6 | ||
|
|
7ea72b5268 | ||
|
|
29dfd671fc | ||
|
|
2978819d0e | ||
|
|
0aba8e5a65 | ||
|
|
78ccfc2912 | ||
|
|
c1878b6134 | ||
|
|
29977358d7 | ||
|
|
f63cad9651 | ||
|
|
528caa8831 | ||
|
|
c32cff8521 | ||
|
|
cf44afbb99 | ||
|
|
d5d8d50ab7 | ||
|
|
a754e7d223 | ||
|
|
a52e6753a2 | ||
|
|
ad1e6f8d75 | ||
|
|
aad0e51fef | ||
|
|
50b43f2015 | ||
|
|
146fb5fb34 | ||
|
|
3ea8dc29c3 | ||
|
|
4e8843a7cd | ||
|
|
761c7c0c29 | ||
|
|
232de93297 | ||
|
|
c746a8fdae | ||
|
|
9b37f1b441 | ||
|
|
8b8a3a11b6 | ||
|
|
e70044af4c | ||
|
|
d20240bf0c | ||
|
|
6730f5262f | ||
|
|
a42dd7a6d9 | ||
|
|
7877341a13 | ||
|
|
141f34a8ae | ||
|
|
8016d15fdc | ||
|
|
488d5886ea | ||
|
|
cf5557e0fd | ||
|
|
1a2363db8a | ||
|
|
3a4e436eed | ||
|
|
0f5d0d7976 | ||
|
|
954c825fab | ||
|
|
3eff4146e1 | ||
|
|
87d62d3a8f | ||
|
|
efcc8e95c3 | ||
|
|
96bfb2bac1 | ||
|
|
cc0f1e9606 | ||
|
|
0054e8b7ae | ||
|
|
aecef0ab75 | ||
|
|
0dde27f41d | ||
|
|
13cca2b2c8 | ||
|
|
7b0c6fb782 | ||
|
|
2829fd30bb | ||
|
|
53af79a392 | ||
|
|
90d51f733a | ||
|
|
d7da0e0914 | ||
|
|
5147c72a45 | ||
|
|
7e33712d08 | ||
|
|
3a44eaf7b5 | ||
|
|
fb93cbeeda | ||
|
|
69c831bad2 | ||
|
|
f544ea706c | ||
|
|
1093bbc62d | ||
|
|
133e8b98c0 | ||
|
|
ed1c5752f3 | ||
|
|
e60f4158ea | ||
|
|
984deac200 | ||
|
|
17360cb6e4 | ||
|
|
770f20eeab | ||
|
|
5b5d87f62a | ||
|
|
6c05580120 | ||
|
|
16154d1442 | ||
|
|
7fc4752a95 | ||
|
|
248bac75a0 | ||
|
|
6794b9cba0 | ||
|
|
8136d4f368 | ||
|
|
1a8124679e | ||
|
|
f3181b9a2a | ||
|
|
3c0b84b137 | ||
|
|
b625a659db | ||
|
|
dd8946d3f2 | ||
|
|
c814128ee2 | ||
|
|
6f59472f78 | ||
|
|
3be27b9dfd | ||
|
|
e535e0126f | ||
|
|
9d59fd0188 | ||
|
|
c4a63ec1de | ||
|
|
8c30b5d53c | ||
|
|
82144e542b | ||
|
|
3b5e3a4de9 | ||
|
|
b4551b901f | ||
|
|
ca1cbf2135 | ||
|
|
3384606dea | ||
|
|
d2ae3521cc | ||
|
|
f3298323f3 | ||
|
|
71293f2f81 | ||
|
|
a5b424bbfe | ||
|
|
966005ecb0 | ||
|
|
5cd344e7cd | ||
|
|
ae52ef04c9 | ||
|
|
b9ce0c2226 | ||
|
|
da316df556 | ||
|
|
ccb65a0016 | ||
|
|
5b92381494 | ||
|
|
e168f8dbfa | ||
|
|
f57bd8c8ee | ||
|
|
5557f4eae3 | ||
|
|
f6efa9da6c | ||
|
|
2db03a9335 | ||
|
|
5b7b83618c | ||
|
|
89cfb75bb6 | ||
|
|
f0429aa4b7 | ||
|
|
1684f4143e | ||
|
|
7823d46fdb | ||
|
|
c3750be380 | ||
|
|
361f877730 | ||
|
|
427007cf5d | ||
|
|
cd41cb9f4d | ||
|
|
295fb91123 | ||
|
|
19554a793d | ||
|
|
0967d42b40 | ||
|
|
e6c1a80498 | ||
|
|
a2f10442e7 | ||
|
|
231d838734 | ||
|
|
81a1159587 | ||
|
|
171db55891 | ||
|
|
59c6c855bd | ||
|
|
78c2712bd3 | ||
|
|
dcdc7ebc32 | ||
|
|
73e70ea49d | ||
|
|
0974d7fd04 | ||
|
|
635d28e2cb | ||
|
|
a2d686e75c | ||
|
|
3fd6e63e52 | ||
|
|
cc1d646347 | ||
|
|
bfef42f9e3 | ||
|
|
4e2af6bf67 | ||
|
|
2a80cd594d | ||
|
|
603a86b7e6 | ||
|
|
e0d3bb45ed | ||
|
|
8c560523e3 | ||
|
|
07590268c1 | ||
|
|
ae612572af | ||
|
|
67571b3cfe | ||
|
|
75016d1e96 | ||
|
|
6208dbe06a | ||
|
|
3597ab07f0 | ||
|
|
0f4f4b94e6 | ||
|
|
c903da3a0c | ||
|
|
86be8429ed | ||
|
|
7e04181391 | ||
|
|
6a6de384fc | ||
|
|
b529125031 | ||
|
|
3ef581c5e8 | ||
|
|
ca11a2fad6 | ||
|
|
7d450507f7 | ||
|
|
95a72cfd50 | ||
|
|
f8f89bb721 | ||
|
|
cb2d93174a | ||
|
|
61bd0828dc | ||
|
|
d44f07f648 | ||
|
|
93be3b8291 | ||
|
|
202524283b | ||
|
|
8ea5061f5e | ||
|
|
d2cf69aebb | ||
|
|
39e9286371 | ||
|
|
0ea42e5f66 | ||
|
|
f610ed4cab | ||
|
|
792998d8e2 | ||
|
|
c4fa2c74c1 | ||
|
|
d4f1a815ed | ||
|
|
a65841c034 | ||
|
|
876974f38b | ||
|
|
ca543d7885 | ||
|
|
fde6f72934 | ||
|
|
3af37362e3 | ||
|
|
99a9ce398d | ||
|
|
48af0b4f6c | ||
|
|
e1dfa1baa7 | ||
|
|
089e2a4ee0 | ||
|
|
46555bcd1e | ||
|
|
521a21c06a | ||
|
|
1acd40f97f | ||
|
|
09c2ca540a | ||
|
|
c863143771 | ||
|
|
5c8da8d88b | ||
|
|
4b22a3d53e | ||
|
|
94c77c1a28 | ||
|
|
ee1c847626 | ||
|
|
a21bfc4303 | ||
|
|
c487e59602 | ||
|
|
1f8dc4c358 | ||
|
|
608159bbbe | ||
|
|
b05359bc82 | ||
|
|
4916cf34f0 | ||
|
|
f460bf926d | ||
|
|
218a2f69a5 | ||
|
|
ff5734ba15 | ||
|
|
9c64a1cb9b | ||
|
|
32dfeacd9d | ||
|
|
48c389b063 | ||
|
|
36b4f68eec | ||
|
|
af7b2f166e | ||
|
|
b25937b310 | ||
|
|
100d6528f5 | ||
|
|
d76187f99a | ||
|
|
8194fabcf8 | ||
|
|
5063af46b1 | ||
|
|
776cb4fcf2 | ||
|
|
1f0aef970c | ||
|
|
5b7debf349 | ||
|
|
e83382d4e4 | ||
|
|
b61321cd19 | ||
|
|
f20f95f67e | ||
|
|
efcef55681 | ||
|
|
0f7b33cebf | ||
|
|
a1b5a0ed99 | ||
|
|
19247ae74b | ||
|
|
5be258f948 | ||
|
|
309018fbf4 | ||
|
|
5e9ada23b2 | ||
|
|
00f4595f0b | ||
|
|
30e520754b | ||
|
|
6b358b39a3 | ||
|
|
b41f4d97d6 | ||
|
|
5c15c77777 | ||
|
|
b4644af80a | ||
|
|
ce57bdf4ab | ||
|
|
d55ab2f827 | ||
|
|
cf8d067fea | ||
|
|
4baf46f080 | ||
|
|
8304462420 | ||
|
|
1f538bcac6 | ||
|
|
937fa1252b | ||
|
|
99914e1a32 | ||
|
|
1a9a7563c0 | ||
|
|
248548e075 | ||
|
|
3eb3f66897 | ||
|
|
5f501da063 | ||
|
|
a0bd3b854e | ||
|
|
32845bccf2 |
No files matched your search
@@ -0,0 +1,76 @@
|
||||
# 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
|
||||
@@ -2,14 +2,18 @@ blank_issues_enabled: false
|
||||
|
||||
contact_links:
|
||||
|
||||
- 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: 💡 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: 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: ❓ 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: Connect with PD Team (Discord)
|
||||
- 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)
|
||||
url: https://discord.gg/projectdiscovery
|
||||
about: Connect with PD Team for direct communication
|
||||
about: Join our Discord for real-time discussions and community support on the #nuclei channel.
|
||||
@@ -0,0 +1,45 @@
|
||||
# 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
|
||||
File renamed without changes.
@@ -2,6 +2,7 @@ addReviewers: true
|
||||
reviewers:
|
||||
- dogancanbakir
|
||||
- dwisiswant0
|
||||
- mzack9999
|
||||
|
||||
numberOfReviewers: 1
|
||||
skipKeywords:
|
||||
|
||||
@@ -18,7 +18,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.DEPENDABOT_PAT }}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go/compat-checks@v1
|
||||
with:
|
||||
release-test: true
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- run: make syntax-docs
|
||||
|
||||
@@ -28,9 +28,10 @@ jobs:
|
||||
LIST_FILE: "/tmp/targets-${{ matrix.targets }}.txt"
|
||||
PROFILE_MEM: "/tmp/nuclei-profile-${{ matrix.targets }}-targets"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/git@v1
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/cache/nuclei@v1
|
||||
- name: Generate list
|
||||
run: for i in {1..${{ matrix.targets }}}; do echo "https://honey.scanme.sh/?_=${i}" >> "${LIST_FILE}"; done
|
||||
# NOTE(dwisiswant0): use `-no-mhe` flag to get better samples.
|
||||
@@ -49,7 +50,7 @@ jobs:
|
||||
files: "${PGO_FILE}"
|
||||
message: "build: update PGO profile :robot:"
|
||||
- run: git push origin $GITHUB_REF
|
||||
- uses: actions/upload-artifact@v4
|
||||
- uses: actions/upload-artifact@v6
|
||||
with:
|
||||
name: "pgo"
|
||||
path: "${{ env.PGO_FILE }}"
|
||||
@@ -16,11 +16,11 @@ jobs:
|
||||
env:
|
||||
OUTPUT: "/tmp/results.sarif"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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@v3
|
||||
- uses: github/codeql-action/upload-sarif@v4
|
||||
with:
|
||||
sarif_file: "${{ env.OUTPUT }}"
|
||||
category: "govulncheck"
|
||||
@@ -11,13 +11,15 @@ jobs:
|
||||
env:
|
||||
BENCH_OUT: "/tmp/bench.out"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- 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-test
|
||||
- run: ./bin/nuclei.test -test.run - -test.bench=. -test.benchmem ./cmd/nuclei/ | tee $BENCH_OUT
|
||||
env:
|
||||
DISABLE_STDOUT: "1"
|
||||
- uses: actions/cache/restore@v4
|
||||
- uses: actions/cache/restore@v5
|
||||
with:
|
||||
path: ./cache
|
||||
key: ${{ runner.os }}-benchmark
|
||||
@@ -31,7 +33,7 @@ jobs:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
comment-on-alert: true
|
||||
summary-always: true
|
||||
- uses: actions/cache/save@v4
|
||||
- uses: actions/cache/save@v5
|
||||
if: github.event_name == 'push'
|
||||
with:
|
||||
path: ./cache
|
||||
|
||||
@@ -16,7 +16,7 @@ jobs:
|
||||
LIST_FILE: "/tmp/targets-${{ matrix.count }}.txt"
|
||||
PROFILE_MEM: "/tmp/nuclei-perf-test-${{ matrix.count }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- run: make verify
|
||||
- name: Generate list
|
||||
|
||||
@@ -10,7 +10,7 @@ jobs:
|
||||
release:
|
||||
runs-on: ubuntu-latest-16-cores
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: actions/stale@v9
|
||||
- uses: actions/stale@v10
|
||||
with:
|
||||
days-before-stale: 90
|
||||
days-before-close: 7
|
||||
@@ -37,5 +37,4 @@ jobs:
|
||||
it, please comment or feel free to reopen it.
|
||||
close-issue-label: "Status: Abandoned"
|
||||
close-pr-label: "Status: Abandoned"
|
||||
exempt-issue-labels: "Status: Abandoned"
|
||||
exempt-pr-labels: "Status: Abandoned"
|
||||
exempt-issue-labels: "Type: Enhancement"
|
||||
@@ -22,8 +22,9 @@ jobs:
|
||||
if: "${{ !endsWith(github.actor, '[bot]') }}"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
|
||||
- uses: projectdiscovery/actions/golangci-lint/v2@v1
|
||||
|
||||
tests:
|
||||
@@ -35,8 +36,19 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: "${{ matrix.os }}"
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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
|
||||
@@ -52,8 +64,10 @@ jobs:
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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/
|
||||
@@ -74,9 +88,11 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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 }}"
|
||||
@@ -93,9 +109,11 @@ jobs:
|
||||
os: [ubuntu-latest, windows-latest, macOS-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- 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 }}"
|
||||
@@ -106,8 +124,9 @@ jobs:
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/cache/go-rod-browser@v1
|
||||
- run: make template-validate
|
||||
|
||||
codeql:
|
||||
@@ -119,19 +138,19 @@ jobs:
|
||||
contents: read
|
||||
security-events: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: github/codeql-action/init@v3
|
||||
- uses: actions/checkout@v6
|
||||
- uses: github/codeql-action/init@v4
|
||||
with:
|
||||
languages: 'go'
|
||||
- uses: github/codeql-action/autobuild@v3
|
||||
- uses: github/codeql-action/analyze@v3
|
||||
- uses: github/codeql-action/autobuild@v4
|
||||
- uses: github/codeql-action/analyze@v4
|
||||
|
||||
release:
|
||||
name: "Release test"
|
||||
needs: ["tests"]
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- uses: projectdiscovery/actions/setup/go@v1
|
||||
- uses: projectdiscovery/actions/goreleaser@v1
|
||||
|
||||
@@ -143,7 +162,7 @@ jobs:
|
||||
TARGET_URL: "http://scanme.sh/a/?b=c"
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/checkout@v6
|
||||
- run: make build
|
||||
- name: "Setup environment (push)"
|
||||
if: ${{ github.event_name == 'push' }}
|
||||
|
||||
+3
-1
@@ -8,7 +8,7 @@
|
||||
.gitignore
|
||||
.idea
|
||||
.vscode
|
||||
|
||||
.claude/settings.local.json
|
||||
# Binaries
|
||||
/bin/*
|
||||
/bindgen
|
||||
@@ -28,6 +28,8 @@
|
||||
/scrapefunc
|
||||
/scrapefuncs
|
||||
/tsgen
|
||||
/integration_tests/integration-test
|
||||
/integration_tests/nuclei
|
||||
|
||||
# Templates
|
||||
/*.yaml
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
# 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
|
||||
@@ -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,6 +40,8 @@ Nuclei was built with some environment variables in mind to help with debugging.
|
||||
| `SHOW_DSL_ERRORS=true` | Enables Printing DSL Errors (that are hidden by default) |
|
||||
| `HIDE_TEMPLATE_SIG_WARNING=true` | Hides Template Signature Verification Warnings |
|
||||
| `NUCLEI_LOG_ALL=true` | Log All Events that were skipped in verbose mode |
|
||||
| `NUCLEI_CONFIG_DIR` | Sets custom configuration directory path |
|
||||
| `NUCLEI_TEMPLATES_DIR` | Sets custom nuclei templates directory path |
|
||||
|
||||
|
||||
|
||||
@@ -145,7 +145,7 @@ type Exporter interface {
|
||||
}
|
||||
```
|
||||
|
||||
Exporters include `Elasticsearch`, `markdown`, `sarif` . Trackers include `GitHub` , `GitLab` and `Jira`.
|
||||
Exporters include `Elasticsearch`, `markdown`, `sarif` . Trackers include `GitHub`, `GitLab` and `Jira`.
|
||||
|
||||
Each exporter and trackers implement their own configuration in YAML format and are very modular in nature, so adding new ones is easy.
|
||||
|
||||
|
||||
@@ -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 syntax-docs
|
||||
.PHONY: integration jsupdate-all jsupdate-bindgen jsupdate-tsgen memogen scan-charts test
|
||||
.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: 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
|
||||
test: GOFLAGS = -race -v -timeout 30m -count 1
|
||||
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
|
||||
@@ -14,8 +14,6 @@
|
||||
|
||||
<p align="center">
|
||||
|
||||
<img src="https://img.shields.io/badge/go-1.22-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white">
|
||||
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/overview?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<a href="https://github.com/projectdiscovery/nuclei-templates"><img src="https://img.shields.io/badge/Templates Library-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNoaWVsZCI+PHBhdGggZD0iTTIwIDEzYzAgNS0zLjUgNy41LTcuNjYgOC45NWExIDEgMCAwIDEtLjY3LS4wMUM3LjUgMjAuNSA0IDE4IDQgMTNWNmExIDEgMCAwIDEgMS0xYzIgMCA0LjUtMS4yIDYuMjQtMi43MmExLjE3IDEuMTcgMCAwIDEgMS41MiAwQzE0LjUxIDMuODEgMTcgNSAxOSA1YTEgMSAwIDAgMSAxIDF6Ii8+PC9zdmc+&logoColor=white"></a>
|
||||
@@ -35,7 +33,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>
|
||||
@@ -85,7 +83,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
|
||||
@@ -99,7 +97,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">
|
||||
|
||||
@@ -111,7 +109,7 @@ Browse the full Nuclei [**`documentation here`**](https://docs.projectdiscovery.
|
||||
|
||||
### Installation
|
||||
|
||||
`nuclei` requires **go1.23** to install successfully. Run the following command to get the repo:
|
||||
`nuclei` requires **go >= 1.24.2** to install successfully. Run the following command to get the repo:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -142,7 +140,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 using resume.cfg (clustering will be disabled)
|
||||
-resume string resume scan from and save to specified file (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)
|
||||
|
||||
@@ -163,7 +161,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 available templates
|
||||
-tl list all templates matching current filters
|
||||
-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
|
||||
@@ -286,6 +284,7 @@ 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)
|
||||
@@ -356,6 +355,7 @@ 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:
|
||||
|
||||
+4
-3
@@ -59,7 +59,7 @@ Nuclei使用零误报的定制模板向目标发送请求,同时可以对主
|
||||
|
||||
# 安装Nuclei
|
||||
|
||||
Nuclei需要 **go1.22** 才能安装成功。执行下列命令安装最新版本的Nuclei
|
||||
Nuclei需要 **go1.24.2** 才能安装成功。执行下列命令安装最新版本的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.cfg文件恢复扫描(将禁用请求聚类)
|
||||
-resume string 从指定文件恢复扫描并保存到指定文件(将禁用请求聚类)
|
||||
-sa, -scan-all-ips 扫描由目标解析出来的所有IP(针对域名对应多个IP的情况)
|
||||
-iv, -ip-version string[] 要扫描的主机名的IP版本(4,6)-(默认为4)
|
||||
|
||||
@@ -238,6 +238,7 @@ UNCOVER引擎:
|
||||
-c, -concurrency int 并行执行的最大模板数量(默认:25)
|
||||
-hbs, -headless-bulk-size int 每个模板并行运行的无头主机最大数量(默认:10)
|
||||
-headc, -headless-concurrency int 并行指定无头主机最大数量(默认:10)
|
||||
-tlc, -template-loading-concurrency int 最大并发模板加载操作数(默认:50)
|
||||
|
||||
|
||||
优化:
|
||||
@@ -370,7 +371,7 @@ Nuclei允许您定制自己的测试方法,可以轻松的运行您的程序
|
||||
- 可以在几分钟处理上千台主机
|
||||
- 使用YAML语法定制自动化测试
|
||||
|
||||
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](http://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](http://chaos.projectdiscovery.io)。
|
||||
欢迎查看我们其他的开源项目,可能有适合您的赏金猎人工作流:[github.com/projectdiscovery](https://github.com/projectdiscovery),我们还使用[Chaos绘制了每日的DNS数据](https://chaos.projectdiscovery.io)。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+3
-2
@@ -56,7 +56,7 @@ Tenemos un [repositorio dedicado](https://github.com/projectdiscovery/nuclei-tem
|
||||
|
||||
# Instalación de Nuclei
|
||||
|
||||
Nuclei requiere **go1.22** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
|
||||
Nuclei requiere **go1.24.2** 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 usando resume.cfg (la clusterización quedará inhabilitada)
|
||||
-resume string reanudar el escaneo desde y guardar en el archivo especificado (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,6 +244,7 @@ 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)
|
||||
|
||||
+4
-3
@@ -54,7 +54,7 @@ Kami memiliki [repositori khusus](https://github.com/projectdiscovery/nuclei-tem
|
||||
|
||||
# Instalasi Nuclei
|
||||
|
||||
Nuclei membutuhkan **go1.22** agar dapat diinstall. Jalankan perintah berikut untuk menginstal versi terbaru -
|
||||
Nuclei membutuhkan **go1.24.2** 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 using resume.cfg (clustering will be disabled)
|
||||
-resume string resume scan from and save to specified file (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,6 +211,7 @@ 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)
|
||||
@@ -341,7 +342,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](http://github.com/projectdiscovery), kami juga menyediakan [penyegaran data DNS di Chaos setiap hari](http://chaos.projectdiscovery.io).
|
||||
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).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+2
-2
@@ -53,7 +53,7 @@ Nucleiは、テンプレートに基づいてターゲット間でリクエス
|
||||
|
||||
# Nucleiのインストール
|
||||
|
||||
Nucleiを正常にインストールするには、**go1.22**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
Nucleiを正常にインストールするには、**go1.24.2**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
|
||||
```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.cfgを使用してスキャンを再開(クラスタリングは無効になります)
|
||||
-resume string 指定されたファイルからスキャンを再開し、指定されたファイルに保存(クラスタリングは無効になります)
|
||||
-sa, -scan-all-ips DNSレコードに関連付けられているすべてのIPをスキャン
|
||||
-iv, -ip-version string[] ホスト名のスキャンするIPバージョン(4,6)-(デフォルトは4)
|
||||
|
||||
|
||||
+4
-3
@@ -52,7 +52,7 @@ Nuclei는 템플릿을 기반으로 대상 간에 요청을 보내기 위해 사
|
||||
|
||||
# 설치
|
||||
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.22**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
Nuclei를 성공적으로 설치하기 위해서 **go1.24.2**가 필요합니다. 다음 명령을 실행하여 최신 버전을 설치합니다.
|
||||
|
||||
```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.cfg를 사용하여 스캔 재개 (클러스터링은 비활성화됨)
|
||||
-resume string 지정된 파일에서 스캔을 재개하고 지정된 파일에 저장 (클러스터링은 비활성화됨)
|
||||
-sa, -scan-all-ips dns 레코드와 관련된 모든 IP 스캔
|
||||
-iv, -ip-version string[] 스캔할 호스트의 IP 버전 (4,6) - (기본값 4)
|
||||
|
||||
@@ -209,6 +209,7 @@ 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)
|
||||
@@ -341,7 +342,7 @@ Nuclei를 사용하면 자체 검사 모음으로 테스트 접근 방식을 사
|
||||
- 몇 분 안에 수천 개의 호스트를 처리할 수 있음.
|
||||
- 간단한 YAML DSL로 사용자 지정 테스트 접근 방식을 쉽게 자동화할 수 있음.
|
||||
|
||||
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](http://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](http://chaos.projectdiscovery.io).
|
||||
버그 바운티 워크플로에 맞는 다른 오픈 소스 프로젝트를 확인할 수 있습니다.: [github.com/projectdiscovery](https://github.com/projectdiscovery), 또한, 우리는 매일 [Chaos에서 DNS 데이터를 갱신해 호스팅합니다](https://chaos.projectdiscovery.io).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
+3
-2
@@ -56,7 +56,7 @@ Temos um [repositório dedicado](https://github.com/projectdiscovery/nuclei-temp
|
||||
|
||||
# Instalação do Nuclei
|
||||
|
||||
O Nuclei requer **go1.22** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
|
||||
O Nuclei requer **go1.24.2** para ser instalado corretamente. Execute o seguinte comando para instalar a versão mais recente:
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
@@ -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 usando resume.cfg (a clusterização será desabilitada)
|
||||
-resume string retomar o escaneamento a partir de e salvar no arquivo especificado (a clusterização será desabilitada)
|
||||
-sa, -scan-all-ips escanear todos os IPs associados ao registro DNS
|
||||
-iv, -ip-version string[] versão de IP a escanear do nome do host (4,6) - (padrão 4)
|
||||
|
||||
@@ -244,6 +244,7 @@ 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
@@ -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 overriden if target/input contains
|
||||
Port is the port to send network requests to. this acts as default port but is overridden if target/input contains
|
||||
non-http(s) ports like 80,8080,8081 etc
|
||||
|
||||
</div>
|
||||
@@ -4512,7 +4512,7 @@ permutations and combinations for all payloads.
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Payload concurreny i.e threads for sending requests.
|
||||
Payload concurrency i.e threads for sending requests.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 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
|
||||
{{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
|
||||
|
||||
# Advanced Filtering
|
||||
{{binary}} -tags cve -author geeknik,pdteam -tc severity=='high'
|
||||
{{binary}} -tc contains(authors,'pdteam')
|
||||
{{binary}} -t cves/ -t exposures/ -tc contains(tags,'cve') -exclude-templates cves/2020/CVE-2020-9757.yaml
|
||||
{{binary}} -t http/cves/ -t http/exposures/ -tc contains(tags,'cve') -exclude-templates http/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
|
||||
|
||||
@@ -24,6 +24,8 @@ 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() }},
|
||||
}
|
||||
|
||||
const (
|
||||
|
||||
@@ -0,0 +1,104 @@
|
||||
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
|
||||
}
|
||||
@@ -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"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
logutil "github.com/projectdiscovery/utils/log"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
@@ -62,9 +62,11 @@ 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{}},
|
||||
@@ -77,6 +79,7 @@ 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{}},
|
||||
@@ -196,7 +199,7 @@ func (d *httpDefaultMatcherCondition) Execute(filePath string) error {
|
||||
return err
|
||||
}
|
||||
if routerErr != nil {
|
||||
return errorutil.NewWithErr(routerErr).Msgf("failed to send http request to interactsh server")
|
||||
return errkit.Wrap(routerErr, "failed to send http request to interactsh server")
|
||||
}
|
||||
if err := expectResultsCount(results, 1); err != nil {
|
||||
return err
|
||||
@@ -628,10 +631,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 = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value1"}, params["key1"])
|
||||
errx = errkit.Append(errx, errkit.New("key1 not found in params", "expected", []string{"value1"}, "got", params["key1"]))
|
||||
}
|
||||
if !reflect.DeepEqual(params["key2"], []string{"value2"}) {
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value2"}, params["key2"])
|
||||
errx = errkit.Append(errx, errkit.New("key2 not found in params", "expected", []string{"value2"}, "got", params["key2"]))
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "Test is test raw-params-matcher text")
|
||||
})
|
||||
@@ -971,10 +974,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 = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"here"}, params["something"])
|
||||
errx = errkit.Append(errx, errkit.New("something not found in params", "expected", []string{"here"}, "got", params["something"]))
|
||||
}
|
||||
if !reflect.DeepEqual(params["key"], []string{"value"}) {
|
||||
errx = errorutil.WrapfWithNil(errx, "expected %v, got %v", []string{"value"}, params["key"])
|
||||
errx = errkit.Append(errx, errkit.New("key not found in params", "expected", []string{"value"}, "got", params["key"]))
|
||||
}
|
||||
_, _ = w.Write([]byte("This is self-contained response"))
|
||||
})
|
||||
@@ -1027,10 +1030,10 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
// create temp file
|
||||
FileLoc, err := os.CreateTemp("", "self-contained-payload-*.txt")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to create temp file")
|
||||
return errkit.Wrap(err, "failed to create temp file")
|
||||
}
|
||||
if _, err := FileLoc.Write([]byte("one\ntwo\n")); err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to write payload to temp file")
|
||||
return errkit.Wrap(err, "failed to write payload to temp file")
|
||||
}
|
||||
defer func() {
|
||||
_ = FileLoc.Close()
|
||||
@@ -1046,7 +1049,7 @@ func (h *httpRequestSelfContainedFileInput) Execute(filePath string) error {
|
||||
}
|
||||
|
||||
if !sliceutil.ElementsMatch(gotReqToEndpoints, []string{"/one", "/two", "/one", "/two"}) {
|
||||
return errorutil.NewWithTag(filePath, "expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints)
|
||||
return errkit.New("expected requests to be sent to `/one` and `/two` endpoints but were sent to `%v`", gotReqToEndpoints, "filePath", filePath)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -1153,6 +1156,26 @@ 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
|
||||
@@ -1220,6 +1243,30 @@ 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
|
||||
@@ -1466,6 +1513,26 @@ 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
|
||||
|
||||
@@ -6,6 +6,7 @@ import (
|
||||
"os"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/kitabisa/go-ci"
|
||||
@@ -24,7 +25,7 @@ type TestCaseInfo struct {
|
||||
}
|
||||
|
||||
var (
|
||||
debug = os.Getenv("DEBUG") == "true"
|
||||
debug = isDebugMode()
|
||||
customTests = os.Getenv("TESTS")
|
||||
protocol = os.Getenv("PROTO")
|
||||
|
||||
@@ -46,6 +47,7 @@ var (
|
||||
"library": libraryTestcases,
|
||||
"templatesPath": templatesPathTestCases,
|
||||
"templatesDir": templatesDirTestCases,
|
||||
"env_vars": templatesDirEnvTestCases,
|
||||
"file": fileTestcases,
|
||||
"offlineHttp": offlineHttpTestcases,
|
||||
"customConfigDir": customConfigDirTestCases,
|
||||
@@ -57,7 +59,9 @@ 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,
|
||||
@@ -88,11 +92,12 @@ 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") {
|
||||
@@ -102,7 +107,6 @@ func main() {
|
||||
}()
|
||||
|
||||
customTestsList := normalizeSplit(customTests)
|
||||
|
||||
failedTestTemplatePaths := runTests(customTestsList)
|
||||
|
||||
if len(failedTestTemplatePaths) > 0 {
|
||||
@@ -129,6 +133,27 @@ 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) {
|
||||
|
||||
@@ -15,13 +15,18 @@ 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/multi-ports.yaml", TestCase: &javascriptMultiPortsSSH{}},
|
||||
}
|
||||
|
||||
var (
|
||||
redisResource *dockertest.Resource
|
||||
sshResource *dockertest.Resource
|
||||
pool *dockertest.Pool
|
||||
defaultRetry = 3
|
||||
redisResource *dockertest.Resource
|
||||
sshResource *dockertest.Resource
|
||||
oracleResource *dockertest.Resource
|
||||
vncResource *dockertest.Resource
|
||||
pool *dockertest.Pool
|
||||
defaultRetry = 3
|
||||
)
|
||||
|
||||
type javascriptNetHttps struct{}
|
||||
@@ -98,6 +103,82 @@ 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 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)
|
||||
}
|
||||
|
||||
// purge any given resource if it is not nil
|
||||
func purge(resource *dockertest.Resource) {
|
||||
if resource != nil && pool != nil {
|
||||
@@ -163,4 +244,41 @@ 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)
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ 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"
|
||||
@@ -70,6 +71,7 @@ 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)
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/julienschmidt/httprouter"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
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 errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
return errkit.Wrap(err, "failed to load template with id")
|
||||
}
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
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 errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
return errkit.Wrap(err, "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 errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
return errkit.Wrap(err, "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 errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
return errkit.Wrap(err, "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))
|
||||
|
||||
@@ -4,7 +4,7 @@ import (
|
||||
"os"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
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 errorutil.NewWithErr(err).Msgf("failed to create temp dir")
|
||||
return errkit.Wrap(err, "failed to create temp dir")
|
||||
}
|
||||
defer func() {
|
||||
_ = os.RemoveAll(tempdir)
|
||||
|
||||
@@ -0,0 +1,217 @@
|
||||
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)
|
||||
}
|
||||
+55
-7
@@ -20,6 +20,7 @@ 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"
|
||||
@@ -39,7 +40,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"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
updateutils "github.com/projectdiscovery/utils/update"
|
||||
@@ -187,14 +188,17 @@ func main() {
|
||||
options.Logger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("couldn't create crash resume file")
|
||||
return errkit.Wrap(err, "couldn't create crash resume file")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
// Setup graceful exits
|
||||
// Setup filename for graceful exits
|
||||
resumeFileName := types.DefaultResumeFilePath()
|
||||
if options.Resume == "" {
|
||||
resumeFileName = options.Resume
|
||||
}
|
||||
c := make(chan os.Signal, 1)
|
||||
signal.Notify(c, os.Interrupt)
|
||||
go func() {
|
||||
@@ -254,7 +258,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 using resume.cfg (clustering will be disabled)"),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "resume scan from and save to specified file (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),
|
||||
)
|
||||
@@ -263,6 +267,8 @@ 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",
|
||||
@@ -277,7 +283,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 available templates"),
|
||||
flagSet.BoolVar(&options.TemplateList, "tl", false, "list all templates matching current filters"),
|
||||
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"),
|
||||
@@ -408,6 +414,7 @@ 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"),
|
||||
@@ -571,6 +578,7 @@ 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)
|
||||
@@ -579,9 +587,49 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
if options.NewTemplatesDirectory != "" {
|
||||
config.DefaultConfig.SetTemplatesDir(options.NewTemplatesDirectory)
|
||||
|
||||
templatesDir := options.NewTemplatesDirectory
|
||||
if templatesDir == "" {
|
||||
templatesDir = os.Getenv(config.NucleiTemplatesDirEnv)
|
||||
}
|
||||
if templatesDir != "" {
|
||||
config.DefaultConfig.SetTemplatesDir(templatesDir)
|
||||
}
|
||||
|
||||
defaultProfilesPath := filepath.Join(config.DefaultConfig.GetTemplateDir(), "profiles")
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
package main_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@@ -48,6 +52,31 @@ 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"},
|
||||
@@ -106,24 +135,55 @@ func runEnumBenchmark(b *testing.B, options *types.Options) {
|
||||
}
|
||||
defer nucleiRunner.Close()
|
||||
|
||||
b.ResetTimer()
|
||||
benchNameSlug := strings.ReplaceAll(b.Name(), "/", "-")
|
||||
|
||||
// Start CPU profiling
|
||||
cpuProfileBase := fmt.Sprintf("%s.cpu.prof", benchNameSlug)
|
||||
cpuProfilePath := getUniqFilename(cpuProfileBase)
|
||||
cpuProfile, err := os.Create(cpuProfilePath)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create CPU profile: %s", err)
|
||||
}
|
||||
defer func() { _ = cpuProfile.Close() }()
|
||||
|
||||
if err := pprof.StartCPUProfile(cpuProfile); err != nil {
|
||||
b.Fatalf("failed to start CPU profile: %s", err)
|
||||
}
|
||||
defer pprof.StopCPUProfile()
|
||||
|
||||
b.ReportAllocs()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
for b.Loop() {
|
||||
if err := nucleiRunner.RunEnumeration(); err != nil {
|
||||
b.Fatalf("%s failed: %s", b.Name(), err)
|
||||
}
|
||||
}
|
||||
|
||||
b.StopTimer()
|
||||
|
||||
// Write heap profile
|
||||
heapProfileBase := fmt.Sprintf("%s.heap.prof", benchNameSlug)
|
||||
heapProfilePath := getUniqFilename(heapProfileBase)
|
||||
heapProfile, err := os.Create(heapProfilePath)
|
||||
if err != nil {
|
||||
b.Fatalf("failed to create heap profile: %s", err)
|
||||
}
|
||||
defer func() { _ = heapProfile.Close() }()
|
||||
|
||||
runtime.GC() // Force GC before heap profile
|
||||
if err := pprof.WriteHeapProfile(heapProfile); err != nil {
|
||||
b.Fatalf("failed to write heap profile: %s", err)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkRunEnumeration(b *testing.B) {
|
||||
// Default case: run enumeration with default options == all nuclei-templates
|
||||
// b.Run("Default", func(b *testing.B) {
|
||||
// options := getDefaultOptions()
|
||||
// options.Targets = []string{targetURL}
|
||||
b.Run("Default", func(b *testing.B) {
|
||||
options := getDefaultOptions()
|
||||
options.Targets = []string{targetURL}
|
||||
|
||||
// runEnumBenchmark(b, options)
|
||||
// })
|
||||
runEnumBenchmark(b, options)
|
||||
})
|
||||
|
||||
// Case: https://github.com/projectdiscovery/nuclei/pull/6258
|
||||
b.Run("Multiproto", func(b *testing.B) {
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
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
@@ -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"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"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, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
return data, false, errkit.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, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
|
||||
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
}
|
||||
return data, false, errorutil.New("validation failed").WithTag("validate")
|
||||
return data, false, errkit.New("validation failed", "tag", "validate")
|
||||
}
|
||||
if templateResp.Error.Name != "" {
|
||||
return data, false, errorutil.New("%s", templateResp.Error.Name)
|
||||
return data, false, errkit.New("%s", templateResp.Error.Name)
|
||||
}
|
||||
if strings.TrimSpace(templateResp.Enhanced) == "" && !templateResp.Lint {
|
||||
if templateResp.LintError.Reason != "" {
|
||||
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
|
||||
return data, false, errkit.New(templateResp.LintError.Reason+" : 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("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
}
|
||||
return data, false, errorutil.New("template enhance failed")
|
||||
return data, false, errkit.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, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
return data, false, errkit.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, errorutil.NewWithTag("validate", templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line)
|
||||
return data, false, errkit.New(templateResp.ValidateError[0].Message+": at line %v", templateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
}
|
||||
return data, false, errorutil.New("validation failed").WithTag("validate")
|
||||
return data, false, errkit.New("validation failed", "tag", "validate")
|
||||
}
|
||||
if templateResp.Error.Name != "" {
|
||||
return data, false, errorutil.New("%s", templateResp.Error.Name)
|
||||
return data, false, errkit.New("%s", templateResp.Error.Name)
|
||||
}
|
||||
if strings.TrimSpace(templateResp.Updated) == "" && !templateResp.Lint {
|
||||
if templateResp.LintError.Reason != "" {
|
||||
return data, false, errorutil.NewWithTag("lint", templateResp.LintError.Reason+" : at line %v", templateResp.LintError.Mark.Line)
|
||||
return data, false, errkit.New(templateResp.LintError.Reason+" : 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("at line: %v", templateResp.LintError.Mark.Line, "tag", "lint")
|
||||
}
|
||||
return data, false, errorutil.New("template format failed")
|
||||
return data, false, errkit.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, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
return false, errkit.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, errorutil.NewWithTag("lint", lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line)
|
||||
return false, errkit.New(lintResp.LintError.Reason+" : at line %v", lintResp.LintError.Mark.Line, "tag", "lint")
|
||||
}
|
||||
return false, errorutil.NewWithTag("lint", "at line: %v", lintResp.LintError.Mark.Line)
|
||||
return false, errkit.New("at line: %v", lintResp.LintError.Mark.Line, "tag", "lint")
|
||||
}
|
||||
|
||||
// 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, errorutil.New("unexpected status code: %v", resp.Status)
|
||||
return false, errkit.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, errorutil.NewWithTag("validate", validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line)
|
||||
return false, errkit.New(validateResp.ValidateError[0].Message+": at line %v", validateResp.ValidateError[0].Mark.Line, "tag", "validate")
|
||||
}
|
||||
return false, errorutil.New("validation failed").WithTag("validate")
|
||||
return false, errkit.New("validation failed", "tag", "validate")
|
||||
}
|
||||
if validateResp.Error.Name != "" {
|
||||
return false, errorutil.New("%s", validateResp.Error.Name)
|
||||
return false, errkit.New("%s", validateResp.Error.Name)
|
||||
}
|
||||
return false, errorutil.New("template validation failed")
|
||||
return false, errkit.New("template validation failed")
|
||||
}
|
||||
|
||||
// parseAndAddMaxRequests parses and adds max requests to templates
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
module github.com/projectdiscovery/nuclei/v3
|
||||
|
||||
go 1.24.1
|
||||
go 1.24.2
|
||||
|
||||
toolchain go1.24.4
|
||||
|
||||
require (
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
|
||||
github.com/andygrunwald/go-jira v1.16.0
|
||||
github.com/antchfx/htmlquery v1.3.4
|
||||
github.com/andygrunwald/go-jira v1.16.1
|
||||
github.com/antchfx/htmlquery v1.3.5
|
||||
github.com/bluele/gcache v0.0.2
|
||||
github.com/go-playground/validator/v10 v10.26.0
|
||||
github.com/go-rod/rod v0.116.2
|
||||
@@ -16,16 +18,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.66
|
||||
github.com/miekg/dns v1.1.68
|
||||
github.com/olekukonko/tablewriter v1.0.8
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/clistats v0.1.1
|
||||
github.com/projectdiscovery/fastdialer v0.4.4
|
||||
github.com/projectdiscovery/hmap v0.0.92
|
||||
github.com/projectdiscovery/fastdialer v0.4.20
|
||||
github.com/projectdiscovery/hmap v0.0.98
|
||||
github.com/projectdiscovery/interactsh v1.2.4
|
||||
github.com/projectdiscovery/rawhttp v0.1.90
|
||||
github.com/projectdiscovery/retryabledns v1.0.105
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.119
|
||||
github.com/projectdiscovery/retryabledns v1.0.111
|
||||
github.com/projectdiscovery/retryablehttp-go v1.1.1
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.6
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.6.0
|
||||
@@ -35,26 +37,28 @@ 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.40.3-0.20250408071509-6074bbe7fd39
|
||||
github.com/weppos/publicsuffix-go v0.50.1
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/net v0.41.0
|
||||
golang.org/x/net v0.47.0
|
||||
golang.org/x/oauth2 v0.30.0
|
||||
golang.org/x/text v0.26.0
|
||||
golang.org/x/text v0.31.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.21.0
|
||||
carvel.dev/ytt v0.52.0
|
||||
code.gitea.io/sdk/gitea v0.17.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.10.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.6.1
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
|
||||
github.com/DataDog/gostackparse v0.7.0
|
||||
github.com/Masterminds/semver/v3 v3.4.0
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
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.4
|
||||
github.com/antchfx/xpath v1.3.5
|
||||
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
|
||||
@@ -71,7 +75,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 v8.0.7+incompatible
|
||||
github.com/go-pg/pg/v10 v10.15.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/goccy/go-json v0.10.5
|
||||
github.com/google/uuid v1.6.0
|
||||
@@ -83,68 +87,72 @@ 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/mholt/archives v0.1.3
|
||||
github.com/maypok86/otter/v2 v2.2.1
|
||||
github.com/mholt/archives v0.1.5
|
||||
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.5.0
|
||||
github.com/projectdiscovery/dsl v0.8.8
|
||||
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.54
|
||||
github.com/projectdiscovery/gologger v1.1.64
|
||||
github.com/projectdiscovery/gostruct v0.0.2
|
||||
github.com/projectdiscovery/gozero v0.0.3
|
||||
github.com/projectdiscovery/httpx v1.7.0
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81
|
||||
github.com/projectdiscovery/httpx v1.7.4
|
||||
github.com/projectdiscovery/mapcidr v1.1.97
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/networkpolicy v0.1.18
|
||||
github.com/projectdiscovery/ratelimit v0.0.81
|
||||
github.com/projectdiscovery/networkpolicy v0.1.32
|
||||
github.com/projectdiscovery/ratelimit v0.0.82
|
||||
github.com/projectdiscovery/rdap v0.9.0
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.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/projectdiscovery/tlsx v1.2.2
|
||||
github.com/projectdiscovery/uncover v1.2.0
|
||||
github.com/projectdiscovery/useragent v0.0.105
|
||||
github.com/projectdiscovery/utils v0.7.3
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.59
|
||||
github.com/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/stretchr/testify v1.10.0
|
||||
github.com/sijms/go-ora/v2 v2.9.0
|
||||
github.com/stretchr/testify v1.11.1
|
||||
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.32.0
|
||||
golang.org/x/term v0.37.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.0 // indirect
|
||||
dario.cat/mergo v1.0.2 // 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-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // 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.10.3 // indirect
|
||||
github.com/STARRY-S/zip v0.2.1 // indirect
|
||||
github.com/PuerkitoBio/goquery v1.11.0 // indirect
|
||||
github.com/STARRY-S/zip v0.2.3 // 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.1.2-0.20250424173009-453214e765f3 // indirect
|
||||
github.com/andybalholm/brotli v1.2.0 // 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
|
||||
@@ -166,12 +174,15 @@ 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.0 // indirect
|
||||
github.com/bodgit/sevenzip v1.6.1 // 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
|
||||
@@ -179,27 +190,36 @@ 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.4 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.1.6 // 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-20201127153200-e1b16c1ebc08 // indirect
|
||||
github.com/cnf/structhash v0.0.0-20250313080605-df4c6cc74a9a // indirect
|
||||
github.com/containerd/continuity v0.4.5 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.4.1 // 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/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/dlclark/regexp2 v1.11.5 // indirect
|
||||
github.com/docker/cli v27.4.1+incompatible // indirect
|
||||
github.com/docker/docker v28.0.0+incompatible // indirect
|
||||
github.com/docker/go-connections v0.5.0 // indirect
|
||||
github.com/docker/docker v28.3.3+incompatible // indirect
|
||||
github.com/docker/go-connections v0.6.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.20.5 // indirect
|
||||
github.com/gaissmai/bart v0.26.0 // 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
|
||||
@@ -207,33 +227,37 @@ 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-ole/go-ole v1.2.6 // 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-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.3.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.4.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.1.4 // indirect
|
||||
github.com/google/certificate-transparency-go v1.3.2 // 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/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/gosimple/slug v1.15.0 // indirect
|
||||
github.com/gosimple/unidecode v1.0.1 // 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
|
||||
@@ -243,9 +267,10 @@ 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.0 // indirect
|
||||
github.com/klauspost/compress v1.18.2 // 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
|
||||
@@ -253,10 +278,11 @@ 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-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/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/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
|
||||
@@ -264,37 +290,42 @@ 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.0 // indirect
|
||||
github.com/minio/minlz v1.0.1 // 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/sys/user v0.3.0 // indirect
|
||||
github.com/moby/term v0.5.0 // 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/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.1.0 // indirect
|
||||
github.com/nwaples/rardecode/v2 v2.2.1 // 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.0 // indirect
|
||||
github.com/opencontainers/runc v1.2.3 // indirect
|
||||
github.com/opencontainers/image-spec v1.1.1 // indirect
|
||||
github.com/opencontainers/runc v1.2.8 // 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.21 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.22 // 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-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.1 // indirect
|
||||
github.com/projectdiscovery/blackrock v0.0.1 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.1.26 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.2.14 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.7 // indirect
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
|
||||
@@ -302,25 +333,33 @@ require (
|
||||
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.5 // indirect
|
||||
github.com/sorairolake/lzip-go v0.3.8 // indirect
|
||||
github.com/spf13/afero v1.15.0 // 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.1.1 // indirect
|
||||
github.com/tidwall/match v1.2.0 // 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.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // 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/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/ulikunitz/xz v0.5.15 // 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
|
||||
@@ -333,13 +372,18 @@ 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.8 // indirect
|
||||
github.com/yuin/goldmark v1.7.13 // 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.15.0 // indirect
|
||||
golang.org/x/sync v0.18.0 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.2 // indirect
|
||||
)
|
||||
@@ -360,16 +404,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.3.10 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
go.etcd.io/bbolt v1.4.0 // indirect
|
||||
go.uber.org/zap v1.27.0 // indirect
|
||||
goftp.io/server/v2 v2.0.1 // indirect
|
||||
golang.org/x/crypto v0.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
|
||||
golang.org/x/crypto v0.45.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250911091902-df9299821621
|
||||
golang.org/x/mod v0.29.0 // indirect
|
||||
golang.org/x/sys v0.38.0 // indirect
|
||||
golang.org/x/time v0.14.0 // indirect
|
||||
golang.org/x/tools v0.38.0
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
|
||||
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
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=
|
||||
@@ -33,17 +35,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.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=
|
||||
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=
|
||||
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/42wim/httpsig v1.2.2 h1:ofAYoHUNs/MJOLqQ8hIxeyz2QxOz8qdSVvp3PX/oPgA=
|
||||
github.com/42wim/httpsig v1.2.2/go.mod h1:P/UYo7ytNBFwc+dg35IubuAUIs8zj5zzFIgUCEl55WY=
|
||||
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/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=
|
||||
@@ -52,16 +54,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.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/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/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.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/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/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=
|
||||
@@ -69,13 +71,15 @@ 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.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
|
||||
github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
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/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=
|
||||
@@ -94,11 +98,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.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
|
||||
github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
|
||||
github.com/PuerkitoBio/goquery v1.11.0 h1:jZ7pwMQXIITcUXNH83LLk+txlaEy6NVOfTuP43xxfqw=
|
||||
github.com/PuerkitoBio/goquery v1.11.0/go.mod h1:wQHgxUOU3JGuj3oD/QFfxUdlzW6xPHfqyHre6VMY4DQ=
|
||||
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.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg=
|
||||
github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4=
|
||||
github.com/STARRY-S/zip v0.2.3 h1:luE4dMvRPDOWQdeDdUxUoZkzUIpTccdKdhHHsQJ1fm4=
|
||||
github.com/STARRY-S/zip v0.2.3/go.mod h1:lqJ9JdeRipyOQJrYSOtpNAiaesFO6zVDsE8GIGFaoSk=
|
||||
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=
|
||||
@@ -124,23 +128,25 @@ 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.1.2-0.20250424173009-453214e765f3 h1:8PmGpDEZl9yDpcdEr6Odf23feCxK3LNUNMxjXg41pZQ=
|
||||
github.com/andybalholm/brotli v1.1.2-0.20250424173009-453214e765f3/go.mod h1:05ib4cKhjx3OQYUY22hTVd34Bc8upXjOLL2rKwwZBoA=
|
||||
github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ=
|
||||
github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY=
|
||||
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.0 h1:PU7C7Fkk5L96JvPc6vDVIrd99vdPnYudHu4ju2c2ikQ=
|
||||
github.com/andygrunwald/go-jira v1.16.0/go.mod h1:UQH4IBVxIYWbgagc0LF/k9FRs9xjIiQ8hIcC6HfLwFU=
|
||||
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/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.4 h1:Isd0srPkni2iNTWCwVj/72t7uCphFeor5Q8nCzj1jdQ=
|
||||
github.com/antchfx/htmlquery v1.3.4/go.mod h1:K9os0BwIEmLAvTqaNSua8tXLWRWZpocZIH73OzWQbwM=
|
||||
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/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.4 h1:1ixrW1VnXd4HurCj7qnqnR0jo14g8JMe20Fshg1Vgz4=
|
||||
github.com/antchfx/xpath v1.3.4/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/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=
|
||||
@@ -191,8 +197,6 @@ 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=
|
||||
@@ -205,10 +209,12 @@ 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.0 h1:a4R0Wu6/P1o1pP/3VV++aEOcyeBxeO/xE2Y9NSTrr6A=
|
||||
github.com/bodgit/sevenzip v1.6.0/go.mod h1:zOBh9nJUof7tcrlqJFv1koWRrhz3LbDbUNngkuZxLMc=
|
||||
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/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=
|
||||
@@ -225,7 +231,11 @@ 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=
|
||||
@@ -249,8 +259,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.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
|
||||
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/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=
|
||||
@@ -272,14 +282,24 @@ 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-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
|
||||
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/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.4.1 h1:JyxxyPEaktOD+GAnqIqTf9A8tHyAG22rowi7HkoSU1s=
|
||||
github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI=
|
||||
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/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=
|
||||
@@ -290,6 +310,8 @@ 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/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
@@ -297,16 +319,18 @@ github.com/dlclark/regexp2 v1.11.5 h1:Q/sSnsKerHeCkc/jSTNq1oCm7KiVgUMZRDUoRu0JQZ
|
||||
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.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/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/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=
|
||||
@@ -315,12 +339,16 @@ 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=
|
||||
@@ -332,8 +360,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.20.5 h1:ehoWZWQ7j//qt0K0Zs4i9hpoPpbgqsMQiR8W2QPJh+c=
|
||||
github.com/gaissmai/bart v0.20.5/go.mod h1:cEed+ge8dalcbpi8wtS9x9m2hn/fNJH5suhdGQOHnYk=
|
||||
github.com/gaissmai/bart v0.26.0 h1:xOZ57E9hJLBiQaSyeZa9wgWhGuzfGACgqp4BE77OkO0=
|
||||
github.com/gaissmai/bart v0.26.0/go.mod h1:GREWQfTLRWz/c5FTOsIw+KkscuFkIV5t8Rp7Nd1Td5c=
|
||||
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=
|
||||
@@ -372,14 +400,22 @@ 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-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
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/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 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-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-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=
|
||||
@@ -397,8 +433,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.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk=
|
||||
github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
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/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=
|
||||
@@ -459,8 +495,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.1.4 h1:hCyXHDbtqlr/lMXU0D4WgbalXL0Zk4dSWWMbPV8VrqY=
|
||||
github.com/google/certificate-transparency-go v1.1.4/go.mod h1:D6lvbfwckhNrbM9WVl1EVeMOyzC19mpIjMOI4nxBHtQ=
|
||||
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/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=
|
||||
@@ -471,7 +507,6 @@ 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=
|
||||
@@ -486,6 +521,8 @@ 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=
|
||||
@@ -511,22 +548,24 @@ 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=
|
||||
@@ -540,6 +579,8 @@ 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=
|
||||
@@ -587,6 +628,10 @@ 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=
|
||||
@@ -598,8 +643,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.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
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/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=
|
||||
@@ -638,14 +683,17 @@ 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/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/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/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=
|
||||
@@ -659,22 +707,24 @@ 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.3 h1:aEAaOtNra78G+TvV5ohmXrJOAzf++dIlYeDW3N9q458=
|
||||
github.com/mholt/archives v0.1.3/go.mod h1:LUCGp++/IbV/I0Xq4SzcIR6uwgeh2yjnQWamjRQfLTU=
|
||||
github.com/mholt/archives v0.1.5 h1:Fh2hl1j7VEhc6DZs2DLMgiBNChUux154a1G+2esNvzQ=
|
||||
github.com/mholt/archives v0.1.5/go.mod h1:3TPMmBLPsgszL+1As5zECTuKwKvIfj6YcwWPpeTAXF4=
|
||||
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.66 h1:FeZXOS3VCVsKnEAd+wBkjMC3D2K+ww66Cq3VnCINuJE=
|
||||
github.com/miekg/dns v1.1.66/go.mod h1:jGFzBsSNbJw6z1HYut1RKBKHA9PBdxeHrZG8J+gC2WE=
|
||||
github.com/miekg/dns v1.1.68 h1:jsSRkNozw7G/mnmXULynzMNIsgY2dHC8LO6U6Ij2JEA=
|
||||
github.com/miekg/dns v1.1.68/go.mod h1:fujopn7TB3Pu3JM69XaawiU0wqjpL9/8xGop5UrTPps=
|
||||
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.0 h1:Kj7aJZ1//LlTP1DM8Jm7lNKvvJS2m74gyyXXn3+uJWQ=
|
||||
github.com/minio/minlz v1.0.0/go.mod h1:qT0aEB35q79LLornSzeDH75LBf3aH1MV+jB5w9Wasec=
|
||||
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/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=
|
||||
@@ -682,10 +732,20 @@ 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/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/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/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=
|
||||
@@ -697,6 +757,8 @@ 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=
|
||||
@@ -705,8 +767,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.1.0 h1:JQl9ZoBPDy+nIZGb1mx8+anfHp/LV3NE2MjMiv0ct/U=
|
||||
github.com/nwaples/rardecode/v2 v2.1.0/go.mod h1:7uz379lSxPe6j9nvzxUZ+n7mnJNgjsRNb6IbvGVHRmw=
|
||||
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/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=
|
||||
@@ -729,10 +791,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.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/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/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=
|
||||
@@ -742,8 +804,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.21 h1:yOVMLb6qSIDP67pl/5F7RepeKYu/VmTyEXvuMI5d9mQ=
|
||||
github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
|
||||
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/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=
|
||||
@@ -755,22 +817,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-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
|
||||
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/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.1.26 h1:R6JxzU3ptGNrzzvS261xHWssDt9GlOCWXkuet1huOyA=
|
||||
github.com/projectdiscovery/cdncheck v1.1.26/go.mod h1:dFEGsG0qAJY0AaRr2N1BY0OtZiTxS4kYeT5+OkF8t1U=
|
||||
github.com/projectdiscovery/cdncheck v1.2.14 h1:mHfNMdFwpQziPxIZnE4i0UERU/8vOcxSTcjYwp1PA2Y=
|
||||
github.com/projectdiscovery/cdncheck v1.2.14/go.mod h1:wPckYZjRFQydtbosCS0beGjuAP5h4faX7eUqB8L3/cA=
|
||||
github.com/projectdiscovery/clistats v0.1.1 h1:8mwbdbwTU4aT88TJvwIzTpiNeow3XnAB72JIg66c8wE=
|
||||
github.com/projectdiscovery/clistats v0.1.1/go.mod h1:4LtTC9Oy//RiuT1+76MfTg8Hqs7FQp1JIGBM3nHK6a0=
|
||||
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/dsl v0.8.8 h1:UlcCf/2hfqqfDlhHWun3xT0u310iLWMiRoVw1t3TTLg=
|
||||
github.com/projectdiscovery/dsl v0.8.8/go.mod h1:gXrMGOfBVu4UvgabOfXSMqeC5RnR93aeZ94+V32OlHI=
|
||||
github.com/projectdiscovery/fastdialer v0.4.20 h1:LBw2pB9Y/0Du+d/asyB4u5vF+QhvkPIo+SLcUaf2pG8=
|
||||
github.com/projectdiscovery/fastdialer v0.4.20/go.mod h1:GXzyco8SVX6E335FKpIiX0WSRbK2MsNwvqXCr195QtM=
|
||||
github.com/projectdiscovery/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=
|
||||
@@ -781,52 +843,52 @@ 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.54 h1:WMzvJ8j/4gGfPKpCttSTaYCVDU1MWQSJnk3wU8/U6Ws=
|
||||
github.com/projectdiscovery/gologger v1.1.54/go.mod h1:vza/8pe2OKOt+ujFWncngknad1XWr8EnLKlbcejOyUE=
|
||||
github.com/projectdiscovery/gologger v1.1.64 h1:GQ1DplEo5YnVozPImYWQqkeU5rwkPBxjMzeZmirVMdA=
|
||||
github.com/projectdiscovery/gologger v1.1.64/go.mod h1:daez34xaA7LTazBb4t+Ccm9/9Kjvlsu4EY033lQdATA=
|
||||
github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M=
|
||||
github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
|
||||
github.com/projectdiscovery/gozero v0.0.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/gozero v0.1.1-0.20251027191944-a4ea43320b81 h1:yHh46pJovYbyiaHCV7oIDinFmy+Fyq36H1BowJgb0M0=
|
||||
github.com/projectdiscovery/gozero v0.1.1-0.20251027191944-a4ea43320b81/go.mod h1:9lmGPBDGZVANzCGjQg+V32n8Y3Cgjo/4kT0E88lsVTI=
|
||||
github.com/projectdiscovery/hmap v0.0.98 h1:XxYIi7yJCNiDAKCJXvuY9IBM5O6OgDgx4XHgKxkR4eg=
|
||||
github.com/projectdiscovery/hmap v0.0.98/go.mod h1:bgN5fuZPJMj2YnAGEEnCypoifCnALJixHEVQszktQIU=
|
||||
github.com/projectdiscovery/httpx v1.7.4 h1:GUEklAZ71VKM0krmgck2Km5odJi5dwfo0euc2r88tj0=
|
||||
github.com/projectdiscovery/httpx v1.7.4/go.mod h1:sPnFYIXh0RAdjb02vpUGOsP5j28VDZs5khjMbXwEUGQ=
|
||||
github.com/projectdiscovery/interactsh v1.2.4 h1:WUSj+fxbcV53J64oIAhbYzCKD1w/IyenyRBhkI5jiqI=
|
||||
github.com/projectdiscovery/interactsh v1.2.4/go.mod h1:E/IVNZ80/WKz8zTwGJWQygxIbhlRmuzZFsZwcGSZTdc=
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb/go.mod h1:vmgC0DTFCfoCLp0RAfsfYTZZan0QMVs+cmTbH6blfjk=
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE=
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983/go.mod h1:3G3BRKui7nMuDFAZKR/M2hiOLtaOmyukT20g88qRQjI=
|
||||
github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQtls1wmXN/kM=
|
||||
github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ=
|
||||
github.com/projectdiscovery/mapcidr v1.1.97 h1:7FkxNNVXp+m1rIu5Nv/2SrF9k4+LwP8QuWs2puwy+2w=
|
||||
github.com/projectdiscovery/mapcidr v1.1.97/go.mod h1:9dgTJh1SP02gYZdpzMjm6vtYFkEHQHoTyaVNvaeJ7lA=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
|
||||
github.com/projectdiscovery/networkpolicy v0.1.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/networkpolicy v0.1.32 h1:MD561zl0/fSzcNTlDlf/7pcQbYzulYoQssFBFjb2pcg=
|
||||
github.com/projectdiscovery/networkpolicy v0.1.32/go.mod h1:5x4rGh4XhnoYl9wACnZyrjDGKIB/bQqxw2KrIM5V+XU=
|
||||
github.com/projectdiscovery/ratelimit v0.0.82 h1:rtO5SQf5uQFu5zTahTaTcO06OxmG8EIF1qhdFPIyTak=
|
||||
github.com/projectdiscovery/ratelimit v0.0.82/go.mod h1:z076BrLkBb5yS7uhHNoCTf8X/BvFSGRxwQ8EzEL9afM=
|
||||
github.com/projectdiscovery/rawhttp v0.1.90 h1:LOSZ6PUH08tnKmWsIwvwv1Z/4zkiYKYOSZ6n+8RFKtw=
|
||||
github.com/projectdiscovery/rawhttp v0.1.90/go.mod h1:VZYAM25UI/wVB3URZ95ZaftgOnsbphxyAw/XnQRRz4Y=
|
||||
github.com/projectdiscovery/rdap v0.9.0 h1:wPhHx5pQ2QI+WGhyNb2PjhTl0NtB39Nk7YFZ9cp8ZGA=
|
||||
github.com/projectdiscovery/rdap v0.9.0/go.mod h1:zk4yrJFQ2Hy36Aqk+DvotYQxYAeALaCJ5ORySkff36Q=
|
||||
github.com/projectdiscovery/retryabledns v1.0.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/retryabledns v1.0.111 h1:iyMdCDgNmaSRJYcGqB+SLlvlw9WijlbJ6Q9OEpRAWsQ=
|
||||
github.com/projectdiscovery/retryabledns v1.0.111/go.mod h1:6TOPJ3QAE4reBu6bvsGsTcyEb+OypcKYFQH7yVsjyIM=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.1.1 h1:R1r5DgxXIW+AH98ifKxFpeFbZAXP/eP9bNOMpaO32As=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.1.1/go.mod h1:D+jVQkvQ3ZNDYX30SfxJQ4ylh//NMPq1Yf11lEo+oyA=
|
||||
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
|
||||
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
||||
github.com/projectdiscovery/tlsx v1.1.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/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.105 h1:yFGFTfWZ/RZP5XbGRJtvKcbRNnlGI6xfPRXHb8DWdhg=
|
||||
github.com/projectdiscovery/useragent v0.0.105/go.mod h1:jWG1BD2yu8EC3olt6Amke7BiyLkXzpErI7Jtzr/tWZM=
|
||||
github.com/projectdiscovery/utils v0.7.3 h1:kX+77AA58yK6EZgkTRJEnK9V/7AZYzlXdcu/o/kJhFs=
|
||||
github.com/projectdiscovery/utils v0.7.3/go.mod h1:uDdQ3/VWomai98l+a3Ye/srDXdJ4xUIar/mSXlQ9gBM=
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.59 h1:oztLKyEf3yC2ncUIfSXguVA6jwb7iaV5tuK1BF3K9CY=
|
||||
github.com/projectdiscovery/wappalyzergo v0.2.59/go.mod h1:lwuDLdAqWDZ1IL8OQnoNQ0t17UP9AQSvVuFcDAm4FpQ=
|
||||
github.com/projectdiscovery/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=
|
||||
@@ -883,12 +945,16 @@ 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=
|
||||
@@ -904,31 +970,41 @@ 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.5 h1:ms5Xri9o1JBIWvOFAorYtUNik6HI3HgBTkISiqu0Cwg=
|
||||
github.com/sorairolake/lzip-go v0.3.5/go.mod h1:N0KYq5iWrMXI0ZEXKXaS9hCyOjZUQdBDEIbXfoUwbdk=
|
||||
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/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.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
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/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=
|
||||
@@ -942,8 +1018,9 @@ 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=
|
||||
@@ -953,10 +1030,12 @@ 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.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/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/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=
|
||||
@@ -966,18 +1045,28 @@ 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.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/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/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.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 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/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=
|
||||
@@ -1030,8 +1119,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.8 h1:iERMLn0/QJeHFhxSt3p6PeN9mGnvIKSpG9YYorDMnic=
|
||||
github.com/yuin/goldmark v1.7.8/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-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=
|
||||
@@ -1060,8 +1149,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.3.10 h1:+BqfJTcCzTItrop8mq/lbzL8wSGtj94UO/3U31shqG0=
|
||||
go.etcd.io/bbolt v1.3.10/go.mod h1:bK3UQLPJZly7IlNmV7uVHJDxfe5aK9Ll93e/74Y9oEQ=
|
||||
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.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=
|
||||
@@ -1069,12 +1158,32 @@ 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.25.0 h1:4Hvk6GtkucQ790dqmj7l1eEnRdKm3k3ZUrUMS2d5+5c=
|
||||
go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk=
|
||||
go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
|
||||
go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
|
||||
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=
|
||||
@@ -1096,6 +1205,7 @@ 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=
|
||||
@@ -1104,8 +1214,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.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM=
|
||||
golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U=
|
||||
golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q=
|
||||
golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4=
|
||||
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=
|
||||
@@ -1116,8 +1226,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-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/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/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=
|
||||
@@ -1143,8 +1253,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.25.0 h1:n7a+ZbQKQA/Ysbyb0/6IbB1H/X41mKgbhfv7AfG/44w=
|
||||
golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA=
|
||||
golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w=
|
||||
golang.org/x/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=
|
||||
@@ -1195,8 +1305,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.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY=
|
||||
golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU=
|
||||
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=
|
||||
@@ -1225,8 +1335,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.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8=
|
||||
golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||
golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I=
|
||||
golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI=
|
||||
golang.org/x/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=
|
||||
@@ -1243,6 +1353,7 @@ 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=
|
||||
@@ -1285,14 +1396,13 @@ 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.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc=
|
||||
golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||
golang.org/x/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=
|
||||
@@ -1306,8 +1416,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.32.0 h1:DR4lr0TjUs3epypdhTOkMmuF5CDFJ/8pOnbzMZPQ7bg=
|
||||
golang.org/x/term v0.32.0/go.mod h1:uZG1FhGx848Sqfsq4/DlJr3xGGsYMu/L5GW4abiaEPQ=
|
||||
golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU=
|
||||
golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254=
|
||||
golang.org/x/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=
|
||||
@@ -1324,13 +1434,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.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM=
|
||||
golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM=
|
||||
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.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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/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=
|
||||
@@ -1379,8 +1489,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.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo=
|
||||
golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg=
|
||||
golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ=
|
||||
golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs=
|
||||
golang.org/x/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=
|
||||
@@ -1437,6 +1547,11 @@ 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=
|
||||
@@ -1449,6 +1564,8 @@ 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=
|
||||
@@ -1465,8 +1582,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.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
|
||||
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
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=
|
||||
@@ -1496,8 +1613,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.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
|
||||
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
|
||||
gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=
|
||||
gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=
|
||||
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=
|
||||
|
||||
@@ -0,0 +1,38 @@
|
||||
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"
|
||||
@@ -0,0 +1,27 @@
|
||||
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"
|
||||
@@ -0,0 +1,25 @@
|
||||
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
|
||||
@@ -0,0 +1,27 @@
|
||||
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
|
||||
@@ -0,0 +1,30 @@
|
||||
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"
|
||||
@@ -0,0 +1,38 @@
|
||||
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"
|
||||
@@ -0,0 +1,28 @@
|
||||
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'
|
||||
@@ -0,0 +1,38 @@
|
||||
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,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 verfication 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 verification functionality introduced in nuclei v3
|
||||
+10
-10
@@ -19,7 +19,7 @@ import (
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
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, errorutil.NewWithErr(err).Msgf("could not create output writer")
|
||||
return nil, errkit.Wrap(err, "could not create output writer")
|
||||
}
|
||||
tmp, err := urlutil.Parse(creds.Server)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not parse server url")
|
||||
return nil, errkit.Wrap(err, "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) {
|
||||
return fmt.Errorf("invalid scan id provided")
|
||||
gologger.Warning().Msgf("invalid asset id provided (unknown xid format): %s", id)
|
||||
}
|
||||
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 errorutil.NewWithErr(err).Msgf("could not upload chunk")
|
||||
return errkit.Wrap(err, "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 errorutil.NewWithErr(err).Msgf("could not create upload request")
|
||||
return errkit.Wrap(err, "could not create upload request")
|
||||
}
|
||||
resp, err := u.client.Do(req)
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("could not upload results")
|
||||
return errkit.Wrap(err, "could not upload results")
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
bin, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("could not get id from response")
|
||||
return errkit.Wrap(err, "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 errorutil.NewWithErr(err).Msgf("could not unmarshal response got %v", string(bin))
|
||||
return errkit.Wrap(err, fmt.Sprintf("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, errorutil.NewWithErr(err).Msgf("could not create cloud upload request")
|
||||
return nil, errkit.Wrap(err, "could not create cloud upload request")
|
||||
}
|
||||
// add pdtm meta params
|
||||
req.Params.Merge(updateutils.GetpdtmParams(config.Version))
|
||||
|
||||
@@ -7,6 +7,7 @@ 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"
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
type AuthLazyFetchOptions struct {
|
||||
@@ -32,7 +33,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, errorutil.NewWithErr(err).Msgf("failed to get template paths from secrets file")
|
||||
return nil, errkit.Wrap(err, "failed to get template paths from secrets file")
|
||||
}
|
||||
tmpls = append(tmpls, data...)
|
||||
}
|
||||
@@ -58,7 +59,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, errorutil.NewWithErr(err).Msgf("failed to initialize dynamic auth templates store")
|
||||
return nil, errkit.Wrap(err, "failed to initialize dynamic auth templates store")
|
||||
}
|
||||
return store, nil
|
||||
}
|
||||
@@ -68,7 +69,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("no templates found for path: %s", d.TemplatePath)
|
||||
return fmt.Errorf("%w for path: %s", disk.ErrNoTemplatesFound, d.TemplatePath)
|
||||
}
|
||||
if len(tmpls) > 1 {
|
||||
return fmt.Errorf("multiple templates found for path: %s", d.TemplatePath)
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
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 errorutil.WrapfWithNil(err, "failed to parse proxy got %v", err)
|
||||
return errkit.Wrapf(err, "failed to parse proxy got %v", err)
|
||||
}
|
||||
if options.ProxyInternal {
|
||||
_ = os.Setenv(HTTP_PROXY_ENV, proxyURL.String())
|
||||
|
||||
+22
-20
@@ -125,14 +125,6 @@ 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 {
|
||||
@@ -254,8 +246,23 @@ 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})
|
||||
inputProvider, err := provider.NewInputProvider(provider.InputOptions{Options: options, TempDir: runner.tmpDir})
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not create input provider")
|
||||
}
|
||||
@@ -384,16 +391,10 @@ func New(options *types.Options) (*Runner, error) {
|
||||
if options.RateLimit > 0 && options.RateLimitDuration == 0 {
|
||||
options.RateLimitDuration = time.Second
|
||||
}
|
||||
if options.RateLimit == 0 && options.RateLimitDuration == 0 {
|
||||
runner.rateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
} else {
|
||||
runner.rateLimiter = ratelimit.New(context.Background(), uint(options.RateLimit), options.RateLimitDuration)
|
||||
}
|
||||
|
||||
if tmpDir, err := os.MkdirTemp("", "nuclei-tmp-*"); err == nil {
|
||||
runner.tmpDir = tmpDir
|
||||
}
|
||||
runner.rateLimiter = utils.GetRateLimiter(context.Background(), options.RateLimit, options.RateLimitDuration)
|
||||
|
||||
// Initialization successful, disable cleanup on error
|
||||
cleanupOnError = false
|
||||
return runner, nil
|
||||
}
|
||||
|
||||
@@ -576,7 +577,9 @@ func (r *Runner) RunEnumeration() error {
|
||||
}
|
||||
|
||||
if len(r.options.SecretsFile) > 0 && !r.options.Validate {
|
||||
authTmplStore, err := GetAuthTmplStore(r.options, r.catalog, executorOpts)
|
||||
// Clone options so GetAuthTmplStore can modify them without affecting the original
|
||||
authOptions := r.options.Copy()
|
||||
authTmplStore, err := GetAuthTmplStore(authOptions, r.catalog, executorOpts)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "failed to load dynamic auth templates")
|
||||
}
|
||||
@@ -659,7 +662,6 @@ 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
|
||||
|
||||
@@ -44,7 +44,13 @@ func (r *Runner) listAvailableStoreTemplates(store *loader.Store) {
|
||||
config.DefaultConfig.TemplateVersion,
|
||||
config.DefaultConfig.TemplatesDirectory,
|
||||
)
|
||||
for _, tpl := range store.Templates() {
|
||||
// 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 {
|
||||
if hasExtraFlags(r.options) {
|
||||
if r.options.TemplateDisplay {
|
||||
colorize := !r.options.NoColor
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+32
-22
@@ -3,11 +3,13 @@ package nuclei
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/goflags"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
@@ -17,7 +19,6 @@ 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"
|
||||
)
|
||||
@@ -102,7 +103,7 @@ type InteractshOpts interactsh.Options
|
||||
func WithInteractshOptions(opts InteractshOpts) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("WithInteractshOptions")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithInteractshOptions")
|
||||
}
|
||||
optsPtr := &opts
|
||||
e.interactshOpts = (*interactsh.Options)(optsPtr)
|
||||
@@ -180,7 +181,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 = ratelimit.New(ctx, uint(e.opts.RateLimit), e.opts.RateLimitDuration)
|
||||
e.rateLimiter = utils.GetRateLimiter(ctx, e.opts.RateLimit, e.opts.RateLimitDuration)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -194,8 +195,10 @@ type HeadlessOpts struct {
|
||||
}
|
||||
|
||||
// EnableHeadless allows execution of headless templates
|
||||
// *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
|
||||
//
|
||||
// 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.
|
||||
func EnableHeadlessWithOpts(hopts *HeadlessOpts) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.Headless = true
|
||||
@@ -205,14 +208,6 @@ 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
|
||||
}
|
||||
}
|
||||
@@ -229,7 +224,7 @@ type StatsOptions struct {
|
||||
func EnableStatsWithOpts(opts StatsOptions) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("EnableStatsWithOpts")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "EnableStatsWithOpts")
|
||||
}
|
||||
if opts.Interval == 0 {
|
||||
opts.Interval = 5 //sec
|
||||
@@ -257,7 +252,7 @@ type VerbosityOptions struct {
|
||||
func WithVerbosity(opts VerbosityOptions) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("WithVerbosity")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithVerbosity")
|
||||
}
|
||||
e.opts.Verbose = opts.Verbose
|
||||
e.opts.Silent = opts.Silent
|
||||
@@ -290,7 +285,7 @@ type NetworkConfig struct {
|
||||
func WithNetworkConfig(opts NetworkConfig) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("WithNetworkConfig")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithNetworkConfig")
|
||||
}
|
||||
e.opts.NoHostErrors = opts.DisableMaxHostErr
|
||||
e.opts.MaxHostError = opts.MaxHostError
|
||||
@@ -321,7 +316,7 @@ func WithNetworkConfig(opts NetworkConfig) NucleiSDKOptions {
|
||||
func WithProxy(proxy []string, proxyInternalRequests bool) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("WithProxy")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithProxy")
|
||||
}
|
||||
e.opts.Proxy = proxy
|
||||
e.opts.ProxyInternal = proxyInternalRequests
|
||||
@@ -346,7 +341,7 @@ type OutputWriter output.Writer
|
||||
func UseOutputWriter(writer OutputWriter) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("UseOutputWriter")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "UseOutputWriter")
|
||||
}
|
||||
e.customWriter = writer
|
||||
return nil
|
||||
@@ -361,7 +356,7 @@ type StatsWriter progress.Progress
|
||||
func UseStatsWriter(writer StatsWriter) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("UseStatsWriter")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "UseStatsWriter")
|
||||
}
|
||||
e.customProgress = writer
|
||||
return nil
|
||||
@@ -375,7 +370,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 ErrOptionsNotSupported.Msgf("WithTemplateUpdateCallback")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithTemplateUpdateCallback")
|
||||
}
|
||||
e.disableTemplatesAutoUpgrade = disableTemplatesAutoUpgrade
|
||||
e.onUpdateAvailableCallback = callback
|
||||
@@ -387,7 +382,7 @@ func WithTemplateUpdateCallback(disableTemplatesAutoUpgrade bool, callback func(
|
||||
func WithSandboxOptions(allowLocalFileAccess bool, restrictLocalNetworkAccess bool) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
if e.mode == threadSafe {
|
||||
return ErrOptionsNotSupported.Msgf("WithSandboxOptions")
|
||||
return errkit.Wrap(ErrOptionsNotSupported, "WithSandboxOptions")
|
||||
}
|
||||
e.opts.AllowLocalFileAccess = allowLocalFileAccess
|
||||
e.opts.RestrictLocalNetworkAccess = restrictLocalNetworkAccess
|
||||
@@ -558,3 +553,18 @@ 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,5 +1,4 @@
|
||||
//go:build !race
|
||||
// +build !race
|
||||
|
||||
package nuclei_test
|
||||
|
||||
|
||||
+11
-13
@@ -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/ratelimit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
@@ -53,11 +53,7 @@ func createEphemeralObjects(ctx context.Context, base *NucleiEngine, opts *types
|
||||
if opts.RateLimit > 0 && opts.RateLimitDuration == 0 {
|
||||
opts.RateLimitDuration = time.Second
|
||||
}
|
||||
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.executerOpts.RateLimiter = utils.GetRateLimiter(ctx, opts.RateLimit, opts.RateLimitDuration)
|
||||
u.engine = core.New(opts)
|
||||
u.engine.SetExecuterOptions(u.executerOpts)
|
||||
return u, nil
|
||||
@@ -91,10 +87,12 @@ 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,
|
||||
opts: defaultOptions,
|
||||
mode: threadSafe,
|
||||
ctx: ctx,
|
||||
Logger: defaultOptions.Logger,
|
||||
}
|
||||
for _, option := range opts {
|
||||
if err := option(e); err != nil {
|
||||
@@ -124,7 +122,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 runned parallelly
|
||||
// This method can be called concurrently and it will use some global resources but can be run 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 {
|
||||
@@ -147,13 +145,13 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOptsCtx(ctx context.Context, t
|
||||
// load templates
|
||||
workflowLoader, err := workflow.NewLoader(unsafeOpts.executerOpts)
|
||||
if err != nil {
|
||||
return errorutil.New("Could not create workflow loader: %s\n", err)
|
||||
return errkit.Wrapf(err, "Could not create workflow loader: %s", err)
|
||||
}
|
||||
unsafeOpts.executerOpts.WorkflowLoader = workflowLoader
|
||||
|
||||
store, err := loader.New(loader.NewConfig(tmpEngine.opts, e.eng.catalog, unsafeOpts.executerOpts))
|
||||
if err != nil {
|
||||
return errorutil.New("Could not create loader client: %s\n", err)
|
||||
return errkit.Wrapf(err, "Could not create loader client: %s", err)
|
||||
}
|
||||
store.Load()
|
||||
|
||||
|
||||
+19
-10
@@ -5,6 +5,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"os"
|
||||
"sync"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
@@ -28,7 +29,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"github.com/rs/xid"
|
||||
)
|
||||
|
||||
@@ -37,13 +38,13 @@ type NucleiSDKOptions func(e *NucleiEngine) error
|
||||
|
||||
var (
|
||||
// ErrNotImplemented is returned when a feature is not implemented
|
||||
ErrNotImplemented = errorutil.New("Not implemented")
|
||||
ErrNotImplemented = errkit.New("Not implemented")
|
||||
// ErrNoTemplatesAvailable is returned when no templates are available to execute
|
||||
ErrNoTemplatesAvailable = errorutil.New("No templates available")
|
||||
ErrNoTemplatesAvailable = errkit.New("No templates available")
|
||||
// ErrNoTargetsAvailable is returned when no targets are available to scan
|
||||
ErrNoTargetsAvailable = errorutil.New("No targets available")
|
||||
ErrNoTargetsAvailable = errkit.New("No targets available")
|
||||
// ErrOptionsNotSupported is returned when an option is not supported in thread safe mode
|
||||
ErrOptionsNotSupported = errorutil.NewWithFmt("Option %v not supported in thread safe mode")
|
||||
ErrOptionsNotSupported = errkit.New("Option not supported in thread safe mode")
|
||||
)
|
||||
|
||||
type engineMode uint
|
||||
@@ -92,19 +93,22 @@ 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 errorutil.New("Could not create workflow loader: %s\n", err)
|
||||
return errkit.Wrapf(err, "Could not create workflow loader: %s", err)
|
||||
}
|
||||
e.executerOpts.WorkflowLoader = workflowLoader
|
||||
|
||||
e.store, err = loader.New(loader.NewConfig(e.opts, e.catalog, e.executerOpts))
|
||||
if err != nil {
|
||||
return errorutil.New("Could not create loader client: %s\n", err)
|
||||
return errkit.Wrapf(err, "Could not create loader client: %s", err)
|
||||
}
|
||||
e.store.Load()
|
||||
e.templatesLoaded = true
|
||||
@@ -231,6 +235,9 @@ func (e *NucleiEngine) closeInternal() {
|
||||
if e.httpxClient != nil {
|
||||
_ = e.httpxClient.Close()
|
||||
}
|
||||
if e.tmpDir != "" {
|
||||
_ = os.RemoveAll(e.tmpDir)
|
||||
}
|
||||
}
|
||||
|
||||
// Close all resources used by nuclei engine
|
||||
@@ -313,10 +320,12 @@ 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,
|
||||
opts: defaultOptions,
|
||||
mode: singleInstance,
|
||||
ctx: ctx,
|
||||
Logger: defaultOptions.Logger,
|
||||
}
|
||||
for _, option := range options {
|
||||
if err := option(e); err != nil {
|
||||
|
||||
+38
-19
@@ -3,6 +3,7 @@ package nuclei
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -12,7 +13,6 @@ 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,6 +29,7 @@ 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"
|
||||
@@ -96,13 +97,11 @@ func (e *NucleiEngine) applyRequiredDefaults(ctx context.Context) {
|
||||
|
||||
// init
|
||||
func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
// Set a default logger if one isn't provided in the options
|
||||
if e.opts.Logger != nil {
|
||||
// Update logger ref (if it was changed by [WithLogger])
|
||||
// (Logger is already initialized)
|
||||
if e.opts.Logger != e.Logger {
|
||||
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)
|
||||
@@ -166,24 +165,44 @@ 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(),
|
||||
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(),
|
||||
TemporaryDirectory: e.tmpDir,
|
||||
Logger: e.opts.Logger,
|
||||
}
|
||||
if e.opts.ShouldUseHostError() && e.hostErrCache != nil {
|
||||
e.executerOpts.HostErrorsCache = e.hostErrCache
|
||||
|
||||
@@ -35,3 +35,23 @@ 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()
|
||||
}
|
||||
@@ -59,6 +59,11 @@
|
||||
"title": "engine",
|
||||
"description": "Engine"
|
||||
},
|
||||
"sandbox": {
|
||||
"$ref": "#/$defs/code.Sandbox",
|
||||
"title": "sandbox",
|
||||
"description": "Sandbox"
|
||||
},
|
||||
"pre-condition": {
|
||||
"type": "string",
|
||||
"title": "pre-condition for the request",
|
||||
@@ -86,6 +91,22 @@
|
||||
"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": [
|
||||
|
||||
@@ -3,12 +3,12 @@ package authx
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/replacer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
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:"-"`
|
||||
m *sync.Mutex `json:"-" yaml:"-"` // mutex for lazy fetch
|
||||
fetched bool `json:"-" yaml:"-"` // flag to check if the secret has been fetched
|
||||
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
|
||||
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 errorutil.New("cannot unmarshal into nil Dynamic struct")
|
||||
return errkit.New("cannot unmarshal into nil Dynamic struct")
|
||||
}
|
||||
|
||||
// Use an alias type (auxiliary) to avoid a recursive call in this method.
|
||||
@@ -70,12 +70,13 @@ func (d *Dynamic) UnmarshalJSON(data []byte) error {
|
||||
|
||||
// Validate validates the dynamic secret
|
||||
func (d *Dynamic) Validate() error {
|
||||
d.m = &sync.Mutex{}
|
||||
d.fetched = &atomic.Bool{}
|
||||
d.fetching = &atomic.Bool{}
|
||||
if d.TemplatePath == "" {
|
||||
return errorutil.New(" template-path is required for dynamic secret")
|
||||
return errkit.New(" template-path is required for dynamic secret")
|
||||
}
|
||||
if len(d.Variables) == 0 {
|
||||
return errorutil.New("variables are required for dynamic secret")
|
||||
return errkit.New("variables are required for dynamic secret")
|
||||
}
|
||||
|
||||
if d.Secret != nil {
|
||||
@@ -97,9 +98,7 @@ 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 {
|
||||
@@ -184,9 +183,15 @@ func (d *Dynamic) applyValuesToSecret(secret *Secret) error {
|
||||
|
||||
// GetStrategy returns the auth strategies for the dynamic secret
|
||||
func (d *Dynamic) GetStrategies() []AuthStrategy {
|
||||
if !d.fetched {
|
||||
if d.fetched.Load() {
|
||||
if d.error != nil {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
// Try to fetch if not already fetched
|
||||
_ = d.Fetch(true)
|
||||
}
|
||||
|
||||
if d.error != nil {
|
||||
return nil
|
||||
}
|
||||
@@ -203,12 +208,23 @@ 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 {
|
||||
d.m.Lock()
|
||||
defer d.m.Unlock()
|
||||
if d.fetched {
|
||||
return nil
|
||||
if d.fetched.Load() {
|
||||
return d.error
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
"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 []KV `json:"headers" yaml:"headers"` // Headers preserve exact casing (useful for case-sensitive APIs)
|
||||
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"`
|
||||
Key string `json:"key" yaml:"key"` // Header key (preserves exact casing)
|
||||
Value string `json:"value" yaml:"value"`
|
||||
}
|
||||
|
||||
@@ -237,7 +237,9 @@ func GetAuthDataFromYAML(data []byte) (*Authx, error) {
|
||||
var auth Authx
|
||||
err := yaml.Unmarshal(data, &auth)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal yaml")
|
||||
errorErr := errkit.FromError(err)
|
||||
errorErr.Msgf("could not unmarshal yaml")
|
||||
return nil, errorErr
|
||||
}
|
||||
return &auth, nil
|
||||
}
|
||||
@@ -247,7 +249,9 @@ func GetAuthDataFromJSON(data []byte) (*Authx, error) {
|
||||
var auth Authx
|
||||
err := json.Unmarshal(data, &auth)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not unmarshal json")
|
||||
errorErr := errkit.FromError(err)
|
||||
errorErr.Msgf("could not unmarshal json")
|
||||
return nil, errorErr
|
||||
}
|
||||
return &auth, nil
|
||||
}
|
||||
@@ -21,15 +21,19 @@ 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.Set(header.Key, header.Value)
|
||||
req.Header[header.Key] = []string{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.Set(header.Key, header.Value)
|
||||
req.Header[header.Key] = []string{header.Value}
|
||||
}
|
||||
}
|
||||
@@ -12,6 +12,8 @@ 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
|
||||
@@ -20,6 +22,8 @@ static:
|
||||
headers:
|
||||
- key: x-pdcp-key
|
||||
value: <api-key-here>
|
||||
- key: barAuthToken
|
||||
value: <auth-token-here>
|
||||
|
||||
# for query based auth session
|
||||
- type: Query
|
||||
|
||||
@@ -7,7 +7,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider/authx"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
@@ -30,16 +30,20 @@ func NewFileAuthProvider(path string, callback authx.LazyFetchSecret) (AuthProvi
|
||||
return nil, ErrNoSecrets
|
||||
}
|
||||
if len(store.Dynamic) > 0 && callback == nil {
|
||||
return nil, errorutil.New("lazy fetch callback is required for dynamic secrets")
|
||||
return nil, errkit.New("lazy fetch callback is required for dynamic secrets")
|
||||
}
|
||||
for _, secret := range store.Secrets {
|
||||
if err := secret.Validate(); err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("invalid secret in file: %s", path)
|
||||
errorErr := errkit.FromError(err)
|
||||
errorErr.Msgf("invalid secret in file: %s", path)
|
||||
return nil, errorErr
|
||||
}
|
||||
}
|
||||
for i, dynamic := range store.Dynamic {
|
||||
if err := dynamic.Validate(); err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("invalid dynamic in file: %s", path)
|
||||
errorErr := errkit.FromError(err)
|
||||
errorErr.Msgf("invalid dynamic in file: %s", path)
|
||||
return nil, errorErr
|
||||
}
|
||||
dynamic.SetLazyFetchCallback(callback)
|
||||
store.Dynamic[i] = dynamic
|
||||
|
||||
@@ -31,7 +31,7 @@ const (
|
||||
CLIConfigFileName = "config.yaml"
|
||||
ReportingConfigFilename = "reporting-config.yaml"
|
||||
// Version is the current version of nuclei
|
||||
Version = `v3.4.8`
|
||||
Version = `v3.6.1`
|
||||
// Directory Names of custom templates
|
||||
CustomS3TemplatesDirName = "s3"
|
||||
CustomGitHubTemplatesDirName = "github"
|
||||
@@ -40,6 +40,7 @@ const (
|
||||
BinaryName = "nuclei"
|
||||
FallbackConfigFolderName = ".nuclei-config"
|
||||
NucleiConfigDirEnv = "NUCLEI_CONFIG_DIR"
|
||||
NucleiTemplatesDirEnv = "NUCLEI_TEMPLATES_DIR"
|
||||
)
|
||||
|
||||
// IsOutdatedVersion compares two versions and returns true
|
||||
|
||||
@@ -2,7 +2,6 @@ package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"runtime/debug"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"gopkg.in/yaml.v2"
|
||||
@@ -18,7 +17,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%s\n", err, string(debug.Stack()))
|
||||
gologger.Error().Msgf("Could not read nuclei-ignore file: %s\n", err)
|
||||
return IgnoreFile{}
|
||||
}
|
||||
defer func() {
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/json"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
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 errorutil.NewWithErr(err).Msgf("could not read nuclei ignore file")
|
||||
return errkit.Newf("could not read nuclei ignore file: %v", err)
|
||||
}
|
||||
c.NucleiIgnoreHash = fmt.Sprintf("%x", md5.Sum(bin))
|
||||
// write config to disk
|
||||
return c.WriteTemplatesConfig()
|
||||
}
|
||||
return errorutil.NewWithTag("config", "ignore file not found: could not update nuclei ignore hash")
|
||||
return errkit.New("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.Fields(string(bin)) {
|
||||
if IsTemplate(v) {
|
||||
for v := range strings.FieldsSeq(string(bin)) {
|
||||
if IsTemplateWithRoot(v, c.TemplatesDirectory) {
|
||||
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 errorutil.NewWithErr(err).Msgf("could not write nuclei config file at %s", c.getTemplatesConfigFilePath())
|
||||
return errkit.Newf("could not write nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
|
||||
}
|
||||
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 errorutil.NewWithTag("config", "nuclei config file at %s does not exist", c.getTemplatesConfigFilePath())
|
||||
return errkit.Newf("nuclei config file at %s does not exist", c.getTemplatesConfigFilePath())
|
||||
}
|
||||
var cfg *Config
|
||||
bin, err := os.ReadFile(c.getTemplatesConfigFilePath())
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("could not read nuclei config file at %s", c.getTemplatesConfigFilePath())
|
||||
return errkit.Newf("could not read nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
|
||||
}
|
||||
if err := json.Unmarshal(bin, &cfg); err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("could not unmarshal nuclei config file at %s", c.getTemplatesConfigFilePath())
|
||||
return errkit.Newf("could not unmarshal nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
|
||||
}
|
||||
// apply config
|
||||
c.TemplatesDirectory = cfg.TemplatesDirectory
|
||||
@@ -292,10 +292,10 @@ func (c *Config) WriteTemplatesConfig() error {
|
||||
}
|
||||
bin, err := json.Marshal(c)
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to marshal nuclei config")
|
||||
return errkit.Newf("failed to marshal nuclei config: %v", err)
|
||||
}
|
||||
if err = os.WriteFile(c.getTemplatesConfigFilePath(), bin, 0600); err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to write nuclei config file at %s", c.getTemplatesConfigFilePath())
|
||||
return errkit.Newf("failed to write nuclei config file at %s: %v", c.getTemplatesConfigFilePath(), err)
|
||||
}
|
||||
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 errorutil.NewWithErr(err).Msgf("could not create nuclei config directory at %s", c.configDir)
|
||||
return errkit.Newf("could not create nuclei config directory at %s: %v", c.configDir, err)
|
||||
}
|
||||
}
|
||||
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 seperator instead of commas
|
||||
// use space as separator instead of commas
|
||||
tmp := strings.Fields(data)
|
||||
for _, v := range tmp {
|
||||
key := v
|
||||
|
||||
@@ -12,7 +12,10 @@ import (
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
var knownConfigFiles = []string{"cves.json", "contributors.json", "TEMPLATES-STATS.json"}
|
||||
var (
|
||||
knownConfigFiles = []string{"cves.json", "contributors.json", "TEMPLATES-STATS.json"}
|
||||
knownMiscDirectories = []string{".git", ".github", "helpers"}
|
||||
)
|
||||
|
||||
// TemplateFormat
|
||||
type TemplateFormat uint8
|
||||
@@ -23,6 +26,25 @@ 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))
|
||||
@@ -41,13 +63,54 @@ func GetSupportTemplateFileExtensions() []string {
|
||||
return []string{extensions.YAML, extensions.JSON}
|
||||
}
|
||||
|
||||
// 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...) {
|
||||
// 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()...) {
|
||||
return false
|
||||
}
|
||||
return stringsutil.EqualFoldAny(filepath.Ext(filename), GetSupportTemplateFileExtensions()...)
|
||||
|
||||
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()...)
|
||||
}
|
||||
|
||||
type template struct {
|
||||
@@ -91,11 +154,21 @@ func GetNucleiTemplatesIndex() (map[string]string, error) {
|
||||
if err == nil {
|
||||
for _, v := range records {
|
||||
if len(v) >= 2 {
|
||||
index[v[0]] = v[1]
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
@@ -111,16 +184,18 @@ func GetNucleiTemplatesIndex() (map[string]string, error) {
|
||||
DefaultConfig.Logger.Verbose().Msgf("failed to walk path=%v err=%v", path, err)
|
||||
return nil
|
||||
}
|
||||
if d.IsDir() || !IsTemplate(path) || stringsutil.ContainsAny(path, ignoreDirs...) {
|
||||
if d.IsDir() || !IsTemplateWithRoot(path, DefaultConfig.TemplatesDirectory) || 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(path)
|
||||
id, err := getTemplateID(normalizedPath)
|
||||
if err != nil || id == "" {
|
||||
DefaultConfig.Logger.Verbose().Msgf("failed to get template id from file=%v got id=%v err=%v", path, id, err)
|
||||
DefaultConfig.Logger.Verbose().Msgf("failed to get template id from file=%v got id=%v err=%v", normalizedPath, id, err)
|
||||
return nil
|
||||
}
|
||||
index[id] = path
|
||||
index[id] = normalizedPath
|
||||
return nil
|
||||
})
|
||||
return index, err
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -38,13 +38,7 @@ 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 {
|
||||
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 os.Open(filename)
|
||||
}
|
||||
|
||||
return d.templatesFS.Open(filename)
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package disk
|
||||
|
||||
import "errors"
|
||||
|
||||
var (
|
||||
ErrNoTemplatesFound = errors.New("no templates found")
|
||||
)
|
||||
+48
-91
@@ -1,6 +1,7 @@
|
||||
package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -65,41 +66,34 @@ 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
|
||||
absPath, err = c.convertPathToAbsolute(absPath)
|
||||
target, err = c.convertPathToAbsolute(target)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not find template file")
|
||||
}
|
||||
}
|
||||
|
||||
// Template input is either a file or a directory
|
||||
match, file, err := c.findFileMatches(absPath, processed)
|
||||
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)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not find file")
|
||||
}
|
||||
|
||||
if file {
|
||||
if match != "" {
|
||||
return []string{match}, nil
|
||||
@@ -109,13 +103,15 @@ 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(absPath, processed)
|
||||
matches, err := c.findDirectoryMatches(target, processed)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not find directory matches")
|
||||
}
|
||||
|
||||
if len(matches) == 0 {
|
||||
return nil, errors.Errorf("no templates found in path %s", absPath)
|
||||
return nil, fmt.Errorf("%w in path %q", ErrNoTemplatesFound, target)
|
||||
}
|
||||
|
||||
return matches, nil
|
||||
}
|
||||
|
||||
@@ -135,72 +131,29 @@ 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
|
||||
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 matched []string
|
||||
var matches []string
|
||||
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)
|
||||
}
|
||||
if c.templatesFS != nil {
|
||||
// fs.FS always uses forward slashes
|
||||
relPath = strings.TrimPrefix(relPath, "/")
|
||||
} else {
|
||||
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)
|
||||
}
|
||||
relPath = strings.TrimPrefix(relPath, string(os.PathSeparator))
|
||||
}
|
||||
|
||||
var err error
|
||||
var matches []string
|
||||
|
||||
if c.templatesFS != nil {
|
||||
matches, err = fs.Glob(c.templatesFS, relPath)
|
||||
} else {
|
||||
matches, err = filepath.Glob(absPath)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
results := make([]string, 0, len(matches))
|
||||
for _, match := range matches {
|
||||
if _, ok := processed[match]; !ok {
|
||||
@@ -208,6 +161,7 @@ func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]s
|
||||
results = append(results, match)
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
@@ -257,7 +211,7 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
|
||||
if !d.IsDir() && config.IsTemplateWithRoot(path, absPath) {
|
||||
if _, ok := processed[path]; !ok {
|
||||
results = append(results, path)
|
||||
processed[path] = struct{}{}
|
||||
@@ -281,7 +235,7 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
|
||||
if !d.IsDir() && config.IsTemplateWithRoot(path, absPath) {
|
||||
if _, ok := processed[path]; !ok {
|
||||
results = append(results, path)
|
||||
processed[path] = struct{}{}
|
||||
@@ -294,8 +248,11 @@ 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
|
||||
// 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.
|
||||
func PrintDeprecatedPathsMsgIfApplicable(isSilent bool) {
|
||||
if !updateutils.IsOutdated("v9.4.3", config.DefaultConfig.TemplateVersion) {
|
||||
return
|
||||
|
||||
@@ -72,6 +72,9 @@ 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
|
||||
|
||||
@@ -0,0 +1,344 @@
|
||||
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, ", ") + ")"
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
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())
|
||||
}
|
||||
@@ -0,0 +1,352 @@
|
||||
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
|
||||
}
|
||||
@@ -0,0 +1,737 @@
|
||||
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)
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
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)
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
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, errorutil.New("Prompt is too short. Please provide a more descriptive prompt")
|
||||
return nil, errkit.Newf("Prompt is too short. Please provide a more descriptive prompt")
|
||||
}
|
||||
|
||||
if len(prompt) > 3000 {
|
||||
return nil, errorutil.New("Prompt is too long. Please limit to 3000 characters")
|
||||
return nil, errkit.Newf("Prompt is too long. Please limit to 3000 characters")
|
||||
}
|
||||
|
||||
template, templateID, err := generateAITemplate(prompt)
|
||||
if err != nil {
|
||||
return nil, errorutil.New("Failed to generate template: %v", err)
|
||||
return nil, errkit.Newf("Failed to generate template: %v", err)
|
||||
}
|
||||
|
||||
pdcpTemplateDir := filepath.Join(config.DefaultConfig.GetTemplateDir(), "pdcp")
|
||||
if err := os.MkdirAll(pdcpTemplateDir, 0755); err != nil {
|
||||
return nil, errorutil.New("Failed to create pdcp template directory: %v", err)
|
||||
return nil, errkit.Newf("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, errorutil.New("Failed to generate template: %v", err)
|
||||
return nil, errkit.Newf("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 "", "", errorutil.New("Failed to marshal request body: %v", err)
|
||||
return "", "", errkit.Newf("Failed to marshal request body: %v", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, aiTemplateGeneratorAPIEndpoint, bytes.NewBuffer(jsonBody))
|
||||
if err != nil {
|
||||
return "", "", errorutil.New("Failed to create HTTP request: %v", err)
|
||||
return "", "", errkit.Newf("Failed to create HTTP request: %v", err)
|
||||
}
|
||||
|
||||
ph := pdcpauth.PDCPCredHandler{}
|
||||
creds, err := ph.GetCreds()
|
||||
if err != nil {
|
||||
return "", "", errorutil.New("Failed to get PDCP credentials: %v", err)
|
||||
return "", "", errkit.Newf("Failed to get PDCP credentials: %v", err)
|
||||
}
|
||||
|
||||
if creds == nil {
|
||||
return "", "", errorutil.New("PDCP API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
|
||||
return "", "", errkit.Newf("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 "", "", errorutil.New("Failed to send HTTP request: %v", err)
|
||||
return "", "", errkit.Newf("Failed to send HTTP request: %v", err)
|
||||
}
|
||||
defer func() {
|
||||
_ = resp.Body.Close()
|
||||
}()
|
||||
|
||||
if resp.StatusCode == http.StatusUnauthorized {
|
||||
return "", "", errorutil.New("Invalid API Key or API Key not configured, Create one for free at https://cloud.projectdiscovery.io/")
|
||||
return "", "", errkit.Newf("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 "", "", errorutil.New("API returned status code %d: %s", resp.StatusCode, string(body))
|
||||
return "", "", errkit.Newf("API returned status code %d: %s", resp.StatusCode, string(body))
|
||||
}
|
||||
|
||||
var result AITemplateResponse
|
||||
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
|
||||
return "", "", errorutil.New("Failed to decode API response: %v", err)
|
||||
return "", "", errkit.Newf("Failed to decode API response: %v", err)
|
||||
}
|
||||
|
||||
if result.TemplateID == "" || result.Completion == "" {
|
||||
return "", "", errorutil.New("Failed to generate template")
|
||||
return "", "", errkit.Newf("Failed to generate template")
|
||||
}
|
||||
|
||||
return result.Completion, result.TemplateID, nil
|
||||
|
||||
+298
-78
@@ -7,13 +7,14 @@ 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/loader/filter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/index"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/keys"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
@@ -24,7 +25,8 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
syncutil "github.com/projectdiscovery/utils/sync"
|
||||
@@ -75,7 +77,6 @@ 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
|
||||
@@ -87,6 +88,19 @@ 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
|
||||
@@ -124,17 +138,10 @@ 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 {
|
||||
@@ -142,20 +149,43 @@ func New(cfg *Config) (*Store, error) {
|
||||
}
|
||||
|
||||
store := &Store{
|
||||
id: cfg.StoreId,
|
||||
config: cfg,
|
||||
tagFilter: tagFilter,
|
||||
pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{
|
||||
IncludedTemplates: cfg.IncludeTemplates,
|
||||
ExcludedTemplates: cfg.ExcludeTemplates,
|
||||
}, cfg.Catalog),
|
||||
id: cfg.StoreId,
|
||||
config: cfg,
|
||||
tagFilter: tagFilter,
|
||||
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 processs them separately and remove them from the initial list
|
||||
// we need to process 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
|
||||
@@ -175,6 +205,7 @@ func New(cfg *Config) (*Store, error) {
|
||||
templatesFinal = append(templatesFinal, v) // something went wrong, treat it as a file
|
||||
}
|
||||
}
|
||||
|
||||
cfg.TemplateURLs = remoteTemplates
|
||||
store.finalTemplates = templatesFinal
|
||||
|
||||
@@ -184,6 +215,7 @@ func New(cfg *Config) (*Store, error) {
|
||||
if err != nil {
|
||||
return store, err
|
||||
}
|
||||
|
||||
store.finalTemplates = append(store.finalTemplates, remoteTemplates...)
|
||||
store.finalWorkflows = append(store.finalWorkflows, remoteWorkflows...)
|
||||
}
|
||||
@@ -219,14 +251,18 @@ 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
|
||||
}
|
||||
|
||||
@@ -236,17 +272,21 @@ 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, errorutil.NewWithErr(err).Msgf("Could not load template %s: got %v", uri, remoteTemplates)
|
||||
return nil, errkit.Wrapf(err, "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)
|
||||
@@ -285,19 +325,106 @@ 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)
|
||||
|
||||
filteredTemplatePaths := store.pathFilter.Match(templatePaths)
|
||||
|
||||
indexFilter := store.indexFilter
|
||||
validPaths := make(map[string]struct{})
|
||||
for templatePath := range filteredTemplatePaths {
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, nil, store.config.Catalog)
|
||||
if loaded || store.pathFilter.MatchIncluded(templatePath) {
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
validPaths[templatePath] = struct{}{}
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
||||
stats.Increment(templates.TemplatesExcludedStats)
|
||||
@@ -306,43 +433,47 @@ func (store *Store) LoadTemplatesOnlyMetadata() error {
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
store.logger.Warning().Msg(err.Error())
|
||||
}
|
||||
}
|
||||
parserItem, ok := store.config.ExecutorOptions.Parser.(*templates.Parser)
|
||||
if !ok {
|
||||
|
||||
templatesCache := store.parserCacheOnce()
|
||||
if templatesCache == nil {
|
||||
return errors.New("invalid parser")
|
||||
}
|
||||
templatesCache := parserItem.Cache()
|
||||
|
||||
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
|
||||
|
||||
for templatePath := range validPaths {
|
||||
template, _, _ := templatesCache.Has(templatePath)
|
||||
|
||||
if len(template.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
if template == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
if len(template.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
if !isListOrDisplay && !template.IsEnabledFor(caps) {
|
||||
continue
|
||||
}
|
||||
|
||||
if template.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||
if loadedTemplateIDs.Has(template.ID) {
|
||||
store.logger.Debug().Msgf("Skipping duplicate template ID '%s' from path '%s'", template.ID, templatePath)
|
||||
continue
|
||||
}
|
||||
|
||||
if template.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
|
||||
continue
|
||||
}
|
||||
|
||||
if template.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
|
||||
continue
|
||||
}
|
||||
|
||||
if template != nil {
|
||||
template.Path = templatePath
|
||||
store.templates = append(store.templates, template)
|
||||
}
|
||||
_ = loadedTemplateIDs.Set(template.ID, struct{}{})
|
||||
template.Path = templatePath
|
||||
store.templates = append(store.templates, template)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -351,15 +482,24 @@ 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)
|
||||
|
||||
filteredTemplatePaths := store.pathFilter.Match(templatePaths)
|
||||
filteredWorkflowPaths := store.pathFilter.Match(workflowPaths)
|
||||
templatePathsMap := make(map[string]struct{}, len(templatePaths))
|
||||
for _, path := range templatePaths {
|
||||
templatePathsMap[path] = struct{}{}
|
||||
}
|
||||
|
||||
if store.areTemplatesValid(filteredTemplatePaths) && store.areWorkflowsValid(filteredWorkflowPaths) {
|
||||
workflowPathsMap := make(map[string]struct{}, len(workflowPaths))
|
||||
for _, path := range workflowPaths {
|
||||
workflowPathsMap[path] = struct{}{}
|
||||
}
|
||||
|
||||
if store.areTemplatesValid(templatePathsMap) && store.areWorkflowsValid(workflowPathsMap) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("errors occurred during template validation")
|
||||
}
|
||||
|
||||
@@ -377,6 +517,7 @@ 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 {
|
||||
@@ -386,13 +527,26 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
var template *templates.Template
|
||||
var err error
|
||||
|
||||
if parsedCache != nil {
|
||||
if cachedTemplate, _, cacheErr := parsedCache.Has(templatePath); cacheErr == nil && cachedTemplate != nil {
|
||||
template = cachedTemplate
|
||||
}
|
||||
} else if template == nil {
|
||||
}
|
||||
|
||||
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 {
|
||||
// NOTE(dwisiswant0): possibly global matchers template.
|
||||
// This could definitely be handled better, for example by returning an
|
||||
// `ErrGlobalMatchersTemplate` during `templates.Parse` and checking it
|
||||
@@ -415,10 +569,12 @@ 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 && len(template.Workflows) > 0 {
|
||||
|
||||
if !isWorkflow && template.HasWorkflows() {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
if isWorkflow {
|
||||
if !areWorkflowTemplatesValid(store, template.Workflows) {
|
||||
areTemplatesValid = false
|
||||
@@ -426,6 +582,7 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return areTemplatesValid
|
||||
}
|
||||
|
||||
@@ -434,6 +591,7 @@ 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) {
|
||||
@@ -441,6 +599,7 @@ func areWorkflowTemplatesValid(store *Store, workflows []*workflows.WorkflowTemp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -448,10 +607,13 @@ 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
|
||||
}
|
||||
|
||||
@@ -464,14 +626,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(workflowPathMap))
|
||||
for workflowPath := range workflowPathMap {
|
||||
loadedWorkflows := make([]*templates.Template, 0, len(includedWorkflows))
|
||||
for _, workflowPath := range includedWorkflows {
|
||||
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 {
|
||||
@@ -481,19 +643,31 @@ 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 {
|
||||
@@ -507,28 +681,66 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
}
|
||||
}
|
||||
|
||||
wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(50))
|
||||
typesOpts := store.config.ExecutorOptions.Options
|
||||
concurrency := typesOpts.TemplateLoadingConcurrency
|
||||
if concurrency <= 0 {
|
||||
concurrency = types.DefaultTemplateLoadingConcurrency
|
||||
}
|
||||
|
||||
wgLoadTemplates, errWg := syncutil.New(syncutil.WithSize(concurrency))
|
||||
if errWg != nil {
|
||||
panic("could not create wait group")
|
||||
}
|
||||
|
||||
if store.config.ExecutorOptions.Options.ExecutionId == "" {
|
||||
store.config.ExecutorOptions.Options.ExecutionId = xid.New().String()
|
||||
if typesOpts.ExecutionId == "" {
|
||||
typesOpts.ExecutionId = xid.New().String()
|
||||
}
|
||||
|
||||
dialers := protocolstate.GetDialersWithId(store.config.ExecutorOptions.Options.ExecutionId)
|
||||
dialers := protocolstate.GetDialersWithId(typesOpts.ExecutionId)
|
||||
if dialers == nil {
|
||||
panic("dialers with executionId " + store.config.ExecutorOptions.Options.ExecutionId + " not found")
|
||||
panic("dialers with executionId " + typesOpts.ExecutionId + " not found")
|
||||
}
|
||||
|
||||
for templatePath := range templatePathMap {
|
||||
for _, templatePath := range includedTemplates {
|
||||
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 || store.pathFilter.MatchIncluded(templatePath) {
|
||||
if loaded {
|
||||
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) {
|
||||
@@ -536,18 +748,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 && store.config.ExecutorOptions.Options.DisableUnsignedTemplates {
|
||||
if !parsed.Verified && typesOpts.DisableUnsignedTemplates {
|
||||
// skip unverified templates when prompted to
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed.SelfContained && !store.config.ExecutorOptions.Options.EnableSelfContainedTemplates {
|
||||
if parsed.SelfContained && !typesOpts.EnableSelfContainedTemplates {
|
||||
stats.Increment(templates.ExcludedSelfContainedStats)
|
||||
return
|
||||
}
|
||||
|
||||
if parsed.HasFileProtocol() && !store.config.ExecutorOptions.Options.EnableFileTemplates {
|
||||
if parsed.HasFileRequest() && !typesOpts.EnableFileTemplates {
|
||||
stats.Increment(templates.ExcludedFileStats)
|
||||
return
|
||||
}
|
||||
@@ -559,25 +771,32 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
}
|
||||
// DAST only templates
|
||||
// Skip DAST filter when loading auth templates
|
||||
if store.ID() != AuthStoreId && store.config.ExecutorOptions.Options.DAST {
|
||||
if store.ID() != AuthStoreId && typesOpts.DAST {
|
||||
// check if the template is a DAST template
|
||||
// also allow global matchers template to be loaded
|
||||
if parsed.IsFuzzing() || parsed.Options.GlobalMatchers != nil && parsed.Options.GlobalMatchers.HasMatchers() {
|
||||
loadTemplate(parsed)
|
||||
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)
|
||||
}
|
||||
}
|
||||
} else if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
} else if parsed.HasHeadlessRequest() && !typesOpts.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 len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
} else if parsed.HasCodeRequest() && !typesOpts.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 len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
||||
} else if parsed.HasCodeRequest() && !parsed.Verified && !parsed.HasWorkflows() {
|
||||
// donot include unverified 'Code' protocol custom template in final list
|
||||
stats.Increment(templates.SkippedCodeTmplTamperedStats)
|
||||
// these will be skipped so increment skip counter
|
||||
@@ -585,7 +804,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.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||
} else if parsed.IsFuzzableRequest() && !typesOpts.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)
|
||||
@@ -623,10 +842,11 @@ func IsHTTPBasedProtocolUsed(store *Store) bool {
|
||||
templates := append(store.Templates(), store.Workflows()...)
|
||||
|
||||
for _, template := range templates {
|
||||
if len(template.RequestsHTTP) > 0 || len(template.RequestsHeadless) > 0 {
|
||||
if template.HasHTTPRequest() || template.HasHeadlessRequest() {
|
||||
return true
|
||||
}
|
||||
if len(template.Workflows) > 0 {
|
||||
|
||||
if template.HasWorkflows() {
|
||||
if workflowContainsProtocol(template.Workflows) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
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()
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -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\": unexpect status %d", URL, response.StatusCode)}
|
||||
return RemoteContent{Error: fmt.Errorf("get \"%s\": unexpected status %d", URL, response.StatusCode)}
|
||||
}
|
||||
|
||||
scanner := bufio.NewScanner(response.Body)
|
||||
|
||||
@@ -109,7 +109,6 @@ func (e *Engine) executeTemplateSpray(ctx context.Context, templatesList []*temp
|
||||
defer wp.Wait()
|
||||
|
||||
for _, template := range templatesList {
|
||||
template := template
|
||||
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
|
||||
+73
-56
@@ -48,8 +48,15 @@ func (e *Engine) executeAllSelfContained(ctx context.Context, alltemplates []*te
|
||||
|
||||
// executeTemplateWithTargets executes a given template on x targets (with a internal targetpool(i.e concurrency))
|
||||
func (e *Engine) executeTemplateWithTargets(ctx context.Context, template *templates.Template, target provider.InputProvider, results *atomic.Bool) {
|
||||
// this is target pool i.e max target to execute
|
||||
wg := e.workPool.InputPool(template.Type())
|
||||
if e.workPool == nil {
|
||||
e.workPool = e.GetWorkPool()
|
||||
}
|
||||
// Bounded worker pool using input concurrency
|
||||
pool := e.workPool.InputPool(template.Type())
|
||||
workerCount := 1
|
||||
if pool != nil && pool.Size > 0 {
|
||||
workerCount = pool.Size
|
||||
}
|
||||
|
||||
var (
|
||||
index uint32
|
||||
@@ -78,6 +85,41 @@ func (e *Engine) executeTemplateWithTargets(ctx context.Context, template *templ
|
||||
currentInfo.Unlock()
|
||||
}
|
||||
|
||||
// task represents a single target execution unit
|
||||
type task struct {
|
||||
index uint32
|
||||
skip bool
|
||||
value *contextargs.MetaInput
|
||||
}
|
||||
|
||||
tasks := make(chan task)
|
||||
var workersWg sync.WaitGroup
|
||||
workersWg.Add(workerCount)
|
||||
for i := 0; i < workerCount; i++ {
|
||||
go func() {
|
||||
defer workersWg.Done()
|
||||
for t := range tasks {
|
||||
func() {
|
||||
defer cleanupInFlight(t.index)
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
default:
|
||||
}
|
||||
if t.skip {
|
||||
return
|
||||
}
|
||||
|
||||
match, err := e.executeTemplateOnInput(ctx, template, t.value)
|
||||
if err != nil {
|
||||
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), t.value.Input, err)
|
||||
}
|
||||
results.CompareAndSwap(false, match)
|
||||
}()
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
target.Iterate(func(scannedValue *contextargs.MetaInput) bool {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
@@ -128,43 +170,13 @@ func (e *Engine) executeTemplateWithTargets(ctx context.Context, template *templ
|
||||
return true
|
||||
}
|
||||
|
||||
wg.Add()
|
||||
go func(index uint32, skip bool, value *contextargs.MetaInput) {
|
||||
defer wg.Done()
|
||||
defer cleanupInFlight(index)
|
||||
if skip {
|
||||
return
|
||||
}
|
||||
|
||||
var match bool
|
||||
var err error
|
||||
ctxArgs := contextargs.New(ctx)
|
||||
ctxArgs.MetaInput = value
|
||||
ctx := scan.NewScanContext(ctx, ctxArgs)
|
||||
switch template.Type() {
|
||||
case types.WorkflowProtocol:
|
||||
match = e.executeWorkflow(ctx, template.CompiledWorkflow)
|
||||
default:
|
||||
if e.Callback != nil {
|
||||
if results, err := template.Executer.ExecuteWithResults(ctx); err == nil {
|
||||
for _, result := range results {
|
||||
e.Callback(result)
|
||||
}
|
||||
}
|
||||
match = true
|
||||
} else {
|
||||
match, err = template.Executer.Execute(ctx)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), value.Input, err)
|
||||
}
|
||||
results.CompareAndSwap(false, match)
|
||||
}(index, skip, scannedValue)
|
||||
tasks <- task{index: index, skip: skip, value: scannedValue}
|
||||
index++
|
||||
return true
|
||||
})
|
||||
wg.Wait()
|
||||
|
||||
close(tasks)
|
||||
workersWg.Wait()
|
||||
|
||||
// on completion marks the template as completed
|
||||
currentInfo.Lock()
|
||||
@@ -202,26 +214,7 @@ func (e *Engine) executeTemplatesOnTarget(ctx context.Context, alltemplates []*t
|
||||
go func(template *templates.Template, value *contextargs.MetaInput, wg *syncutil.AdaptiveWaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var match bool
|
||||
var err error
|
||||
ctxArgs := contextargs.New(ctx)
|
||||
ctxArgs.MetaInput = value
|
||||
ctx := scan.NewScanContext(ctx, ctxArgs)
|
||||
switch template.Type() {
|
||||
case types.WorkflowProtocol:
|
||||
match = e.executeWorkflow(ctx, template.CompiledWorkflow)
|
||||
default:
|
||||
if e.Callback != nil {
|
||||
if results, err := template.Executer.ExecuteWithResults(ctx); err == nil {
|
||||
for _, result := range results {
|
||||
e.Callback(result)
|
||||
}
|
||||
}
|
||||
match = true
|
||||
} else {
|
||||
match, err = template.Executer.Execute(ctx)
|
||||
}
|
||||
}
|
||||
match, err := e.executeTemplateOnInput(ctx, template, value)
|
||||
if err != nil {
|
||||
e.options.Logger.Warning().Msgf("[%s] Could not execute step on %s: %s\n", e.executerOpts.Colorizer.BrightBlue(template.ID), value.Input, err)
|
||||
}
|
||||
@@ -229,3 +222,27 @@ func (e *Engine) executeTemplatesOnTarget(ctx context.Context, alltemplates []*t
|
||||
}(tpl, target, sg)
|
||||
}
|
||||
}
|
||||
|
||||
// executeTemplateOnInput performs template execution for a single input and returns match status and error
|
||||
func (e *Engine) executeTemplateOnInput(ctx context.Context, template *templates.Template, value *contextargs.MetaInput) (bool, error) {
|
||||
ctxArgs := contextargs.New(ctx)
|
||||
ctxArgs.MetaInput = value
|
||||
scanCtx := scan.NewScanContext(ctx, ctxArgs)
|
||||
|
||||
switch template.Type() {
|
||||
case types.WorkflowProtocol:
|
||||
return e.executeWorkflow(scanCtx, template.CompiledWorkflow), nil
|
||||
default:
|
||||
if e.Callback != nil {
|
||||
results, err := template.Executer.ExecuteWithResults(scanCtx)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
for _, result := range results {
|
||||
e.Callback(result)
|
||||
}
|
||||
return len(results) > 0, nil
|
||||
}
|
||||
return template.Executer.Execute(scanCtx)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package core
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
inputtypes "github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
tmpltypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
// fakeExecuter is a simple stub for protocols.Executer used to test executeTemplateOnInput
|
||||
type fakeExecuter struct {
|
||||
withResults bool
|
||||
}
|
||||
|
||||
func (f *fakeExecuter) Compile() error { return nil }
|
||||
func (f *fakeExecuter) Requests() int { return 1 }
|
||||
func (f *fakeExecuter) Execute(ctx *scan.ScanContext) (bool, error) { return !f.withResults, nil }
|
||||
func (f *fakeExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) {
|
||||
if !f.withResults {
|
||||
return nil, nil
|
||||
}
|
||||
return []*output.ResultEvent{{Host: "h"}}, nil
|
||||
}
|
||||
|
||||
// newTestEngine creates a minimal Engine for tests
|
||||
func newTestEngine() *Engine {
|
||||
return New(&types.Options{})
|
||||
}
|
||||
|
||||
func Test_executeTemplateOnInput_CallbackPath(t *testing.T) {
|
||||
e := newTestEngine()
|
||||
called := 0
|
||||
e.Callback = func(*output.ResultEvent) { called++ }
|
||||
|
||||
tpl := &templates.Template{}
|
||||
tpl.Executer = &fakeExecuter{withResults: true}
|
||||
|
||||
ok, err := e.executeTemplateOnInput(context.Background(), tpl, &contextargs.MetaInput{Input: "x"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected match true")
|
||||
}
|
||||
if called == 0 {
|
||||
t.Fatalf("expected callback to be called")
|
||||
}
|
||||
}
|
||||
|
||||
func Test_executeTemplateOnInput_ExecutePath(t *testing.T) {
|
||||
e := newTestEngine()
|
||||
tpl := &templates.Template{}
|
||||
tpl.Executer = &fakeExecuter{withResults: false}
|
||||
|
||||
ok, err := e.executeTemplateOnInput(context.Background(), tpl, &contextargs.MetaInput{Input: "x"})
|
||||
if err != nil {
|
||||
t.Fatalf("unexpected error: %v", err)
|
||||
}
|
||||
if !ok {
|
||||
t.Fatalf("expected match true from Execute path")
|
||||
}
|
||||
}
|
||||
|
||||
type fakeExecuterErr struct{}
|
||||
|
||||
func (f *fakeExecuterErr) Compile() error { return nil }
|
||||
func (f *fakeExecuterErr) Requests() int { return 1 }
|
||||
func (f *fakeExecuterErr) Execute(ctx *scan.ScanContext) (bool, error) { return false, nil }
|
||||
func (f *fakeExecuterErr) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) {
|
||||
return nil, fmt.Errorf("boom")
|
||||
}
|
||||
|
||||
func Test_executeTemplateOnInput_CallbackErrorPropagates(t *testing.T) {
|
||||
e := newTestEngine()
|
||||
e.Callback = func(*output.ResultEvent) {}
|
||||
tpl := &templates.Template{}
|
||||
tpl.Executer = &fakeExecuterErr{}
|
||||
|
||||
ok, err := e.executeTemplateOnInput(context.Background(), tpl, &contextargs.MetaInput{Input: "x"})
|
||||
if err == nil {
|
||||
t.Fatalf("expected error to propagate")
|
||||
}
|
||||
if ok {
|
||||
t.Fatalf("expected match to be false on error")
|
||||
}
|
||||
}
|
||||
|
||||
type fakeTargetProvider struct {
|
||||
values []*contextargs.MetaInput
|
||||
}
|
||||
|
||||
func (f *fakeTargetProvider) Count() int64 { return int64(len(f.values)) }
|
||||
func (f *fakeTargetProvider) Iterate(cb func(value *contextargs.MetaInput) bool) {
|
||||
for _, v := range f.values {
|
||||
if !cb(v) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
func (f *fakeTargetProvider) Set(string, string) {}
|
||||
func (f *fakeTargetProvider) SetWithProbe(string, string, inputtypes.InputLivenessProbe) error {
|
||||
return nil
|
||||
}
|
||||
func (f *fakeTargetProvider) SetWithExclusions(string, string) error { return nil }
|
||||
func (f *fakeTargetProvider) InputType() string { return "test" }
|
||||
func (f *fakeTargetProvider) Close() {}
|
||||
|
||||
type slowExecuter struct{}
|
||||
|
||||
func (s *slowExecuter) Compile() error { return nil }
|
||||
func (s *slowExecuter) Requests() int { return 1 }
|
||||
func (s *slowExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
select {
|
||||
case <-ctx.Context().Done():
|
||||
return false, ctx.Context().Err()
|
||||
case <-time.After(200 * time.Millisecond):
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
func (s *slowExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.ResultEvent, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func Test_executeTemplateWithTargets_RespectsCancellation(t *testing.T) {
|
||||
e := newTestEngine()
|
||||
e.SetExecuterOptions(&protocols.ExecutorOptions{Logger: e.Logger, ResumeCfg: types.NewResumeCfg(), ProtocolType: tmpltypes.HTTPProtocol})
|
||||
|
||||
tpl := &templates.Template{}
|
||||
tpl.Executer = &slowExecuter{}
|
||||
|
||||
targets := &fakeTargetProvider{values: []*contextargs.MetaInput{{Input: "a"}, {Input: "b"}, {Input: "c"}}}
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
cancel()
|
||||
|
||||
var matched atomic.Bool
|
||||
e.executeTemplateWithTargets(ctx, tpl, targets, &matched)
|
||||
}
|
||||
@@ -34,17 +34,14 @@ func (e *Engine) executeWorkflow(ctx *scan.ScanContext, w *workflows.Workflow) b
|
||||
swg, _ := syncutil.New(syncutil.WithSize(templateThreads))
|
||||
|
||||
for _, template := range w.Workflows {
|
||||
swg.Add()
|
||||
|
||||
func(template *workflows.WorkflowTemplate) {
|
||||
defer swg.Done()
|
||||
|
||||
if err := e.runWorkflowStep(template, ctx, results, swg, w); err != nil {
|
||||
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
|
||||
}
|
||||
}(template)
|
||||
newCtx := scan.NewScanContext(ctx.Context(), ctx.Input.Clone())
|
||||
if err := e.runWorkflowStep(template, newCtx, results, swg, w); err != nil {
|
||||
gologger.Warning().Msgf(workflowStepExecutionError, template.Template, err)
|
||||
}
|
||||
}
|
||||
|
||||
swg.Wait()
|
||||
|
||||
return results.Load()
|
||||
}
|
||||
|
||||
|
||||
+4
-2
@@ -12,7 +12,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
var _ Provider = &customTemplateAzureBlob{}
|
||||
@@ -29,7 +29,9 @@ func NewAzureProviders(options *types.Options) ([]*customTemplateAzureBlob, erro
|
||||
// Establish a connection to Azure and build a client object with which to download templates from Azure Blob Storage
|
||||
azClient, err := getAzureBlobClient(options.AzureTenantID, options.AzureClientID, options.AzureClientSecret, options.AzureServiceURL)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("Error establishing Azure Blob client for %s", options.AzureContainerName)
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("Error establishing Azure Blob client for %s", options.AzureContainerName)
|
||||
return nil, errx
|
||||
}
|
||||
|
||||
// Create a new Azure Blob Storage container object
|
||||
|
||||
+33
-6
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
folderutil "github.com/projectdiscovery/utils/folder"
|
||||
"golang.org/x/oauth2"
|
||||
@@ -46,19 +47,45 @@ func (customTemplate *customTemplateGitHubRepo) Update(ctx context.Context) {
|
||||
downloadPath := config.DefaultConfig.CustomGitHubTemplatesDirectory
|
||||
clonePath := customTemplate.getLocalRepoClonePath(downloadPath)
|
||||
|
||||
// If folder does not exits then clone/download the repo
|
||||
// If folder does not exist then clone/download the repo
|
||||
if !fileutil.FolderExists(clonePath) {
|
||||
customTemplate.Download(ctx)
|
||||
return
|
||||
}
|
||||
|
||||
// Attempt to pull changes and handle the result
|
||||
customTemplate.handlePullChanges(clonePath)
|
||||
}
|
||||
|
||||
// handlePullChanges attempts to pull changes and logs the appropriate message
|
||||
func (customTemplate *customTemplateGitHubRepo) handlePullChanges(clonePath string) {
|
||||
err := customTemplate.pullChanges(clonePath, customTemplate.githubToken)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("%s", err)
|
||||
} else {
|
||||
gologger.Info().Msgf("Repo %s/%s successfully pulled the changes.\n", customTemplate.owner, customTemplate.reponame)
|
||||
|
||||
switch {
|
||||
case err == nil:
|
||||
customTemplate.logPullSuccess()
|
||||
case errors.Is(err, git.NoErrAlreadyUpToDate):
|
||||
customTemplate.logAlreadyUpToDate(err)
|
||||
default:
|
||||
customTemplate.logPullError(err)
|
||||
}
|
||||
}
|
||||
|
||||
// logPullSuccess logs a success message when changes are pulled
|
||||
func (customTemplate *customTemplateGitHubRepo) logPullSuccess() {
|
||||
gologger.Info().Msgf("Repo %s/%s successfully pulled the changes.\n", customTemplate.owner, customTemplate.reponame)
|
||||
}
|
||||
|
||||
// logAlreadyUpToDate logs an info message when repo is already up to date
|
||||
func (customTemplate *customTemplateGitHubRepo) logAlreadyUpToDate(err error) {
|
||||
gologger.Info().Msgf("%s", err)
|
||||
}
|
||||
|
||||
// logPullError logs an error message when pull fails
|
||||
func (customTemplate *customTemplateGitHubRepo) logPullError(err error) {
|
||||
gologger.Error().Msgf("%s", err)
|
||||
}
|
||||
|
||||
// NewGitHubProviders returns new instance of GitHub providers for downloading custom templates
|
||||
func NewGitHubProviders(options *types.Options) ([]*customTemplateGitHubRepo, error) {
|
||||
providers := []*customTemplateGitHubRepo{}
|
||||
@@ -187,7 +214,7 @@ func (ctr *customTemplateGitHubRepo) pullChanges(repoPath, githubToken string) e
|
||||
|
||||
err = w.Pull(pullOpts)
|
||||
if err != nil {
|
||||
return errors.Errorf("%s/%s: %s", ctr.owner, ctr.reponame, err.Error())
|
||||
return errkit.Wrapf(err, "%s/%s", ctr.owner, ctr.reponame)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
||||
+4
-2
@@ -9,7 +9,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
gitlab "gitlab.com/gitlab-org/api/client-go"
|
||||
)
|
||||
|
||||
@@ -28,7 +28,9 @@ func NewGitLabProviders(options *types.Options) ([]*customTemplateGitLabRepo, er
|
||||
// Establish a connection to GitLab and build a client object with which to download templates from GitLab
|
||||
gitLabClient, err := getGitLabClient(options.GitLabServerURL, options.GitLabToken)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("Error establishing GitLab client for %s %s", options.GitLabServerURL, err)
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("Error establishing GitLab client for %s %s", options.GitLabServerURL, err)
|
||||
return nil, errx
|
||||
}
|
||||
|
||||
// Create a new GitLab service client
|
||||
|
||||
Vendored
+4
-2
@@ -14,7 +14,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
nucleiConfig "github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
@@ -64,7 +64,9 @@ func NewS3Providers(options *types.Options) ([]*customTemplateS3Bucket, error) {
|
||||
if options.AwsBucketName != "" && !options.AwsTemplateDisableDownload {
|
||||
s3c, err := getS3Client(context.TODO(), options.AwsAccessKey, options.AwsSecretKey, options.AwsRegion, options.AwsProfile)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("error downloading s3 bucket %s", options.AwsBucketName)
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("error downloading s3 bucket %s", options.AwsBucketName)
|
||||
return nil, errx
|
||||
}
|
||||
ctBucket := &customTemplateS3Bucket{
|
||||
bucketName: options.AwsBucketName,
|
||||
|
||||
+13
-5
@@ -4,7 +4,7 @@ import (
|
||||
"context"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
type Provider interface {
|
||||
@@ -38,7 +38,9 @@ func NewCustomTemplatesManager(options *types.Options) (*CustomTemplatesManager,
|
||||
// Add GitHub providers
|
||||
githubProviders, err := NewGitHubProviders(options)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not create github providers for custom templates")
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("could not create github providers for custom templates")
|
||||
return nil, errx
|
||||
}
|
||||
for _, v := range githubProviders {
|
||||
ctm.providers = append(ctm.providers, v)
|
||||
@@ -47,7 +49,9 @@ func NewCustomTemplatesManager(options *types.Options) (*CustomTemplatesManager,
|
||||
// Add AWS S3 providers
|
||||
s3Providers, err := NewS3Providers(options)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not create s3 providers for custom templates")
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("could not create s3 providers for custom templates")
|
||||
return nil, errx
|
||||
}
|
||||
for _, v := range s3Providers {
|
||||
ctm.providers = append(ctm.providers, v)
|
||||
@@ -56,7 +60,9 @@ func NewCustomTemplatesManager(options *types.Options) (*CustomTemplatesManager,
|
||||
// Add Azure providers
|
||||
azureProviders, err := NewAzureProviders(options)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not create azure providers for custom templates")
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("could not create azure providers for custom templates")
|
||||
return nil, errx
|
||||
}
|
||||
for _, v := range azureProviders {
|
||||
ctm.providers = append(ctm.providers, v)
|
||||
@@ -65,7 +71,9 @@ func NewCustomTemplatesManager(options *types.Options) (*CustomTemplatesManager,
|
||||
// Add GitLab providers
|
||||
gitlabProviders, err := NewGitLabProviders(options)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not create gitlab providers for custom templates")
|
||||
errx := errkit.FromError(err)
|
||||
errx.Msgf("could not create gitlab providers for custom templates")
|
||||
return nil, errx
|
||||
}
|
||||
for _, v := range gitlabProviders {
|
||||
ctm.providers = append(ctm.providers, v)
|
||||
|
||||
@@ -61,7 +61,7 @@ func checkTimingDependency(
|
||||
|
||||
var requestsSent []requestsSentMetadata
|
||||
for requestsLeft > 0 {
|
||||
isCorrelationPossible, delayRecieved, err := sendRequestAndTestConfidence(regression, highSleepTimeSeconds, requestSender, baselineDelay)
|
||||
isCorrelationPossible, delayReceived, err := sendRequestAndTestConfidence(regression, highSleepTimeSeconds, requestSender, baselineDelay)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
@@ -69,29 +69,29 @@ func checkTimingDependency(
|
||||
return false, "", nil
|
||||
}
|
||||
// Check the delay is greater than baseline by seconds requested
|
||||
if delayRecieved < baselineDelay+float64(highSleepTimeSeconds)*0.8 {
|
||||
if delayReceived < baselineDelay+float64(highSleepTimeSeconds)*0.8 {
|
||||
return false, "", nil
|
||||
}
|
||||
requestsSent = append(requestsSent, requestsSentMetadata{
|
||||
delay: highSleepTimeSeconds,
|
||||
delayReceived: delayRecieved,
|
||||
delayReceived: delayReceived,
|
||||
})
|
||||
|
||||
isCorrelationPossibleSecond, delayRecievedSecond, err := sendRequestAndTestConfidence(regression, int(DefaultLowSleepTimeSeconds), requestSender, baselineDelay)
|
||||
isCorrelationPossibleSecond, delayReceivedSecond, err := sendRequestAndTestConfidence(regression, int(DefaultLowSleepTimeSeconds), requestSender, baselineDelay)
|
||||
if err != nil {
|
||||
return false, "", err
|
||||
}
|
||||
if !isCorrelationPossibleSecond {
|
||||
return false, "", nil
|
||||
}
|
||||
if delayRecievedSecond < baselineDelay+float64(DefaultLowSleepTimeSeconds)*0.8 {
|
||||
if delayReceivedSecond < baselineDelay+float64(DefaultLowSleepTimeSeconds)*0.8 {
|
||||
return false, "", nil
|
||||
}
|
||||
requestsLeft = requestsLeft - 2
|
||||
|
||||
requestsSent = append(requestsSent, requestsSentMetadata{
|
||||
delay: int(DefaultLowSleepTimeSeconds),
|
||||
delayReceived: delayRecievedSecond,
|
||||
delayReceived: delayReceivedSecond,
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Loaded 100 of 260 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user