Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3be6ccf28a | ||
|
|
55a4be4b6d | ||
|
|
49811d24a1 | ||
|
|
cb1bbff0f0 | ||
|
|
ebdabef420 | ||
|
|
0442aa79da | ||
|
|
7a9facfc26 | ||
|
|
abc63f1f8d | ||
|
|
f5f5f5fc21 | ||
|
|
efb27cfd49 | ||
|
|
5f952fc87a | ||
|
|
505ebc752f | ||
|
|
44f398c08d | ||
|
|
d7c8c8af80 | ||
|
|
7e4b4a8c55 | ||
|
|
92213e1335 | ||
|
|
159a8a53cf | ||
|
|
98948d0266 | ||
|
|
2c832f5590 | ||
|
|
53f56e179d | ||
|
|
cc5c5509dc | ||
|
|
aab2cadb64 | ||
|
|
4ef058758c | ||
|
|
d68af67e6e | ||
|
|
888a732fbc | ||
|
|
3f0de96726 | ||
|
|
1cd42c46c7 | ||
|
|
82680980a5 | ||
|
|
690089e1ce | ||
|
|
f0624820d3 | ||
|
|
7ba5d51b00 | ||
|
|
8b9acb2927 | ||
|
|
23825c77b2 | ||
|
|
d1614857bd | ||
|
|
1f945d6d50 | ||
|
|
e4dae52d5a | ||
|
|
c9f67897c4 | ||
|
|
c93a2b1ba4 | ||
|
|
6a561c6470 | ||
|
|
828dac9002 | ||
|
|
9983d7415c | ||
|
|
a118daa375 | ||
|
|
b8e818ac29 | ||
|
|
3e6c62b03b | ||
|
|
182e000f2c | ||
|
|
21205253c3 | ||
|
|
b18f99e462 | ||
|
|
694835c459 | ||
|
|
3eee9678d0 | ||
|
|
b69de15777 | ||
|
|
1610d96bc2 | ||
|
|
6347efa590 | ||
|
|
4cd065df5f | ||
|
|
3d2f31a56f | ||
|
|
0a536e6ce0 | ||
|
|
c8cc419e81 | ||
|
|
87e99be4f6 | ||
|
|
2ac9aaf871 | ||
|
|
a45e4bbd19 | ||
|
|
72da91a399 | ||
|
|
bd6330f72a | ||
|
|
8b1d9aa0d7 | ||
|
|
39f8be2125 | ||
|
|
59e69b561d | ||
|
|
fcdead9157 | ||
|
|
f7f8d53019 | ||
|
|
4ef78ae5fa | ||
|
|
e88c7e31d0 | ||
|
|
7f4cdf6196 | ||
|
|
6b0ba0f4f3 | ||
|
|
cc332979aa | ||
|
|
2b5a8f5b63 | ||
|
|
f30d0b3b8d |
No files matched your search
@@ -1,6 +1,7 @@
|
||||
addReviewers: true
|
||||
reviewers:
|
||||
- dogancanbakir
|
||||
- dwisiswant0
|
||||
|
||||
numberOfReviewers: 1
|
||||
skipKeywords:
|
||||
|
||||
@@ -39,4 +39,12 @@ dist
|
||||
pkg/protocols/common/helpers/deserialization/testdata/Deserialize.class
|
||||
pkg/protocols/common/helpers/deserialization/testdata/ValueObject.class
|
||||
pkg/protocols/common/helpers/deserialization/testdata/ValueObject2.ser
|
||||
vendor
|
||||
vendor
|
||||
|
||||
# Headless `screenshot` action
|
||||
*.png
|
||||
|
||||
# Profiling & tracing
|
||||
*.prof
|
||||
*.pprof
|
||||
*.trace
|
||||
@@ -457,26 +457,49 @@ func (template *Template) compileProtocolRequests(options protocols.ExecuterOpti
|
||||
That's it, you've added a new protocol to Nuclei. The next good step would be to write integration tests which are described in `integration-tests` and `cmd/integration-tests` directories.
|
||||
|
||||
|
||||
## Profiling Instructions
|
||||
## Profiling and Tracing
|
||||
|
||||
To enable dumping of Memory profiling data, `-profile-mem` flag can be used along with path to a file. This writes a pprof formatted file which can be used for investigate resource usage with `pprof` tool.
|
||||
To analyze Nuclei's performance and resource usage, you can generate memory profiles and trace files using the `-profile-mem` flag:
|
||||
|
||||
```console
|
||||
$ nuclei -t nuclei-templates/ -u https://example.com -profile-mem mem.pprof
|
||||
```bash
|
||||
nuclei -t nuclei-templates/ -u https://example.com -profile-mem=nuclei-$(git describe --tags)
|
||||
```
|
||||
|
||||
To view profile data in pprof, first install pprof. Then run the below command -
|
||||
This command creates two files:
|
||||
|
||||
```console
|
||||
$ go tool pprof mem.pprof
|
||||
* `nuclei.prof`: Memory (heap) profile
|
||||
* `nuclei.trace`: Execution trace
|
||||
|
||||
### Analyzing the Memory Profile
|
||||
|
||||
1. View the profile in the terminal:
|
||||
|
||||
```bash
|
||||
go tool pprof nuclei.prof
|
||||
```
|
||||
|
||||
To open a web UI on a port to visualize debug data, the below command can be used.
|
||||
2. Display top memory consumers:
|
||||
|
||||
```console
|
||||
$ go tool pprof -http=:8081 mem.pprof
|
||||
```bash
|
||||
go tool pprof -top nuclei.prof | grep "$(go list -m)" | head -10
|
||||
```
|
||||
|
||||
3. Visualize the profile in a web browser:
|
||||
|
||||
```bash
|
||||
go tool pprof -http=:$(shuf -i 1000-99999 -n 1) nuclei.prof
|
||||
```
|
||||
|
||||
### Analyzing the Trace File
|
||||
|
||||
To examine the execution trace:
|
||||
|
||||
```bash
|
||||
go tool trace nuclei.trace
|
||||
```
|
||||
|
||||
These tools help identify performance bottlenecks and memory leaks, allowing for targeted optimizations of Nuclei's codebase.
|
||||
|
||||
## Project Structure
|
||||
|
||||
- [pkg/reporting](./pkg/reporting) - Reporting modules for nuclei.
|
||||
|
||||
@@ -3,7 +3,6 @@ FROM golang:1.21-alpine AS build-env
|
||||
RUN apk add build-base
|
||||
WORKDIR /app
|
||||
COPY . /app
|
||||
WORKDIR /app
|
||||
RUN go mod download
|
||||
RUN go build ./cmd/nuclei
|
||||
|
||||
|
||||
@@ -1,30 +1,4 @@
|
||||
<h1 align="center">
|
||||
<br>
|
||||
<a href="https://nuclei.projectdiscovery.io"><img src="static/nuclei-logo.png" width="200px" alt="Nuclei"></a>
|
||||
</h1>
|
||||
|
||||
<h4 align="center">Fast and customisable vulnerability scanner based on simple YAML based DSL.</h4>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#how-it-works">How</a> •
|
||||
<a href="#install-nuclei">Install</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">Documentation</a> •
|
||||
<a href="#credits">Credits</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">FAQs</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Join Discord</a>
|
||||
</p>
|
||||

|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
@@ -34,78 +8,103 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_JP.md">日本語</a>
|
||||
</p>
|
||||
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
|
||||
<img src="https://img.shields.io/badge/go-1.21-%2300ADD8.svg?style=for-the-badge&logo=go&logoColor=white">
|
||||
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/overview?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme"><img src="https://img.shields.io/badge/Documentation-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNCIgaGVpZ2h0PSIyNCIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMiIgc3Ryb2tlLWxpbmVjYXA9InJvdW5kIiBzdHJva2UtbGluZWpvaW49InJvdW5kIiBjbGFzcz0ibHVjaWRlIGx1Y2lkZS1ib29rLW9wZW4iPjxwYXRoIGQ9Ik0xMiA3djE0Ii8+PHBhdGggZD0iTTMgMThhMSAxIDAgMCAxLTEtMVY0YTEgMSAwIDAgMSAxLTFoNWE0IDQgMCAwIDEgNCA0IDQgNCAwIDAgMSA0LTRoNWExIDEgMCAwIDEgMSAxdjEzYTEgMSAwIDAgMS0xIDFoLTZhMyAzIDAgMCAwLTMgMyAzIDMgMCAwIDAtMy0zeiIvPjwvc3ZnPg==&logoColor=white"></a>
|
||||
|
||||
<a href="https://github.com/projectdiscovery/nuclei-templates"><img src="https://img.shields.io/badge/Templates Library-%23000000.svg?style=for-the-badge&logo=data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxNiIgaGVpZ2h0PSIxNiIgdmlld0JveD0iMCAwIDI0IDI0IiBmaWxsPSJub25lIiBzdHJva2U9IiNmZmZmZmYiIHN0cm9rZS13aWR0aD0iMS41IiBzdHJva2UtbGluZWNhcD0icm91bmQiIHN0cm9rZS1saW5lam9pbj0icm91bmQiIGNsYXNzPSJsdWNpZGUgbHVjaWRlLXNoaWVsZCI+PHBhdGggZD0iTTIwIDEzYzAgNS0zLjUgNy41LTcuNjYgOC45NWExIDEgMCAwIDEtLjY3LS4wMUM3LjUgMjAuNSA0IDE4IDQgMTNWNmExIDEgMCAwIDEgMS0xYzIgMCA0LjUtMS4yIDYuMjQtMi43MmExLjE3IDEuMTcgMCAwIDEgMS41MiAwQzE0LjUxIDMuODEgMTcgNSAxOSA1YTEgMSAwIDAgMSAxIDF6Ii8+PC9zdmc+&logoColor=white"></a>
|
||||
|
||||
<a href="https://discord.gg/projectdiscovery?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme"><img src="https://img.shields.io/badge/Discord-%235865F2.svg?style=for-the-badge&logo=discord&logoColor=white"></a>
|
||||
|
||||
---
|
||||
|
||||
Nuclei is used to send requests across targets based on a template, leading to zero false positives and providing fast scanning on a large number of hosts. Nuclei offers scanning for a variety of protocols, including TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless, Code etc. With powerful and flexible templating, Nuclei can be used to model all kinds of security checks.
|
||||
</p>
|
||||
|
||||
<br />
|
||||
|
||||
We have a [dedicated repository](https://github.com/projectdiscovery/nuclei-templates) that houses various type of vulnerability templates contributed by **more than 300** security researchers and engineers.
|
||||
Nuclei is a modern, high-performance vulnerability scanner that leverages simple YAML-based templates. It empowers you to design custom vulnerability detection scenarios that mimic real-world conditions, leading to zero false positives.
|
||||
|
||||
## How it works
|
||||
- Simple YAML format for creating and customizing vulnerability templates.
|
||||
- Contributed by thousands of security professionals to tackle trending vulnerabilities.
|
||||
- Reduce false positives by simulating real-world steps to verify a vulnerability.
|
||||
- Ultra-fast parallel scan processing and request clustering.
|
||||
- Integrate into CI/CD pipelines for vulnerability detection and regression testing.
|
||||
- Supports multiple protocols like TCP, DNS, HTTP, SSL, WHOIS JavaScript, Code and more.
|
||||
- Integrate with Jira, Splunk, GitHub, Elastic, GitLab.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
- [Get Started](#get-started)
|
||||
- [1. Nuclei CLI](#1-nuclei-cli)
|
||||
- [2. Pro and Enterprise Editions](#2-pro-and-enterprise-editions)
|
||||
- [Documentation](#documentation)
|
||||
- [Command Line Flags](#command-line-flags)
|
||||
- [Single target scan](#single-target-scan)
|
||||
- [Scanning multiple targets](#scanning-multiple-targets)
|
||||
- [Network scan](#network-scan)
|
||||
- [Scanning with your custom template](#scanning-with-your-custom-template)
|
||||
- [Connect Nuclei to ProjectDiscovery](#connect-nuclei-to-projectdiscovery)
|
||||
- [Nuclei Templates, Community and Rewards 💎](#nuclei-templates-community-and-rewards-)
|
||||
- [Our Mission](#our-mission)
|
||||
- [Contributors ❤️](#contributors-️)
|
||||
- [License](#license)
|
||||
|
||||
---
|
||||
|
||||
| :exclamation: **Disclaimer** |
|
||||
|---------------------------------|
|
||||
| **This project is in active development**. Expect breaking changes with releases. Review the release changelog before updating. |
|
||||
| This project was primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
| This project is primarily built to be used as a standalone CLI tool. **Running nuclei as a service may pose security risks.** It's recommended to use with caution and additional security measures. |
|
||||
|
||||
# Install Nuclei
|
||||
## Get Started
|
||||
|
||||
Nuclei requires **go1.21** to install successfully. Run the following command to install the latest version -
|
||||
### **1. Nuclei CLI**
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
Install Nuclei on your machine. Get started by following the installation guide [here](https://docs.projectdiscovery.io/tools/nuclei/install?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme). Additionally, We provide [a free cloud tier](https://cloud.projectdiscovery.io/sign-up) and comes with a generous monthly free limits:
|
||||
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
- Store and visualize your vulnerability findings
|
||||
- Write and manage your nuclei templates
|
||||
- Access latest nuclei templates
|
||||
- Discover and store your targets
|
||||
|
||||
**More installation [methods can be found here](https://docs.projectdiscovery.io/tools/nuclei/install).**
|
||||
### **2. Pro and Enterprise Editions**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
For security teams and enterprises, we provide a cloud-hosted service built on top of Nuclei OSS, fine-tuned to help you continuously run vulnerability scans at scale with your team and existing workflows:
|
||||
|
||||
### Nuclei Templates
|
||||
- 50x faster scans
|
||||
- Large scale scanning with high accuracy
|
||||
- Integrations with cloud services (AWS, GCP, Azure, CloudFlare, Fastly, Terraform, Kubernetes)
|
||||
- Jira, Slack, Linear, APIs and Webhooks
|
||||
- Executive and compliance reporting
|
||||
- Plus: Real-time scanning, SAML SSO, SOC 2 compliant platform (with EU and US hosting options), shared team workspaces, and more
|
||||
- We're constantly [adding new features](https://feedback.projectdiscovery.io/changelog)!
|
||||
- **Ideal for:** Pentesters, security teams, and enterprises
|
||||
|
||||
Nuclei has built-in support for automatic template download/update as default since version [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2). [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) project provides a community-contributed list of ready-to-use templates that is constantly updated.
|
||||
## Documentation
|
||||
|
||||
You may still use the `update-templates` flag to update the nuclei templates at any time; You can write your own checks for your individual workflow and needs following Nuclei's [templating guide](https://docs.projectdiscovery.io/templates/).
|
||||
Browse the full Nuclei [documentation here](https://docs.projectdiscovery.io/tools/nuclei/running). If you’re new to Nuclei, check out our [foundational Youtube series.](https://www.youtube.com/playlist?list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl)
|
||||
|
||||
The YAML DSL reference syntax is available [here](SYNTAX-REFERENCE.md).
|
||||
<p align="center">
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<a href="https://www.youtube.com/watch?v=b5qMyQvL1ZA&list=PLZRbR9aMzTTpItEdeNSulo8bYsvil80Rl&utm_source=github&utm_medium=web&utm_campaign=nuclei_readme" target="_blank"><img src="/static/nuclei-getting-started.png" width="350px"></a> <a href="https://www.youtube.com/watch?v=nFXygQdtjyw&utm_source=github&utm_medium=web&utm_campaign=nuclei_readme" target="_blank"><img src="/static/nuclei-write-your-first-template.png" width="350px"></a>
|
||||
|
||||
### Usage
|
||||
</p>
|
||||
|
||||
### Command Line Flags
|
||||
|
||||
To display all the flags for the tool:
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
This will display help for the tool. Here are all the switches it supports.
|
||||
|
||||
<details>
|
||||
<summary>Expand full help flags</summary>
|
||||
|
||||
```console
|
||||
Nuclei is a fast, template based vulnerability scanner focusing
|
||||
@@ -279,23 +278,24 @@ HEADLESS:
|
||||
-lha, -list-headless-action list available headless actions
|
||||
|
||||
DEBUG:
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string optional nuclei memory profile dump file
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
-debug show all requests and responses
|
||||
-dreq, -debug-req show all sent requests
|
||||
-dresp, -debug-resp show all received responses
|
||||
-p, -proxy string[] list of http/socks5 proxy to use (comma separated or file input)
|
||||
-pi, -proxy-internal proxy all internal requests
|
||||
-ldf, -list-dsl-function list all supported DSL function signatures
|
||||
-tlog, -trace-log string file to write sent requests trace log
|
||||
-elog, -error-log string file to write sent requests error log
|
||||
-version show nuclei version
|
||||
-hm, -hang-monitor enable nuclei hang monitoring
|
||||
-v, -verbose show verbose output
|
||||
-profile-mem string generate memory (heap) profile & trace files
|
||||
-vv display templates loaded for scan
|
||||
-svd, -show-var-dump show variables dump for debugging
|
||||
-vdl, -var-dump-limit int limit the number of characters displayed in var dump (default 255)
|
||||
-ep, -enable-pprof enable pprof debugging server
|
||||
-tv, -templates-version shows the version of the installed nuclei-templates
|
||||
-hc, -health-check run diagnostic check up
|
||||
|
||||
UPDATE:
|
||||
-up, -update update nuclei engine to the latest released version
|
||||
@@ -310,11 +310,13 @@ STATISTICS:
|
||||
-mp, -metrics-port int port to expose nuclei metrics on (default 9092)
|
||||
|
||||
CLOUD:
|
||||
-auth configure projectdiscovery cloud (pdcp) api key (default true)
|
||||
-tid, -team-id string upload scan results to given team id (optional) (default "none")
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard
|
||||
-sid, -scan-id string upload scan results to existing scan id (optional)
|
||||
-sname, -scan-name string scan name to set (optional)
|
||||
-auth configure projectdiscovery cloud (pdcp) api key (default true)
|
||||
-tid, -team-id string upload scan results to given team id (optional) (default "none")
|
||||
-cup, -cloud-upload upload scan results to pdcp dashboard [DEPRECATED use -dashboard]
|
||||
-sid, -scan-id string upload scan results to existing scan id (optional)
|
||||
-sname, -scan-name string scan name to set (optional)
|
||||
-pd, -dashboard upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard
|
||||
-pdu, -dashboard-upload string upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard
|
||||
|
||||
AUTHENTICATION:
|
||||
-sf, -secret-file string[] path to config file containing secrets for nuclei authenticated scan
|
||||
@@ -323,59 +325,189 @@ AUTHENTICATION:
|
||||
|
||||
EXAMPLES:
|
||||
Run nuclei on single host:
|
||||
$ nuclei -target example.com
|
||||
$ nuclei -target example.com
|
||||
|
||||
Run nuclei with specific template directories:
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
|
||||
Run nuclei against a list of hosts:
|
||||
$ nuclei -list hosts.txt
|
||||
$ nuclei -list hosts.txt
|
||||
|
||||
Run nuclei with a JSON output:
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
|
||||
Run nuclei with sorted Markdown outputs (with environment variables):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Additional documentation is available at: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Running Nuclei
|
||||
Additional documentation is available at: [https://docs.nuclei.sh/getting-started/running](https://docs.nuclei.sh/getting-started/running?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme)
|
||||
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/running for details on running Nuclei
|
||||
</details>
|
||||
|
||||
### Using Nuclei From Go Code
|
||||
### Single target scan
|
||||
|
||||
Complete guide of using Nuclei as Library/SDK is available at [godoc](https://pkg.go.dev/github.com/projectdiscovery/nuclei/v3/lib#section-readme)
|
||||
To perform a quick scan on web-application:
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com
|
||||
```
|
||||
|
||||
### Scanning multiple targets
|
||||
|
||||
Nuclei can handle bulk scanning by providing a list of targets. You can use a file containing multiple URLs.
|
||||
|
||||
```sh
|
||||
nuclei -targets urls.txt
|
||||
```
|
||||
|
||||
### Network scan
|
||||
|
||||
This will scan the entire subnet for network-related issues, such as open ports or misconfigured services.
|
||||
|
||||
```sh
|
||||
nuclei -target 192.168.1.0/24
|
||||
```
|
||||
|
||||
### Scanning with your custom template
|
||||
|
||||
To write and use your own template, create a `.yaml` file with specific rules, then use it as follows.
|
||||
|
||||
```sh
|
||||
nuclei -u https://example.com -t /path/to/your-template.yaml
|
||||
```
|
||||
|
||||
### Connect Nuclei to ProjectDiscovery
|
||||
|
||||
You can run the scans on your machine and upload the results to the cloud platform for further analysis and remediation.
|
||||
|
||||
```sh
|
||||
nuclei -target https://example.com -dashboard
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> This feature is absolutely free and does not require any subscription. For a detailed guide, refer to the [documentation](https://docs.projectdiscovery.io/cloud/scanning/nuclei-scan?utm_source=github&utm_medium=web&utm_campaign=nuclei_readme).
|
||||
|
||||
## Nuclei Templates, Community and Rewards 💎
|
||||
[Nuclei templates](https://github.com/projectdiscovery/nuclei-templates) are based on the concepts of YAML based template files that define how the requests will be sent and processed. This allows easy extensibility capabilities to nuclei. The templates are written in YAML which specifies a simple human-readable format to quickly define the execution process.
|
||||
|
||||
Try it online with our free AI powered Nuclei Templates Editor by [clicking here.](https://cloud.projectdiscovery.io/templates)
|
||||
|
||||
Nuclei Templates offer a streamlined way to identify and communicate vulnerabilities, combining essential details like severity ratings and detection methods. This open-source, community-developed tool accelerates threat response and is widely recognized in the cybersecurity world. Nuclei templates are actively contributed by thousands of security researchers globally. We run two programs for our contributors: [Pioneers](https://projectdiscovery.io/pioneers) and [💎 bounties](https://github.com/projectdiscovery/nuclei-templates/issues?q=is%3Aissue%20state%3Aopen%20label%3A%22%F0%9F%92%8E%20Bounty%22).
|
||||
|
||||
|
||||
### Resources
|
||||
<p align="left">
|
||||
<a href="/static/nuclei-templates-teamcity.png" target="_blank"><img src="/static/nuclei-templates-teamcity.png" width="1200px" alt="Nuclei template example for detecting TeamCity misconfiguration" /></a>
|
||||
</p>
|
||||
|
||||
You can access the main documentation for Nuclei at https://docs.projectdiscovery.io/tools/nuclei/, and learn more about Nuclei in the cloud with [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io)
|
||||
#### Examples
|
||||
|
||||
See https://docs.projectdiscovery.io/tools/nuclei/resources for more resources and videos about Nuclei!
|
||||
Visit [our documentation](https://docs.projectdiscovery.io/templates/introduction) for use cases and ideas.
|
||||
|
||||
### Credits
|
||||
| Use case | Nuclei template |
|
||||
| :----------------------------------- | :------------------------------------------------- |
|
||||
| Detect known CVEs | **[CVE-2021-44228 (Log4Shell)](https://cloud.projectdiscovery.io/public/CVE-2021-45046)** |
|
||||
| Identify Out-of-Band vulnerabilities | **[Blind SQL Injection via OOB](https://cloud.projectdiscovery.io/public/CVE-2024-22120)** |
|
||||
| SQL Injection detection | **[Generic SQL Injection](https://cloud.projectdiscovery.io/public/CVE-2022-34265)** |
|
||||
| Cross-Site Scripting (XSS) | **[Reflected XSS Detection](https://cloud.projectdiscovery.io/public/CVE-2023-4173)** |
|
||||
| Default or weak passwords | **[Default Credentials Check](https://cloud.projectdiscovery.io/public/airflow-default-login)** |
|
||||
| Secret files or data exposure | **[Sensitive File Disclosure](https://cloud.projectdiscovery.io/public/airflow-configuration-exposure)** |
|
||||
| Identify open redirects | **[Open Redirect Detection](https://cloud.projectdiscovery.io/public/open-redirect)** |
|
||||
| Detect subdomain takeovers | **[Subdomain Takeover Templates](https://cloud.projectdiscovery.io/public/azure-takeover-detection)** |
|
||||
| Security misconfigurations | **[Unprotected Jenkins Console](https://cloud.projectdiscovery.io/public/unauthenticated-jenkins)** |
|
||||
| Weak SSL/TLS configurations | **[SSL Certificate Expiry](https://cloud.projectdiscovery.io/public/expired-ssl)** |
|
||||
| Misconfigured cloud services | **[Open S3 Bucket Detection](https://cloud.projectdiscovery.io/public/s3-public-read-acp)** |
|
||||
| Remote code execution vulnerabilities| **[RCE Detection Templates](https://cloud.projectdiscovery.io/public/CVE-2024-29824)** |
|
||||
| Directory traversal attacks | **[Path Traversal Detection](https://cloud.projectdiscovery.io/public/oracle-fatwire-lfi)** |
|
||||
| File inclusion vulnerabilities | **[Local/Remote File Inclusion](https://cloud.projectdiscovery.io/public/CVE-2023-6977)** |
|
||||
|
||||
|
||||
## Our Mission
|
||||
|
||||
Traditional vulnerability scanners were built decades ago. They are closed-source, incredibly slow, and vendor-driven. Today's attackers are mass exploiting newly released CVEs across the internet within days, unlike the years it used to take. This shift requires a completely different approach to tackling trending exploits on the internet.
|
||||
|
||||
We built Nuclei to solve this challenge. We made the entire scanning engine framework open and customizable—allowing the global security community to collaborate and tackle the trending attack vectors and vulnerabilities on the internet. Nuclei is now used and contributed by Fortune 500 enterprises, government agencies, universities.
|
||||
|
||||
You can participate by contributing to our code, [templates library](https://github.com/projectdiscovery/nuclei-templates), or [joining our team.](https://projectdiscovery.io/)
|
||||
|
||||
## Contributors :heart:
|
||||
|
||||
Thanks to all the amazing [community contributors for sending PRs](https://github.com/projectdiscovery/nuclei/graphs/contributors) and keeping this project updated. :heart:
|
||||
|
||||
If you have an idea or some kind of improvement, you are welcome to contribute and participate in the Project, feel free to send your PR.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors">
|
||||
<img src="https://contrib.rocks/image?repo=projectdiscovery/nuclei&max=500">
|
||||
</a>
|
||||
<p align="left">
|
||||
<a href="https://github.com/Ice3man543"><img src="https://avatars.githubusercontent.com/u/22318055?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/apps/dependabot"><img src="https://avatars.githubusercontent.com/in/29110?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehsandeep"><img src="https://avatars.githubusercontent.com/u/8293321?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Mzack9999"><img src="https://avatars.githubusercontent.com/u/13421144?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/forgedhallpass"><img src="https://avatars.githubusercontent.com/u/13679401?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tarunKoyalwar"><img src="https://avatars.githubusercontent.com/u/45962551?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/manuelbua"><img src="https://avatars.githubusercontent.com/u/819314?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/actions-user"><img src="https://avatars.githubusercontent.com/u/65916846?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dogancanbakir"><img src="https://avatars.githubusercontent.com/u/65292895?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parrasajad"><img src="https://avatars.githubusercontent.com/u/16835787?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vzamanillo"><img src="https://avatars.githubusercontent.com/u/10209695?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ShubhamRasal"><img src="https://avatars.githubusercontent.com/u/45902122?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/RamanaReddy0M"><img src="https://avatars.githubusercontent.com/u/90540245?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/LuitelSamikshya"><img src="https://avatars.githubusercontent.com/u/85764322?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kchason"><img src="https://avatars.githubusercontent.com/u/1111099?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/pmareke"><img src="https://avatars.githubusercontent.com/u/3502075?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dwisiswant0"><img src="https://avatars.githubusercontent.com/u/25837540?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/xm1k3"><img src="https://avatars.githubusercontent.com/u/73166077?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/5amu"><img src="https://avatars.githubusercontent.com/u/39925709?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ehrishirajsharma"><img src="https://avatars.githubusercontent.com/u/35542790?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zerodivisi0n"><img src="https://avatars.githubusercontent.com/u/687694?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/geeknik"><img src="https://avatars.githubusercontent.com/u/466878?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/TerminalFi"><img src="https://avatars.githubusercontent.com/u/32599364?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KaulSe"><img src="https://avatars.githubusercontent.com/u/45340011?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MachadoOtto"><img src="https://avatars.githubusercontent.com/u/93268441?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jonathanwalker"><img src="https://avatars.githubusercontent.com/u/14978093?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/nHurD"><img src="https://avatars.githubusercontent.com/u/233374?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/jessekelly881"><img src="https://avatars.githubusercontent.com/u/22938931?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/JaneX8"><img src="https://avatars.githubusercontent.com/u/5116641?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/eltociear"><img src="https://avatars.githubusercontent.com/u/22633385?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/atomiczsec"><img src="https://avatars.githubusercontent.com/u/75549184?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/M-Faheem-Khan"><img src="https://avatars.githubusercontent.com/u/17150767?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/denandz"><img src="https://avatars.githubusercontent.com/u/5291556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/tibbon"><img src="https://avatars.githubusercontent.com/u/82880?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/dany74q"><img src="https://avatars.githubusercontent.com/u/2129762?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0x123456789"><img src="https://avatars.githubusercontent.com/u/36066426?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/danigoland"><img src="https://avatars.githubusercontent.com/u/15079567?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/ChrisMandich"><img src="https://avatars.githubusercontent.com/u/14286797?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/austintraver"><img src="https://avatars.githubusercontent.com/u/25112463?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/socialsister"><img src="https://avatars.githubusercontent.com/u/155628741?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Anemys"><img src="https://avatars.githubusercontent.com/u/51196227?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/andreangelucci"><img src="https://avatars.githubusercontent.com/u/18552197?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/AlexS778"><img src="https://avatars.githubusercontent.com/u/98418121?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/noraj"><img src="https://avatars.githubusercontent.com/u/16578570?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/akkuman"><img src="https://avatars.githubusercontent.com/u/7813511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/zrquan"><img src="https://avatars.githubusercontent.com/u/33086594?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/1efty"><img src="https://avatars.githubusercontent.com/u/18194777?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/rsrdesarrollo"><img src="https://avatars.githubusercontent.com/u/5142014?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/razin99"><img src="https://avatars.githubusercontent.com/u/44442082?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MetzinAround"><img src="https://avatars.githubusercontent.com/u/65838556?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/vil02"><img src="https://avatars.githubusercontent.com/u/65706193?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mrschyte"><img src="https://avatars.githubusercontent.com/u/8571?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/PeterDaveHello"><img src="https://avatars.githubusercontent.com/u/3691490?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/parthmalhotra"><img src="https://avatars.githubusercontent.com/u/28601533?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/owenrumney"><img src="https://avatars.githubusercontent.com/u/3049157?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Ovi3"><img src="https://avatars.githubusercontent.com/u/29408109?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/Bisstocuz"><img src="https://avatars.githubusercontent.com/u/42398278?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/daffainfo"><img src="https://avatars.githubusercontent.com/u/36522826?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/mhmdiaa"><img src="https://avatars.githubusercontent.com/u/19687798?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/MiryangJung"><img src="https://avatars.githubusercontent.com/u/48237511?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/0xmin"><img src="https://avatars.githubusercontent.com/u/44919834?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/maikthulhu"><img src="https://avatars.githubusercontent.com/u/680830?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/sttlr"><img src="https://avatars.githubusercontent.com/u/40246850?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/iamRjarpan"><img src="https://avatars.githubusercontent.com/u/45498226?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/leoloobeek"><img src="https://avatars.githubusercontent.com/u/8801754?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KristinnVikar"><img src="https://avatars.githubusercontent.com/u/93918469?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/kant01ne"><img src="https://avatars.githubusercontent.com/u/5072452?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
<a href="https://github.com/KeisukeYamashita"><img src="https://avatars.githubusercontent.com/u/23056537?v=4" width="50" height="50" alt="" style="max-width: 100%;"></a>
|
||||
</p>
|
||||
|
||||
## License
|
||||
|
||||
Do also check out the below similar open-source projects that may fit in your workflow:
|
||||
Nuclei is distributed under [MIT License](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md).
|
||||
|
||||
[FFuF](https://github.com/ffuf/ffuf), [Qsfuzz](https://github.com/ameenmaali/qsfuzz), [Inception](https://github.com/proabiral/inception), [Snallygaster](https://github.com/hannob/snallygaster), [Gofingerprint](https://github.com/Static-Flow/gofingerprint), [Sn1per](https://github.com/1N3/Sn1per/tree/master/templates), [Google tsunami](https://github.com/google/tsunami-security-scanner), [Jaeles](https://github.com/jaeles-project/jaeles), [ChopChop](https://github.com/michelin/ChopChop)
|
||||
|
||||
### License
|
||||
|
||||
Nuclei is distributed under [MIT License](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://docs.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Check Nuclei Documentation"></a>
|
||||
</h1>
|
||||
<img src="https://img.shields.io/badge/license-MIT-000000.svg?style=for-the-badge">
|
||||
@@ -1651,6 +1651,19 @@ FuzzPreConditionOperator is the operator between multiple PreConditions for fuzz
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>global-matchers</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
GlobalMatchers marks matchers as static and applies globally to all result events from other templates
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3210,6 +3223,19 @@ read-all: false
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>stop-at-first-match</code> <i>bool</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -3664,8 +3690,18 @@ Enum Values:
|
||||
|
||||
- <code>files</code>
|
||||
|
||||
- <code>waitdom</code>
|
||||
|
||||
- <code>waitfcp</code>
|
||||
|
||||
- <code>waitfmp</code>
|
||||
|
||||
- <code>waitidle</code>
|
||||
|
||||
- <code>waitload</code>
|
||||
|
||||
- <code>waitstable</code>
|
||||
|
||||
- <code>getresource</code>
|
||||
|
||||
- <code>extract</code>
|
||||
@@ -3754,11 +3790,33 @@ Appears in:
|
||||
Part Definitions:
|
||||
|
||||
|
||||
- <code>type</code> - Type is the type of request made
|
||||
- <code>response</code> - JSON SSL protocol handshake details
|
||||
- <code>not_after</code> - Timestamp after which the remote cert expires
|
||||
- <code>template-id</code> - ID of the template executed
|
||||
- <code>template-info</code> - Info Block of the template executed
|
||||
- <code>template-path</code> - Path of the template executed
|
||||
- <code>host</code> - Host is the input to the template
|
||||
- <code>port</code> - Port is the port of the host
|
||||
- <code>matched</code> - Matched is the input which was matched upon
|
||||
- <code>type</code> - Type is the type of request made
|
||||
- <code>timestamp</code> - Timestamp is the time when the request was made
|
||||
- <code>response</code> - JSON SSL protocol handshake details
|
||||
- <code>cipher</code> - Cipher is the encryption algorithm used
|
||||
- <code>domains</code> - Domains are the list of domain names in the certificate
|
||||
- <code>fingerprint_hash</code> - Fingerprint hash is the unique identifier of the certificate
|
||||
- <code>ip</code> - IP is the IP address of the server
|
||||
- <code>issuer_cn</code> - Issuer CN is the common name of the certificate issuer
|
||||
- <code>issuer_dn</code> - Issuer DN is the distinguished name of the certificate issuer
|
||||
- <code>issuer_org</code> - Issuer organization is the organization of the certificate issuer
|
||||
- <code>not_after</code> - Timestamp after which the remote cert expires
|
||||
- <code>not_before</code> - Timestamp before which the certificate is not valid
|
||||
- <code>probe_status</code> - Probe status indicates if the probe was successful
|
||||
- <code>serial</code> - Serial is the serial number of the certificate
|
||||
- <code>sni</code> - SNI is the server name indication used in the handshake
|
||||
- <code>subject_an</code> - Subject AN is the list of subject alternative names
|
||||
- <code>subject_cn</code> - Subject CN is the common name of the certificate subject
|
||||
- <code>subject_dn</code> - Subject DN is the distinguished name of the certificate subject
|
||||
- <code>subject_org</code> - Subject organization is the organization of the certificate subject
|
||||
- <code>tls_connection</code> - TLS connection is the type of TLS connection used
|
||||
- <code>tls_version</code> - TLS version is the version of the TLS protocol used
|
||||
|
||||
<hr />
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
@@ -26,9 +27,9 @@ func (h *httpNoAccess) Execute(filePath string) error {
|
||||
}
|
||||
event := &output.ResultEvent{}
|
||||
_ = json.Unmarshal([]byte(results[0]), event)
|
||||
|
||||
if event.Error != "no address found for host" {
|
||||
return fmt.Errorf("unexpected result: expecting \"no address found for host\" error but got none")
|
||||
expectedError := "no address found for host"
|
||||
if !strings.Contains(event.Error, expectedError) {
|
||||
return fmt.Errorf("unexpected result: expecting \"%s\" error but got \"%s\"", expectedError, event.Error)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/testutils"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
var workflowTestcases = []TestCaseInfo{
|
||||
@@ -25,6 +26,7 @@ var workflowTestcases = []TestCaseInfo{
|
||||
{Path: "workflow/dns-value-share-workflow.yaml", TestCase: &workflowDnsKeyValueShare{}},
|
||||
{Path: "workflow/code-value-share-workflow.yaml", TestCase: &workflowCodeKeyValueShare{}, DisableOn: isCodeDisabled}, // isCodeDisabled declared in code.go
|
||||
{Path: "workflow/multiprotocol-value-share-workflow.yaml", TestCase: &workflowMultiProtocolKeyValueShare{}},
|
||||
{Path: "workflow/multimatch-value-share-workflow.yaml", TestCase: &workflowMultiMatchKeyValueShare{}},
|
||||
{Path: "workflow/shared-cookie.yaml", TestCase: &workflowSharedCookies{}},
|
||||
}
|
||||
|
||||
@@ -229,6 +231,44 @@ func (h *workflowMultiProtocolKeyValueShare) Execute(filePath string) error {
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
|
||||
type workflowMultiMatchKeyValueShare struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
func (h *workflowMultiMatchKeyValueShare) Execute(filePath string) error {
|
||||
var receivedData []string
|
||||
router := httprouter.New()
|
||||
router.GET("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Fprintf(w, "This is test matcher text")
|
||||
})
|
||||
router.GET("/path1", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
fmt.Fprintf(w, "href=\"test-value-%s\"", r.URL.Query().Get("v"))
|
||||
})
|
||||
router.GET("/path2", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
body, _ := io.ReadAll(r.Body)
|
||||
receivedData = append(receivedData, string(body))
|
||||
fmt.Fprintf(w, "test-value")
|
||||
})
|
||||
ts := httptest.NewServer(router)
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiWorkflowAndGetResults(filePath, ts.URL, debug)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Check if we received the data from both request to /path1 and it is not overwritten by the later one.
|
||||
// They will appear in brackets because of another bug: https://github.com/orgs/projectdiscovery/discussions/3766
|
||||
if !sliceutil.Contains(receivedData, "[test-value-1]") || !sliceutil.Contains(receivedData, "[test-value-2]") {
|
||||
return fmt.Errorf(
|
||||
"incorrect data: did not receive both extracted data from the first request!\nReceived Data:\n\t%s\nResults:\n\t%s",
|
||||
strings.Join(receivedData, "\n\t"),
|
||||
strings.Join(results, "\n\t"),
|
||||
)
|
||||
}
|
||||
// The number of expected results is 3: the workflow's Matcher Name based condition check forwards both match, and the other branch with simple subtemplates goes with one
|
||||
return expectResultsCount(results, 3)
|
||||
}
|
||||
|
||||
type workflowSharedCookies struct{}
|
||||
|
||||
// Execute executes a test case and returns an error if occurred
|
||||
|
||||
@@ -142,4 +142,34 @@
|
||||
# # Username for the elasticsearch instance
|
||||
# username: test
|
||||
# # Password is the password for elasticsearch instance
|
||||
# password: test
|
||||
# password: test
|
||||
#linear:
|
||||
# # api-key is the API key for the linear account
|
||||
# api-key: ""
|
||||
# # allow-list sets a tracker level filter to only create issues for templates with
|
||||
# # these severity labels or tags (does not affect exporters. set those globally)
|
||||
# deny-list:
|
||||
# severity: critical
|
||||
# # deny-list sets a tracker level filter to never create issues for templates with
|
||||
# # these severity labels or tags (does not affect exporters. set those globally)
|
||||
# deny-list:
|
||||
# severity: low
|
||||
# # team-id is the ID of the team in Linear
|
||||
# team-id: ""
|
||||
# # project-id is the ID of the project in Linear
|
||||
# project-id: ""
|
||||
# # duplicate-issue-check flag to enable duplicate tracking issue check
|
||||
# duplicate-issue-check: false
|
||||
# # open-state-id is the ID of the open state in Linear
|
||||
# open-state-id: ""
|
||||
#mongodb:
|
||||
# # the connection string to the MongoDB database
|
||||
# # (e.g., mongodb://root:example@localhost:27017/nuclei?ssl=false&authSource=admin)
|
||||
# connection-string: ""
|
||||
# # the name of the collection to store the issues
|
||||
# collection-name: ""
|
||||
# # excludes the Request and Response from the results (helps with filesize)
|
||||
# omit-raw: false
|
||||
# # determines the number of results to be kept in memory before writing it to the database or 0 to
|
||||
# # persist all in memory and write all results at the end (default)
|
||||
# batch-size: 0
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"runtime/pprof"
|
||||
"runtime/trace"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -103,26 +104,52 @@ func main() {
|
||||
return
|
||||
}
|
||||
|
||||
// Profiling related code
|
||||
// Profiling & tracing related code
|
||||
if memProfile != "" {
|
||||
f, err := os.Create(memProfile)
|
||||
memProfile = strings.TrimSuffix(memProfile, filepath.Ext(memProfile)) + ".prof"
|
||||
memProfileFile, err := os.Create(memProfile)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("profile: could not create memory profile %q: %v", memProfile, err)
|
||||
gologger.Fatal().Msgf("profile: could not create memory profile %q file: %v", memProfile, err)
|
||||
}
|
||||
old := runtime.MemProfileRate
|
||||
|
||||
traceFilepath := strings.TrimSuffix(memProfile, filepath.Ext(memProfile)) + ".trace"
|
||||
traceFile, err := os.Create(traceFilepath)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("profile: could not create trace %q file: %v", traceFilepath, err)
|
||||
}
|
||||
|
||||
oldMemProfileRate := runtime.MemProfileRate
|
||||
runtime.MemProfileRate = 4096
|
||||
gologger.Print().Msgf("profile: memory profiling enabled (rate %d), %s", runtime.MemProfileRate, memProfile)
|
||||
|
||||
// Start tracing
|
||||
if err := trace.Start(traceFile); err != nil {
|
||||
gologger.Fatal().Msgf("profile: could not start trace: %v", err)
|
||||
}
|
||||
|
||||
defer func() {
|
||||
_ = pprof.Lookup("heap").WriteTo(f, 0)
|
||||
f.Close()
|
||||
runtime.MemProfileRate = old
|
||||
gologger.Print().Msgf("profile: memory profiling disabled, %s", memProfile)
|
||||
// Start CPU profiling
|
||||
if err := pprof.WriteHeapProfile(memProfileFile); err != nil {
|
||||
gologger.Fatal().Msgf("profile: could not start CPU profile: %v", err)
|
||||
}
|
||||
memProfileFile.Close()
|
||||
traceFile.Close()
|
||||
trace.Stop()
|
||||
runtime.MemProfileRate = oldMemProfileRate
|
||||
|
||||
gologger.Info().Msgf("Memory profile saved at %q", memProfile)
|
||||
gologger.Info().Msgf("Traced at %q", traceFilepath)
|
||||
}()
|
||||
}
|
||||
|
||||
runner.ParseOptions(options)
|
||||
|
||||
if options.ScanUploadFile != "" {
|
||||
if err := runner.UploadResultsToCloud(options); err != nil {
|
||||
gologger.Fatal().Msgf("could not upload scan results to cloud dashboard: %s\n", err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
nucleiRunner, err := runner.New(options)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not create runner: %s\n", err)
|
||||
@@ -395,9 +422,10 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CallbackVar(printVersion, "version", "show nuclei version"),
|
||||
flagSet.BoolVarP(&options.HangMonitor, "hang-monitor", "hm", false, "enable nuclei hang monitoring"),
|
||||
flagSet.BoolVarP(&options.Verbose, "verbose", "v", false, "show verbose output"),
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "optional nuclei memory profile dump file"),
|
||||
flagSet.StringVar(&memProfile, "profile-mem", "", "generate memory (heap) profile & trace files"),
|
||||
flagSet.BoolVar(&options.VerboseVerbose, "vv", false, "display templates loaded for scan"),
|
||||
flagSet.BoolVarP(&options.ShowVarDump, "show-var-dump", "svd", false, "show variables dump for debugging"),
|
||||
flagSet.IntVarP(&options.VarDumpLimit, "var-dump-limit", "vdl", 255, "limit the number of characters displayed in var dump"),
|
||||
flagSet.BoolVarP(&options.EnablePprof, "enable-pprof", "ep", false, "enable pprof debugging server"),
|
||||
flagSet.CallbackVarP(printTemplateVersion, "templates-version", "tv", "shows the version of the installed nuclei-templates"),
|
||||
flagSet.BoolVarP(&options.HealthCheck, "health-check", "hc", false, "run diagnostic check up"),
|
||||
@@ -420,9 +448,11 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.CreateGroup("cloud", "Cloud",
|
||||
flagSet.DynamicVar(&pdcpauth, "auth", "true", "configure projectdiscovery cloud (pdcp) api key"),
|
||||
flagSet.StringVarP(&options.TeamID, "team-id", "tid", _pdcp.TeamIDEnv, "upload scan results to given team id (optional)"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard [DEPRECATED use -dashboard]"),
|
||||
flagSet.StringVarP(&options.ScanID, "scan-id", "sid", "", "upload scan results to existing scan id (optional)"),
|
||||
flagSet.StringVarP(&options.ScanName, "scan-name", "sname", "", "scan name to set (optional)"),
|
||||
flagSet.BoolVarP(&options.EnableCloudUpload, "dashboard", "pd", false, "upload / view nuclei results in projectdiscovery cloud (pdcp) UI dashboard"),
|
||||
flagSet.StringVarP(&options.ScanUploadFile, "dashboard-upload", "pdu", "", "upload / view nuclei results file (jsonl) in projectdiscovery cloud (pdcp) UI dashboard"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("Authentication", "Authentication",
|
||||
|
||||
@@ -21,9 +21,9 @@ WORKFLOW="Build Test"
|
||||
GREP_ERROR_PATTERN='Test "http/interactsh.yaml" failed'
|
||||
|
||||
#Set fonts for Help.
|
||||
NORM=`tput sgr0`
|
||||
BOLD=`tput bold`
|
||||
REV=`tput smso`
|
||||
NORM=$(tput sgr0)
|
||||
BOLD=$(tput bold)
|
||||
REV=$(tput smso)
|
||||
|
||||
HELP()
|
||||
{
|
||||
@@ -73,7 +73,7 @@ function print_bold() {
|
||||
function retry_failed_jobs() {
|
||||
print_bold "Checking failed workflows for branch $BRANCH before $BEFORE"
|
||||
|
||||
date=`date +%Y-%m-%d'T'%H:%M'Z' -d "$BEFORE"`
|
||||
date=$(date +%Y-%m-%d'T'%H:%M'Z' -d "$BEFORE")
|
||||
|
||||
workflowIds=$(gh run list --limit "$LIMIT" --json headBranch,status,name,conclusion,databaseId,updatedAt | jq -c '.[] |
|
||||
select ( .headBranch==$branch ) |
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
module github.com/projectdiscovery/nuclei/v3
|
||||
|
||||
go 1.21
|
||||
go 1.21.0
|
||||
|
||||
require (
|
||||
github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible
|
||||
@@ -19,13 +19,13 @@ require (
|
||||
github.com/miekg/dns v1.1.59
|
||||
github.com/olekukonko/tablewriter v0.0.5
|
||||
github.com/pkg/errors v0.9.1
|
||||
github.com/projectdiscovery/clistats v0.1.0
|
||||
github.com/projectdiscovery/fastdialer v0.2.7
|
||||
github.com/projectdiscovery/hmap v0.0.56
|
||||
github.com/projectdiscovery/clistats v0.1.1
|
||||
github.com/projectdiscovery/fastdialer v0.2.9
|
||||
github.com/projectdiscovery/hmap v0.0.62
|
||||
github.com/projectdiscovery/interactsh v1.2.0
|
||||
github.com/projectdiscovery/rawhttp v0.1.61
|
||||
github.com/projectdiscovery/retryabledns v1.0.73
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.76
|
||||
github.com/projectdiscovery/rawhttp v0.1.70
|
||||
github.com/projectdiscovery/retryabledns v1.0.80
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.82
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.5.0
|
||||
@@ -38,9 +38,9 @@ require (
|
||||
github.com/weppos/publicsuffix-go v0.30.2
|
||||
github.com/xanzy/go-gitlab v0.107.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/net v0.29.0
|
||||
golang.org/x/oauth2 v0.22.0
|
||||
golang.org/x/text v0.16.0
|
||||
golang.org/x/text v0.18.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
@@ -59,7 +59,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.11.72
|
||||
github.com/aws/aws-sdk-go-v2/service/s3 v1.37.0
|
||||
github.com/cespare/xxhash v1.1.0
|
||||
github.com/charmbracelet/glamour v0.6.0
|
||||
github.com/charmbracelet/glamour v0.8.0
|
||||
github.com/clbanning/mxj/v2 v2.7.0
|
||||
github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c
|
||||
github.com/docker/go-units v0.5.0
|
||||
@@ -72,7 +72,7 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.7.1
|
||||
github.com/h2non/filetype v1.1.3
|
||||
github.com/invopop/yaml v0.3.1
|
||||
github.com/kitabisa/go-ci v1.0.2
|
||||
github.com/kitabisa/go-ci v1.0.3
|
||||
github.com/labstack/echo/v4 v4.10.2
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa
|
||||
github.com/lib/pq v1.10.9
|
||||
@@ -81,31 +81,33 @@ require (
|
||||
github.com/microsoft/go-mssqldb v1.6.0
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/praetorian-inc/fingerprintx v1.1.9
|
||||
github.com/projectdiscovery/dsl v0.2.1
|
||||
github.com/projectdiscovery/dsl v0.3.0
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
|
||||
github.com/projectdiscovery/goflags v0.1.64
|
||||
github.com/projectdiscovery/gologger v1.1.21
|
||||
github.com/projectdiscovery/gologger v1.1.28
|
||||
github.com/projectdiscovery/gostruct v0.0.2
|
||||
github.com/projectdiscovery/gozero v0.0.2
|
||||
github.com/projectdiscovery/httpx v1.6.8
|
||||
github.com/projectdiscovery/gozero v0.0.3-0.20241023095525-626f55f7bb32
|
||||
github.com/projectdiscovery/httpx v1.6.9
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/ratelimit v0.0.53
|
||||
github.com/projectdiscovery/ratelimit v0.0.60
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.1.7
|
||||
github.com/projectdiscovery/uncover v1.0.9
|
||||
github.com/projectdiscovery/useragent v0.0.65
|
||||
github.com/projectdiscovery/utils v0.2.7
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.14
|
||||
github.com/projectdiscovery/useragent v0.0.71
|
||||
github.com/projectdiscovery/utils v0.2.15
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.22
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/seh-msft/burpxml v1.0.1
|
||||
github.com/shurcooL/graphql v0.0.0-20230722043721-ed46e5a46466
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
|
||||
github.com/yassinebenaid/godump v0.10.0
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706
|
||||
golang.org/x/term v0.21.0
|
||||
go.mongodb.org/mongo-driver v1.17.0
|
||||
golang.org/x/term v0.24.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
@@ -120,6 +122,7 @@ require (
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 // indirect
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/VividCortex/ewma v1.2.0 // indirect
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 // indirect
|
||||
github.com/andybalholm/brotli v1.1.0 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.27 // indirect
|
||||
@@ -134,6 +137,8 @@ require (
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.2.1 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.2.0 // indirect
|
||||
github.com/charmbracelet/lipgloss v0.13.0 // indirect
|
||||
github.com/charmbracelet/x/ansi v0.3.2 // indirect
|
||||
github.com/cheggaaa/pb/v3 v3.1.4 // indirect
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
|
||||
github.com/cloudflare/cfssl v1.6.4 // indirect
|
||||
@@ -142,7 +147,7 @@ require (
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
|
||||
github.com/dlclark/regexp2 v1.11.0 // indirect
|
||||
github.com/dlclark/regexp2 v1.11.4 // indirect
|
||||
github.com/docker/cli v24.0.5+incompatible // indirect
|
||||
github.com/docker/docker v24.0.9+incompatible // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
@@ -192,11 +197,12 @@ require (
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moby/term v0.5.0 // indirect
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 // indirect
|
||||
github.com/montanaflynn/stats v0.7.1 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/muesli/termenv v0.15.1 // indirect
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/runc v1.1.12 // indirect
|
||||
github.com/opencontainers/runc v1.1.14 // 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
|
||||
@@ -204,7 +210,7 @@ require (
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.1 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.1.0 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.5 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.7 // indirect
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
|
||||
github.com/refraction-networking/utls v1.6.7 // indirect
|
||||
@@ -225,18 +231,22 @@ require (
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
github.com/ugorji/go/codec v1.2.11 // indirect
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 // indirect
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
github.com/ysmood/fetchup v0.2.3 // indirect
|
||||
github.com/ysmood/got v0.34.1 // indirect
|
||||
github.com/yuin/goldmark v1.5.4 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.1 // indirect
|
||||
github.com/yuin/goldmark v1.7.4 // indirect
|
||||
github.com/yuin/goldmark-emoji v1.0.3 // indirect
|
||||
github.com/zcalusic/sysinfo v1.0.2 // indirect
|
||||
github.com/zeebo/blake3 v0.2.3 // indirect
|
||||
go.uber.org/goleak v1.3.0 // indirect
|
||||
golang.org/x/arch v0.3.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
@@ -278,16 +288,16 @@ require (
|
||||
github.com/lor00x/goldap v0.0.0-20180618054307-a546dffdd1a3 // indirect
|
||||
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.15 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mholt/acmez v1.2.0 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 // indirect
|
||||
github.com/microcosm-cc/bluemonday v1.0.27 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
|
||||
github.com/projectdiscovery/blackrock v0.0.1 // indirect
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9
|
||||
github.com/rivo/uniseg v0.4.6 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d // indirect
|
||||
github.com/tklauser/go-sysconf v0.3.12 // indirect
|
||||
github.com/tklauser/numcpus v0.6.1 // indirect
|
||||
@@ -303,10 +313,10 @@ require (
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
goftp.io/server/v2 v2.0.1 // indirect
|
||||
golang.org/x/crypto v0.24.0 // indirect
|
||||
golang.org/x/crypto v0.27.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842
|
||||
golang.org/x/mod v0.17.0 // indirect
|
||||
golang.org/x/sys v0.21.0 // indirect
|
||||
golang.org/x/sys v0.25.0 // indirect
|
||||
golang.org/x/time v0.6.0 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
|
||||
@@ -93,9 +93,15 @@ github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/
|
||||
github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c=
|
||||
github.com/akrylysov/pogreb v0.10.2 h1:e6PxmeyEhWyi2AKOBIJzAEi4HkiC+lKyCocRGlnDi78=
|
||||
github.com/akrylysov/pogreb v0.10.2/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
||||
github.com/alecthomas/assert/v2 v2.7.0 h1:QtqSACNS3tF7oasA8CU6A6sXZSBDqnm7RfpLl9bZqbE=
|
||||
github.com/alecthomas/assert/v2 v2.7.0/go.mod h1:Bze95FyfUr7x34QZrjL+XP+0qgp/zg8yS+TtBj1WA3k=
|
||||
github.com/alecthomas/chroma v0.10.0 h1:7XDcGkCQopCNKjZHfYrNLraA+M7e0fMiJ/Mfikbfjek=
|
||||
github.com/alecthomas/chroma v0.10.0/go.mod h1:jtJATyUxlIORhUOFNA9NZDWGAQ8wpxQQqNSB4rjA/1s=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0 h1:R3+wzpnUArGcQz7fCETQBzO5n9IMNi13iIs46aU4V9E=
|
||||
github.com/alecthomas/chroma/v2 v2.14.0/go.mod h1:QolEbTfmUHIMVpBqxeDnNBj2uoeI4EbYP4i6n68SG4I=
|
||||
github.com/alecthomas/kingpin/v2 v2.3.1/go.mod h1:oYL5vtsvEHZGHxU7DMp32Dvx+qL+ptGn6lWaot2vCNE=
|
||||
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
@@ -174,9 +180,10 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.19.3 h1:e5mnydVdCVWxP+5rPAGi2PYxC7u2
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.19.3/go.mod h1:yVGZA1CPkmUhBdA039jXNJJG7/6t+G+EBWmFq23xqnY=
|
||||
github.com/aws/smithy-go v1.13.5 h1:hgz0X/DX0dGqTYpGALqXJoRKRj5oQ7150i5FdTePzO8=
|
||||
github.com/aws/smithy-go v1.13.5/go.mod h1:Tg+OJXh4MB2R/uN61Ko2f6hTZwB/ZYGOtib8J3gBHzA=
|
||||
github.com/aymanbagabas/go-osc52 v1.0.3/go.mod h1:zT8H+Rk4VSabYN90pWyugflM3ZhpTZNC7cASDfUCdT4=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0 h1:TK0fH4MteXUDspT88n8CKzvK0X9O2xu9yQjWpi6yML8=
|
||||
github.com/aymanbagabas/go-udiff v0.2.0/go.mod h1:RE4Ex0qsGkTAJoQdQQCA0uG+nAzJO/pI/QwceO5fgrA=
|
||||
github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
|
||||
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=
|
||||
@@ -217,8 +224,14 @@ github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XL
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44=
|
||||
github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charmbracelet/glamour v0.6.0 h1:wi8fse3Y7nfcabbbDuwolqTqMQPMnVPeZhDM273bISc=
|
||||
github.com/charmbracelet/glamour v0.6.0/go.mod h1:taqWV4swIMMbWALc0m7AfE9JkPSU8om2538k9ITBxOc=
|
||||
github.com/charmbracelet/glamour v0.8.0 h1:tPrjL3aRcQbn++7t18wOpgLyl8wrOHUEDS7IZ68QtZs=
|
||||
github.com/charmbracelet/glamour v0.8.0/go.mod h1:ViRgmKkf3u5S7uakt2czJ272WSg2ZenlYEZXT2x7Bjw=
|
||||
github.com/charmbracelet/lipgloss v0.13.0 h1:4X3PPeoWEDCMvzDvGmTajSyYPcZM4+y8sCA/SsA3cjw=
|
||||
github.com/charmbracelet/lipgloss v0.13.0/go.mod h1:nw4zy0SBX/F/eAO1cWdcvy6qnkDUxr8Lw7dvFrAIbbY=
|
||||
github.com/charmbracelet/x/ansi v0.3.2 h1:wsEwgAN+C9U06l9dCVMX0/L3x7ptvY1qmjMwyfE6USY=
|
||||
github.com/charmbracelet/x/ansi v0.3.2/go.mod h1:dk73KoMTT5AX5BsX0KrqhsTqAnhZZoCBjs7dGWp4Ktw=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a h1:G99klV19u0QnhiizODirwVksQB91TJKV/UaTnACcG30=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20240806155701-69247e0abc2a/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4 h1:DN8j4TVVdKu3WxVwcRKu0sG00IIU6FewoABZzXbRQeo=
|
||||
github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA=
|
||||
github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY=
|
||||
@@ -271,8 +284,8 @@ github.com/ditashi/jsbeautifier-go v0.0.0-20141206144643-2520a8026a9c/go.mod h1:
|
||||
github.com/dlclark/regexp2 v1.4.0/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwuY/m+8K6iRw6wQdMtk+rH5tNGR1i55kozfMjCc=
|
||||
github.com/dlclark/regexp2 v1.7.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI=
|
||||
github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/dlclark/regexp2 v1.11.4 h1:rPYF9/LECdNymJufQKmri9gV604RvvABwgOA8un7yAo=
|
||||
github.com/dlclark/regexp2 v1.11.4/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=
|
||||
github.com/docker/cli v24.0.5+incompatible h1:WeBimjvS0eKdH4Ygx+ihVq1Q++xg36M/rMi4aXAvodc=
|
||||
github.com/docker/cli v24.0.5+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
|
||||
github.com/docker/docker v24.0.9+incompatible h1:HPGzNmwfLZWdxHqK9/II92pyi1EpYKsAqcl4G0Of9v0=
|
||||
@@ -508,7 +521,6 @@ github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5m
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg=
|
||||
github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
|
||||
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/mux v1.6.2/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs=
|
||||
@@ -559,6 +571,8 @@ github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf h1:umfGUaWdFP2s6
|
||||
github.com/hbakhtiyor/strsim v0.0.0-20190107154042-4d2bbb273edf/go.mod h1:V99KdStnMHZsvVOwIvhfcUzYgYkRZeQWUtumtL+SKxA=
|
||||
github.com/hdm/jarm-go v0.0.7 h1:Eq0geenHrBSYuKrdVhrBdMMzOmA+CAMLzN2WrF3eL6A=
|
||||
github.com/hdm/jarm-go v0.0.7/go.mod h1:kinGoS0+Sdn1Rr54OtanET5E5n7AlD6T6CrJAKDjJSQ=
|
||||
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/hudl/fargo v1.3.0/go.mod h1:y3CKSmjA+wD2gak7sUSXTAoopbhU08POFhmITJgmKTg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
@@ -625,8 +639,8 @@ github.com/kevinburke/ssh_config v1.2.0/go.mod h1:CT57kijsi8u/K/BOFA39wgDQJ9CxiF
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kitabisa/go-ci v1.0.2 h1:rqHf8KEbQOxVb998TbqGRo70Z7ol44io7/jLYJUvKp8=
|
||||
github.com/kitabisa/go-ci v1.0.2/go.mod h1:e3wBSzaJbcifXrr/Gw2ZBLn44MmeqP5WySwXyHlCK/U=
|
||||
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.11.4/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
@@ -695,9 +709,8 @@ github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D
|
||||
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/mattn/go-runewidth v0.0.12/go.mod h1:RAqKPSqVFrSLVXbA8x7dzmKdmGzieGRCM46jaSJTDAk=
|
||||
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.15 h1:UNAjwbU9l54TA3KzvqLGxwWjHmMgBUVhBiTjelZgg3U=
|
||||
github.com/mattn/go-runewidth v0.0.15/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
|
||||
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
@@ -708,9 +721,8 @@ github.com/mholt/archiver v3.1.1+incompatible h1:1dCVxuqs0dJseYEhi5pl7MYPH9zDa1w
|
||||
github.com/mholt/archiver v3.1.1+incompatible/go.mod h1:Dh2dOXnSdiLxRiPoVfIr/fI1TwETms9B8CTWfeh7ROU=
|
||||
github.com/mholt/archiver/v3 v3.5.1 h1:rDjOBX9JSF5BvoJGvjqK479aL70qh9DIpZCl+k7Clwo=
|
||||
github.com/mholt/archiver/v3 v3.5.1/go.mod h1:e3dqJ7H78uzsRSEACH1joayhuSyhnonssnDhppzS1L4=
|
||||
github.com/microcosm-cc/bluemonday v1.0.21/go.mod h1:ytNkv4RrDrLJ2pqlsSI46O6IVXmZOBBD4SaJyDwwTkM=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3rQ0k/Khz58=
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
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.6.0 h1:mM3gYdVwEPFrlg/Dvr2DNVEgYFG7L42l+dGc67NNNpc=
|
||||
github.com/microsoft/go-mssqldb v1.6.0/go.mod h1:00mDtPbeQCRGC1HwOOR5K/gr30P1NcEG0vx6Kbv2aJU=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
@@ -743,13 +755,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826 h1:RWengNIwukTxcDr9M+97sNutRR1RKhG96O6jWumTTnw=
|
||||
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/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=
|
||||
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
|
||||
github.com/muesli/termenv v0.13.0/go.mod h1:sP1+uffeLaEYpyOTb8pLCUctGcGLnoFjSn4YJK5e2bc=
|
||||
github.com/muesli/termenv v0.15.1 h1:UzuTb/+hhlBugQz28rpzey4ZuKcZ03MeKsoG7IJZIxs=
|
||||
github.com/muesli/termenv v0.15.1/go.mod h1:HeAQPTzpfs016yGtA4g00CsdYnVLJvxsS4ANqrZs2sQ=
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a h1:2MaM6YC3mGu54x+RKAA6JiFFHlHDY1UbkxqppT7wYOg=
|
||||
github.com/muesli/termenv v0.15.3-0.20240618155329-98d742f6907a/go.mod h1:hxSnBBYLK21Vtq/PHd0S2FYCxBXzBua8ov5s1RobyRQ=
|
||||
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/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
|
||||
@@ -781,8 +794,8 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
|
||||
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
|
||||
github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
|
||||
github.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=
|
||||
github.com/opencontainers/runc v1.1.12 h1:BOIssBaW1La0/qbNZHXOOa71dZfZEQOzW7dqQf3phss=
|
||||
github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8=
|
||||
github.com/opencontainers/runc v1.1.14 h1:rgSuzbmgz5DUJjeSnw337TxDbRuqjs6iqQck/2weR6w=
|
||||
github.com/opencontainers/runc v1.1.14/go.mod h1:E4C2z+7BxR7GHXp0hAY53mek+x49X1LjPNeMTfRGvOA=
|
||||
github.com/opentracing-contrib/go-observer v0.0.0-20170622124052-a52f23424492/go.mod h1:Ngi6UdF0k5OKD5t5wlmGhe/EDKPoUM3BXZSSfIuJbis=
|
||||
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
|
||||
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
@@ -830,30 +843,32 @@ github.com/projectdiscovery/blackrock v0.0.1 h1:lHQqhaaEFjgf5WkuItbpeCZv2DUIE45k
|
||||
github.com/projectdiscovery/blackrock v0.0.1/go.mod h1:ANUtjDfaVrqB453bzToU+YB4cUbvBRpLvEwoWIwlTss=
|
||||
github.com/projectdiscovery/cdncheck v1.1.0 h1:qDITidmJsejzpk3rMkauCh6sjI2GH9hW/snk0cQ3kXE=
|
||||
github.com/projectdiscovery/cdncheck v1.1.0/go.mod h1:sZ8U4MjHSsyaTVjBbYWHT1cwUVvUYwDX1W+WvWRicIc=
|
||||
github.com/projectdiscovery/clistats v0.1.0 h1:b+LF1w0xhNd7cneKWMXb+/yUmF1n5szawxP4XGpmbxs=
|
||||
github.com/projectdiscovery/clistats v0.1.0/go.mod h1:GJ2av0KnOvK0AISQnP8hyDclYIji1LVkx2l0pwnzAu4=
|
||||
github.com/projectdiscovery/dsl v0.2.1 h1:TK3KD4jsg4YbvY7WJqnz1QyH4AOvAwezeBFOX97Evgk=
|
||||
github.com/projectdiscovery/dsl v0.2.1/go.mod h1:IRQXsmi5/g1dDZ79//A9t2vrRtxm4frRSd5t8CZVSbI=
|
||||
github.com/projectdiscovery/fastdialer v0.2.7 h1:x7bEf/kDyVOuG9kHfJLMhWO3h7J+6fMDgLWkGYL8EFg=
|
||||
github.com/projectdiscovery/fastdialer v0.2.7/go.mod h1:7/BGdaw7sCekuBh6lLZqPuoO5N0Qaizwvex/PjO+V8o=
|
||||
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.3.0 h1:NzU6WcJ8GFQ676IVefrxC5nk/bvb7FCuhmp8tbNC9dQ=
|
||||
github.com/projectdiscovery/dsl v0.3.0/go.mod h1:GMVxLtQuQUc/RmSn3jQxGCI4yit3iyf2AoZxUoXbsb0=
|
||||
github.com/projectdiscovery/fastdialer v0.2.9 h1:vDCqxVMCyUu3oVEizEK1K8K+CCcLkVDW3X2HfiWaVFA=
|
||||
github.com/projectdiscovery/fastdialer v0.2.9/go.mod h1:mYv5QaNBDDSHlZO9DI0niRMw+G5hUzwIhs8QixSElUI=
|
||||
github.com/projectdiscovery/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.5 h1:jnd3Oqsl4S8n0KuFkE5Hm8WGDP24ITBvmyw5pFTHS8Q=
|
||||
github.com/projectdiscovery/freeport v0.0.5/go.mod h1:PY0bxSJ34HVy67LHIeF3uIutiCSDwOqKD8ruBkdiCwE=
|
||||
github.com/projectdiscovery/freeport v0.0.7 h1:Q6uXo/j8SaV/GlAHkEYQi8WQoPXyJWxyspx+aFmz9Qk=
|
||||
github.com/projectdiscovery/freeport v0.0.7/go.mod h1:cOhWKvNBe9xM6dFJ3RrrLvJ5vXx2NQ36SecuwjenV2k=
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb h1:rutG906Drtbpz4DwU5mhGIeOhRcktDH4cGQitGUMAsg=
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb/go.mod h1:FLjF1DmZ+POoGEiIQdWuYVwS++C/GwpX8YaCsTSm1RY=
|
||||
github.com/projectdiscovery/goflags v0.1.64 h1:FDfwdt9N97Hi8OuhbkDlKtVttpc/CRMIWQVa08VsHsI=
|
||||
github.com/projectdiscovery/goflags v0.1.64/go.mod h1:3FyHIVQtnycNOc1LE3O1jj/XR5XuMdF9QfHd0ujhnX4=
|
||||
github.com/projectdiscovery/gologger v1.1.21 h1:FD9MGE9adY/y2IxDpQgPZIvZAkQIx3bvcuY+d1FLQUo=
|
||||
github.com/projectdiscovery/gologger v1.1.21/go.mod h1:eyvlcHiSuzJQtwmh+fcfwZVaqVgSGtEP1oL1gJFgP3c=
|
||||
github.com/projectdiscovery/gologger v1.1.28 h1:3OYYnoEaHAYkPm2c2KgUifBDPy93JCQq1hl5ICtpJT8=
|
||||
github.com/projectdiscovery/gologger v1.1.28/go.mod h1:rB7xNi4tcx/HqpQu4QgpFIopIg8SNF/VBc6cTeQLqWQ=
|
||||
github.com/projectdiscovery/gostruct v0.0.2 h1:s8gP8ApugGM4go1pA+sVlPDXaWqNP5BBDDSv7VEdG1M=
|
||||
github.com/projectdiscovery/gostruct v0.0.2/go.mod h1:H86peL4HKwMXcQQtEa6lmC8FuD9XFt6gkNR0B/Mu5PE=
|
||||
github.com/projectdiscovery/gozero v0.0.2 h1:8fJeaCjxL9tpm33uG/RsCQs6HGM/NE6eA3cjkilRQ+E=
|
||||
github.com/projectdiscovery/gozero v0.0.2/go.mod h1:d8bZvDWW07LWNYWrwjZ4OO1I0cpkfqaysyDfSs9ibK8=
|
||||
github.com/projectdiscovery/hmap v0.0.56 h1:QLIuFhCc7jrBH9WYpxNBDooSoH+/S8cCm7VO5rMt3lU=
|
||||
github.com/projectdiscovery/hmap v0.0.56/go.mod h1:XyGKMO93nLOzpaTTCDBSKEGx/XWXjSpoyZJxT92Qd/Q=
|
||||
github.com/projectdiscovery/httpx v1.6.8 h1:k0Y5g3ue/7QbDP0+LykIxp/VhPDLfau3UEUyuxtP7qE=
|
||||
github.com/projectdiscovery/httpx v1.6.8/go.mod h1:7BIsDxyRwkBjthqFmEajXrA5f3yb4tlVfLmpNdf0ZXA=
|
||||
github.com/projectdiscovery/gozero v0.0.3-0.20241023095525-626f55f7bb32 h1:PubQkk+F/BiW4+wqBhWiMwPEKYxi/LdYXjxUtHd/tpM=
|
||||
github.com/projectdiscovery/gozero v0.0.3-0.20241023095525-626f55f7bb32/go.mod h1:d8bZvDWW07LWNYWrwjZ4OO1I0cpkfqaysyDfSs9ibK8=
|
||||
github.com/projectdiscovery/hmap v0.0.62 h1:Pb3omgGQkRHB2EBDhgXniUXVC8Jz2CBzpSMKZYRUKFs=
|
||||
github.com/projectdiscovery/hmap v0.0.62/go.mod h1:pNheW3ukrLOcRUDFWrq8OAZiizhosDonIKRVg/4PGnM=
|
||||
github.com/projectdiscovery/httpx v1.6.9 h1:ihyFclesLjvQpiJpRIlAYeebapyIbOI/arDAvvy1ES8=
|
||||
github.com/projectdiscovery/httpx v1.6.9/go.mod h1:zQtX5CtcDYXzIRWne1ztCVtqG0sXCnx84tFwfMHoB8Q=
|
||||
github.com/projectdiscovery/interactsh v1.2.0 h1:Al6jHiR+Usl9egYJDLJaWNHOcH8Rugk8gWMasc8Cmw8=
|
||||
github.com/projectdiscovery/interactsh v1.2.0/go.mod h1:Wxt0fnzxsfrAZQQlpVrf3xMatP4OXZaZbjuDkIQKdYY=
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb h1:MGtI4oE12ruWv11ZlPXXd7hl/uAaQZrFvrIDYDeVMd8=
|
||||
@@ -866,16 +881,16 @@ github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9 h1:IrlDoYZagNNO8y+7iZeHT8k5izE+nek7TdtvEBwCxqk=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9/go.mod h1:XFJ2Lnv8BE/ziQCFjBHMsH1w6VmkPiQtk+NlBpdMU7M=
|
||||
github.com/projectdiscovery/ratelimit v0.0.53 h1:1ci2zaVCtJ9daDi9IXjNwpDxl2InYq375role/xqUR4=
|
||||
github.com/projectdiscovery/ratelimit v0.0.53/go.mod h1:4kNYTL7xKLo1qMYHTREUCBqHyvykFF+XBWutTh9fkwg=
|
||||
github.com/projectdiscovery/rawhttp v0.1.61 h1:EbskCj6kkDSG31sO5zEUFTqHp9ltccG1DdcI+MCdahQ=
|
||||
github.com/projectdiscovery/rawhttp v0.1.61/go.mod h1:5XmDAKph9pLVnh87zjL+vXDpfG5W8Gz5N2BAtYTkuVU=
|
||||
github.com/projectdiscovery/ratelimit v0.0.60 h1:8nz8gVPPIH7by8ZZKY4LnsjwrlKPkQzJiTACILIdqvo=
|
||||
github.com/projectdiscovery/ratelimit v0.0.60/go.mod h1:ld9toJ1gj8E92pJz5/WFClEaoa6ke5xS62JIYMbctGk=
|
||||
github.com/projectdiscovery/rawhttp v0.1.70 h1:tzCns5KLL98qLYJwSSCdjT41ljX8G58lDrL20gL1YZ8=
|
||||
github.com/projectdiscovery/rawhttp v0.1.70/go.mod h1:fv/488Z6p5ifZ9hl/MWGYOoaDvpR7lV7IOMwIl8mATo=
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917 h1:m03X4gBVSorSzvmm0bFa7gDV4QNSOWPL/fgZ4kTXBxk=
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917/go.mod h1:JxXtZC9e195awe7EynrcnBJmFoad/BNDzW9mzFkK8Sg=
|
||||
github.com/projectdiscovery/retryabledns v1.0.73 h1:CLqtgoI6+YVcUE2jnZPFVvwj59CHDE4HNImLf62sSuw=
|
||||
github.com/projectdiscovery/retryabledns v1.0.73/go.mod h1:Wo5tQHKtx6XcYYhMypRg8dr0GGnqIb4CzoTSEw2lo7I=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.76 h1:2+4QpWnXUZFBkoeCEBXtUjSeBTdgKtaGwP8LRtKAC3g=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.76/go.mod h1:0qIv7Ld8WWSKWYXMsNAWRYU5gsxnxpBza3SjDgt6nVc=
|
||||
github.com/projectdiscovery/retryabledns v1.0.80 h1:P1oqWHZdF/IMtxXV4mrtENTNsghYojVlrNcv8dZAMu8=
|
||||
github.com/projectdiscovery/retryabledns v1.0.80/go.mod h1:BdCE4+Lph6hkNx+f+EjOnG+K8Z6shFWs9ZiTj6YcNvU=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.82 h1:XS+ZEKO291KGZVhHMO+RhvNvVE+IK8VHeM5i6CQ4Ixc=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.82/go.mod h1:JvlWId6aXT8en1ly22///wHoRQwSsMNAttdJ/YkhHHY=
|
||||
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=
|
||||
@@ -884,12 +899,12 @@ github.com/projectdiscovery/tlsx v1.1.7 h1:eSsl/SmTDL/z2CMeSrbssk4f/9oOotMP1SgXl
|
||||
github.com/projectdiscovery/tlsx v1.1.7/go.mod h1:g66QQ4/y4tLVjoGbzWIv+Q6xwFzxfJbEDx86Y1dYHDM=
|
||||
github.com/projectdiscovery/uncover v1.0.9 h1:s5RbkD/V4r8QcPkys4gTTqMuRSgXq0JprejqLSopN9Y=
|
||||
github.com/projectdiscovery/uncover v1.0.9/go.mod h1:2PUF3SpB5QNIJ8epaB2xbRzkPaxEAWRDm3Ir2ijt81U=
|
||||
github.com/projectdiscovery/useragent v0.0.65 h1:x78ZwWdqpzokOHxLITUXvq+ljkTKc19z3ILGtoV1N70=
|
||||
github.com/projectdiscovery/useragent v0.0.65/go.mod h1:deOP8YLJU6SCzM8k+K8PjkcOF4Ux0spqyO4ODZGIT4A=
|
||||
github.com/projectdiscovery/utils v0.2.7 h1:XWdz7SscL++jqsnQ9ecHzSZE0RK33tyPcnqcXw+vmKs=
|
||||
github.com/projectdiscovery/utils v0.2.7/go.mod h1:N0N7tbdNFPegd9NpJ3onCPClaBrERcOIB88yww6UCF8=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.14 h1:nt1IM4RUmqeymsXk4h6BsZbKDoS2hjFvPkT2GaI1rz4=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.14/go.mod h1:/hzgxkBFTMe2wDbA93nFfoMjULw7/vIZ9QPSAnCgUa8=
|
||||
github.com/projectdiscovery/useragent v0.0.71 h1:Q02L3LV15ztOQ6FfmVSqVmOd5QhvzI+yAgYOc/32Nvg=
|
||||
github.com/projectdiscovery/useragent v0.0.71/go.mod h1:DHPruFLCvCvkd2qqPwwQZrP9sziv0lxQJ0R1rE1fa8E=
|
||||
github.com/projectdiscovery/utils v0.2.15 h1:FO3n7uhLazHtat3qBxWIq0JCfLoR/IvP5JXAOSKaQ5w=
|
||||
github.com/projectdiscovery/utils v0.2.15/go.mod h1:2NAFRu8j/82bkVqx2TcsZFdgtUOnHEUi7u6s3lv79Lo=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.22 h1:7QnpeZy/txsvVcQNlL1n4xIu8IXgMxEsda3w6k6bShY=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.22/go.mod h1:wnvmbC10pQTOoCKnCTmWKP20rpEtqrMJZvzuTuleeyw=
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4 h1:eZoESapnMw6WAHiVgRwNqvbJEfNHEH148uthhFbG5jE=
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4/go.mod h1:8PIPRcUD55UbtQdcfFR1hpIGRWG0P7alClXNGt1TBik=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
@@ -933,8 +948,8 @@ github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7
|
||||
github.com/remeh/sizedwaitgroup v1.0.0/go.mod h1:3j2R4OIe/SeS6YDhICBy22RWjJC5eNCJ1V+9+NVNYlo=
|
||||
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
|
||||
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
@@ -1074,6 +1089,12 @@ github.com/xanzy/go-gitlab v0.107.0 h1:P2CT9Uy9yN9lJo3FLxpMZ4xj6uWcpnigXsjvqJ6nd
|
||||
github.com/xanzy/go-gitlab v0.107.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
|
||||
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
|
||||
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c=
|
||||
github.com/xdg-go/pbkdf2 v1.0.0/go.mod h1:jrpuAogTd400dnrH08LKmI/xc1MbPOebTwRqcT5RDeI=
|
||||
github.com/xdg-go/scram v1.1.2 h1:FHX5I5B4i4hKRVRBCFRxq1iQRej7WO3hhBuJf+UUySY=
|
||||
github.com/xdg-go/scram v1.1.2/go.mod h1:RT/sEzTbU5y00aCK8UOx6R7YryM0iF1N2MOmC3kKLN4=
|
||||
github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8=
|
||||
github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
@@ -1085,8 +1106,12 @@ github.com/xhit/go-str2duration v1.2.0/go.mod h1:3cPSlfZlUHVlneIVfePFWcJZsuwf+P1
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
|
||||
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yassinebenaid/godump v0.10.0 h1:FolBA+Ix5uwUiXYBBYOsf1VkT5+0f4gtFNTkYTiIR08=
|
||||
github.com/yassinebenaid/godump v0.10.0/go.mod h1:dc/0w8wmg6kVIvNGAzbKH1Oa54dXQx8SNKh4dPRyW44=
|
||||
github.com/yl2chen/cidranger v1.0.2 h1:lbOWZVCG1tCRX4u24kuM1Tb4nHqWkDxwLdoS+SevawU=
|
||||
github.com/yl2chen/cidranger v1.0.2/go.mod h1:9U1yz7WPYDwf0vpNWFaeRh0bjwz5RVgRy/9UEQfHl0g=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM=
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI=
|
||||
github.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=
|
||||
github.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=
|
||||
github.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=
|
||||
@@ -1106,11 +1131,11 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
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.5.2/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark v1.5.4 h1:2uY/xC0roWy8IBEGLgB1ywIoEJFGmRrX21YQcvGZzjU=
|
||||
github.com/yuin/goldmark v1.5.4/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
github.com/yuin/goldmark-emoji v1.0.1 h1:ctuWEyzGBwiucEqxzwe0SOYDXPAucOrE9NQC18Wa1os=
|
||||
github.com/yuin/goldmark-emoji v1.0.1/go.mod h1:2w1E6FEWLcDQkoTE+7HU6QF1F6SLlNGjRIBbIZQFqkQ=
|
||||
github.com/yuin/goldmark v1.7.1/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark v1.7.4 h1:BDXOHExt+A7gwPCJgPIIq7ENvceR7we7rOS9TNoLZeg=
|
||||
github.com/yuin/goldmark v1.7.4/go.mod h1:uzxRWxtg69N339t3louHJ7+O03ezfj6PlliRlaOzY1E=
|
||||
github.com/yuin/goldmark-emoji v1.0.3 h1:aLRkLHOuBR2czCY4R8olwMjID+tENfhyFDMCRhbIQY4=
|
||||
github.com/yuin/goldmark-emoji v1.0.3/go.mod h1:tTkZEbwu5wkPmgTcitqddVxY9osFZiavD+r4AzQrh1U=
|
||||
github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0=
|
||||
github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
|
||||
github.com/zcalusic/sysinfo v1.0.2 h1:nwTTo2a+WQ0NXwo0BGRojOJvJ/5XKvQih+2RrtWqfxc=
|
||||
@@ -1139,6 +1164,8 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
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/etcd v0.0.0-20191023171146-3cf2f69b5738/go.mod h1:dnLIgRNXwCJa5e+c6mIZCrds/GIG4ncV9HhK5PX7jPg=
|
||||
go.mongodb.org/mongo-driver v1.17.0 h1:Hp4q2MCjvY19ViwimTs00wHi7G4yzxh4/2+nTx8r40k=
|
||||
go.mongodb.org/mongo-driver v1.17.0/go.mod h1:wwWm/+BuOddhcq3n68LKRmgk2wXzmF6s0SFOa0GINL4=
|
||||
go.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
@@ -1187,8 +1214,8 @@ golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU
|
||||
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
|
||||
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
|
||||
golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
|
||||
golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI=
|
||||
golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM=
|
||||
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
|
||||
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
|
||||
golang.org/x/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=
|
||||
@@ -1272,7 +1299,6 @@ golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.0.0-20221002022538-bcab6841153b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
@@ -1282,8 +1308,8 @@ golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.17.0/go.mod h1:NxSsAGuq816PNPmqtQdLE42eU2Fs7NoRIZrHJAlaCOE=
|
||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||
golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
|
||||
golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ=
|
||||
golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE=
|
||||
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
|
||||
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
|
||||
golang.org/x/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=
|
||||
@@ -1308,8 +1334,8 @@ golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJ
|
||||
golang.org/x/sync v0.0.0-20220601150217-0de741cfad7f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
@@ -1372,7 +1398,6 @@ golang.org/x/sys v0.0.0-20220330033206-e17cdc41300f/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1385,8 +1410,8 @@ golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws=
|
||||
golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
|
||||
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
@@ -1399,8 +1424,8 @@ golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
|
||||
golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
|
||||
golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
|
||||
golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA=
|
||||
golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0=
|
||||
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
|
||||
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
|
||||
golang.org/x/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=
|
||||
@@ -1417,8 +1442,8 @@ golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||
golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4=
|
||||
golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
id: multimatch-value-share-template
|
||||
|
||||
info:
|
||||
name: MultiMatch Value Share Template
|
||||
author: tovask
|
||||
severity: info
|
||||
|
||||
http:
|
||||
- path:
|
||||
- "{{BaseURL}}/path1?v=1"
|
||||
- "{{BaseURL}}/path1?v=2"
|
||||
matchers:
|
||||
- type: word
|
||||
name: test-matcher
|
||||
words:
|
||||
- "href"
|
||||
extractors:
|
||||
- type: regex
|
||||
part: body
|
||||
name: extracted
|
||||
regex:
|
||||
- 'href="(.*)"'
|
||||
group: 1
|
||||
@@ -0,0 +1,21 @@
|
||||
id: multimatch-value-share-workflow
|
||||
|
||||
info:
|
||||
name: MultiMatch Value Share Workflow
|
||||
author: tovask
|
||||
severity: info
|
||||
description: Workflow to test value sharing when multiple matches occur in the extractor template
|
||||
|
||||
workflows:
|
||||
- template: workflow/multimatch-value-share-template.yaml
|
||||
subtemplates:
|
||||
- template: workflow/match-1.yaml
|
||||
subtemplates:
|
||||
- template: workflow/http-value-share-template-2.yaml
|
||||
- template: workflow/multimatch-value-share-template.yaml
|
||||
matchers:
|
||||
- name: test-matcher
|
||||
subtemplates:
|
||||
- template: workflow/match-1.yaml
|
||||
subtemplates:
|
||||
- template: workflow/http-value-share-template-2.yaml
|
||||
@@ -51,6 +51,7 @@ func GetAuthTmplStore(opts types.Options, catalog catalog.Catalog, execOpts prot
|
||||
opts.ExcludeProtocols = nil
|
||||
opts.IncludeConditions = nil
|
||||
cfg := loader.NewConfig(&opts, catalog, execOpts)
|
||||
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")
|
||||
|
||||
@@ -68,6 +68,7 @@ func ParseOptions(options *types.Options) {
|
||||
|
||||
if options.ShowVarDump {
|
||||
vardump.EnableVarDump = true
|
||||
vardump.Limit = options.VarDumpLimit
|
||||
}
|
||||
if options.ShowActions {
|
||||
gologger.Info().Msgf("Showing available headless actions: ")
|
||||
|
||||
@@ -48,6 +48,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/automaticscan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
|
||||
@@ -475,6 +476,7 @@ func (r *Runner) RunEnumeration() error {
|
||||
TemporaryDirectory: r.tmpDir,
|
||||
Parser: r.parser,
|
||||
FuzzParamsFrequency: fuzzFreqCache,
|
||||
GlobalMatchers: globalmatchers.New(),
|
||||
}
|
||||
|
||||
if config.DefaultConfig.IsDebugArgEnabled(config.DebugExportURLPattern) {
|
||||
@@ -501,8 +503,17 @@ func (r *Runner) RunEnumeration() error {
|
||||
}
|
||||
|
||||
if r.options.ShouldUseHostError() {
|
||||
cache := hosterrorscache.New(r.options.MaxHostError, hosterrorscache.DefaultMaxHostsCount, r.options.TrackError)
|
||||
maxHostError := r.options.MaxHostError
|
||||
if r.options.TemplateThreads > maxHostError {
|
||||
gologger.Print().Msgf("[%v] The concurrency value is higher than max-host-error", r.colorizer.BrightYellow("WRN"))
|
||||
gologger.Info().Msgf("Adjusting max-host-error to the concurrency value: %d", r.options.TemplateThreads)
|
||||
|
||||
maxHostError = r.options.TemplateThreads
|
||||
}
|
||||
|
||||
cache := hosterrorscache.New(maxHostError, hosterrorscache.DefaultMaxHostsCount, r.options.TrackError)
|
||||
cache.SetVerbose(r.options.Verbose)
|
||||
|
||||
r.hostErrors = cache
|
||||
executorOpts.HostErrorsCache = cache
|
||||
}
|
||||
@@ -784,6 +795,52 @@ func (r *Runner) SaveResumeConfig(path string) error {
|
||||
return os.WriteFile(path, data, permissionutil.ConfigFilePermission)
|
||||
}
|
||||
|
||||
// upload existing scan results to cloud with progress
|
||||
func UploadResultsToCloud(options *types.Options) error {
|
||||
h := &pdcpauth.PDCPCredHandler{}
|
||||
creds, err := h.GetCreds()
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get credentials for cloud upload")
|
||||
}
|
||||
ctx := context.TODO()
|
||||
uploadWriter, err := pdcp.NewUploadWriter(ctx, creds)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not create upload writer")
|
||||
}
|
||||
if options.ScanID != "" {
|
||||
_ = uploadWriter.SetScanID(options.ScanID)
|
||||
}
|
||||
if options.ScanName != "" {
|
||||
uploadWriter.SetScanName(options.ScanName)
|
||||
}
|
||||
if options.TeamID != "" {
|
||||
uploadWriter.SetTeamID(options.TeamID)
|
||||
}
|
||||
|
||||
// Open file to count the number of results first
|
||||
file, err := os.Open(options.ScanUploadFile)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not open scan upload file")
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
gologger.Info().Msgf("Uploading scan results to cloud dashboard from %s", options.ScanUploadFile)
|
||||
dec := json.NewDecoder(file)
|
||||
for dec.More() {
|
||||
var r output.ResultEvent
|
||||
err := dec.Decode(&r)
|
||||
if err != nil {
|
||||
gologger.Warning().Msgf("Could not decode jsonl: %s\n", err)
|
||||
continue
|
||||
}
|
||||
if err = uploadWriter.Write(&r); err != nil {
|
||||
gologger.Warning().Msgf("[%s] failed to upload: %s\n", r.TemplateID, err)
|
||||
}
|
||||
}
|
||||
uploadWriter.Close()
|
||||
return nil
|
||||
}
|
||||
|
||||
type WalkFunc func(reflect.Value, reflect.StructField)
|
||||
|
||||
// Walk traverses a struct and executes a callback function on each value in the struct.
|
||||
|
||||
@@ -100,6 +100,7 @@ func (e *NucleiEngine) LoadAllTemplates() error {
|
||||
return errorutil.New("Could not create loader client: %s\n", err)
|
||||
}
|
||||
e.store.Load()
|
||||
e.templatesLoaded = true
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ package nuclei
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@@ -108,14 +109,6 @@ func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if e.opts.ProxyInternal && types.ProxyURL != "" || types.ProxySocksURL != "" {
|
||||
httpclient, err := httpclientpool.Get(e.opts, &httpclientpool.Configuration{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.httpClient = httpclient
|
||||
}
|
||||
|
||||
e.parser = templates.NewParser()
|
||||
|
||||
if sharedInit == nil || protocolstate.ShouldInit() {
|
||||
@@ -126,6 +119,14 @@ func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
_ = protocolinit.Init(e.opts)
|
||||
})
|
||||
|
||||
if e.opts.ProxyInternal && types.ProxyURL != "" || types.ProxySocksURL != "" {
|
||||
httpclient, err := httpclientpool.Get(e.opts, &httpclientpool.Configuration{})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
e.httpClient = httpclient
|
||||
}
|
||||
|
||||
e.applyRequiredDefaults(ctx)
|
||||
var err error
|
||||
|
||||
@@ -171,6 +172,7 @@ func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
ResumeCfg: types.NewResumeCfg(),
|
||||
Browser: e.browserInstance,
|
||||
Parser: e.parser,
|
||||
InputHelper: input.NewHelper(),
|
||||
}
|
||||
if len(e.opts.SecretsFile) > 0 {
|
||||
authTmplStore, err := runner.GetAuthTmplStore(*e.opts, e.catalog, e.executerOpts)
|
||||
|
||||
@@ -247,7 +247,12 @@
|
||||
"time",
|
||||
"select",
|
||||
"files",
|
||||
"waitdom",
|
||||
"waitfcp",
|
||||
"waitfmp",
|
||||
"waitidle",
|
||||
"waitload",
|
||||
"waitstable",
|
||||
"getresource",
|
||||
"extract",
|
||||
"setmethod",
|
||||
@@ -884,6 +889,11 @@
|
||||
],
|
||||
"title": "condition between the filters",
|
||||
"description": "Operator to use between multiple per-conditions"
|
||||
},
|
||||
"global-matchers": {
|
||||
"type": "boolean",
|
||||
"title": "global matchers",
|
||||
"description": "marks matchers as static and applies globally to all result events from other templates"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
@@ -1360,6 +1370,11 @@
|
||||
"title": "read all response stream",
|
||||
"description": "Read all response stream till the server stops sending"
|
||||
},
|
||||
"stop-at-first-match": {
|
||||
"type": "boolean",
|
||||
"title": "stop at first match",
|
||||
"description": "Stop the execution after a match is found"
|
||||
},
|
||||
"matchers": {
|
||||
"items": {
|
||||
"$ref": "#/$defs/matchers.Matcher"
|
||||
|
||||
@@ -31,7 +31,7 @@ const (
|
||||
CLIConfigFileName = "config.yaml"
|
||||
ReportingConfigFilename = "reporting-config.yaml"
|
||||
// Version is the current version of nuclei
|
||||
Version = `v3.3.2`
|
||||
Version = `v3.3.5`
|
||||
// Directory Names of custom templates
|
||||
CustomS3TemplatesDirName = "s3"
|
||||
CustomGitHubTemplatesDirName = "github"
|
||||
|
||||
@@ -216,12 +216,17 @@ func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]s
|
||||
// is a file, it returns true otherwise false with no errors.
|
||||
func (c *DiskCatalog) findFileMatches(absPath string, processed map[string]struct{}) (match string, matched bool, err error) {
|
||||
if c.templatesFS != nil {
|
||||
absPath = strings.TrimPrefix(absPath, "/")
|
||||
absPath = strings.Trim(absPath, "/")
|
||||
}
|
||||
var info fs.File
|
||||
if c.templatesFS == nil {
|
||||
info, err = os.Open(absPath)
|
||||
} else {
|
||||
// If we were given no path, then it's not a file, it's the root, and we can quietly return.
|
||||
if absPath == "" {
|
||||
return "", false, nil
|
||||
}
|
||||
|
||||
info, err = c.templatesFS.Open(absPath)
|
||||
}
|
||||
if err != nil {
|
||||
@@ -263,6 +268,12 @@ func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]
|
||||
},
|
||||
)
|
||||
} else {
|
||||
// For the special case of the root directory, we need to pass "." to `fs.WalkDir`.
|
||||
if absPath == "" {
|
||||
absPath = "."
|
||||
}
|
||||
absPath = strings.TrimSuffix(absPath, "/")
|
||||
|
||||
err = fs.WalkDir(
|
||||
c.templatesFS,
|
||||
absPath,
|
||||
|
||||
@@ -2,6 +2,7 @@ package disk
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -21,6 +22,11 @@ func (c *DiskCatalog) ResolvePath(templateName, second string) (string, error) {
|
||||
if filepath.IsAbs(templateName) {
|
||||
return templateName, nil
|
||||
}
|
||||
if c.templatesFS != nil {
|
||||
if potentialPath, err := c.tryResolve(templateName); err != errNoValidCombination {
|
||||
return potentialPath, nil
|
||||
}
|
||||
}
|
||||
if second != "" {
|
||||
secondBasePath := filepath.Join(filepath.Dir(second), templateName)
|
||||
if potentialPath, err := c.tryResolve(secondBasePath); err != errNoValidCombination {
|
||||
@@ -28,17 +34,19 @@ func (c *DiskCatalog) ResolvePath(templateName, second string) (string, error) {
|
||||
}
|
||||
}
|
||||
|
||||
curDirectory, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
if c.templatesFS == nil {
|
||||
curDirectory, err := os.Getwd()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
templatePath := filepath.Join(curDirectory, templateName)
|
||||
if potentialPath, err := c.tryResolve(templatePath); err != errNoValidCombination {
|
||||
return potentialPath, nil
|
||||
}
|
||||
}
|
||||
|
||||
templatePath := filepath.Join(curDirectory, templateName)
|
||||
if potentialPath, err := c.tryResolve(templatePath); err != errNoValidCombination {
|
||||
return potentialPath, nil
|
||||
}
|
||||
|
||||
templatePath = filepath.Join(config.DefaultConfig.GetTemplateDir(), templateName)
|
||||
templatePath := filepath.Join(config.DefaultConfig.GetTemplateDir(), templateName)
|
||||
if potentialPath, err := c.tryResolve(templatePath); err != errNoValidCombination {
|
||||
return potentialPath, nil
|
||||
}
|
||||
@@ -50,8 +58,14 @@ var errNoValidCombination = errors.New("no valid combination found")
|
||||
|
||||
// tryResolve attempts to load locate the target by iterating across all the folders tree
|
||||
func (c *DiskCatalog) tryResolve(fullPath string) (string, error) {
|
||||
if fileutil.FileOrFolderExists(fullPath) {
|
||||
return fullPath, nil
|
||||
if c.templatesFS == nil {
|
||||
if fileutil.FileOrFolderExists(fullPath) {
|
||||
return fullPath, nil
|
||||
}
|
||||
} else {
|
||||
if _, err := fs.Stat(c.templatesFS, fullPath); err == nil {
|
||||
return fullPath, nil
|
||||
}
|
||||
}
|
||||
return "", errNoValidCombination
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ import (
|
||||
"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/keys"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
@@ -32,6 +33,7 @@ import (
|
||||
const (
|
||||
httpPrefix = "http://"
|
||||
httpsPrefix = "https://"
|
||||
AuthStoreId = "auth_store"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -40,6 +42,7 @@ var (
|
||||
|
||||
// Config contains the configuration options for the loader
|
||||
type Config struct {
|
||||
StoreId string // used to set store id (optional)
|
||||
Templates []string
|
||||
TemplateURLs []string
|
||||
Workflows []string
|
||||
@@ -66,6 +69,7 @@ type Config struct {
|
||||
|
||||
// Store is a storage for loaded nuclei templates
|
||||
type Store struct {
|
||||
id string // id of the store (optional)
|
||||
tagFilter *templates.TagFilter
|
||||
pathFilter *filter.PathFilter
|
||||
config *Config
|
||||
@@ -131,6 +135,7 @@ func New(cfg *Config) (*Store, error) {
|
||||
|
||||
// Create a tag filter based on provided configuration
|
||||
store := &Store{
|
||||
id: cfg.StoreId,
|
||||
config: cfg,
|
||||
tagFilter: tagFilter,
|
||||
pathFilter: filter.NewPathFilter(&filter.PathFilterConfig{
|
||||
@@ -229,6 +234,10 @@ func (store *Store) ReadTemplateFromURI(uri string, remote bool) ([]byte, error)
|
||||
}
|
||||
}
|
||||
|
||||
func (store *Store) ID() string {
|
||||
return store.id
|
||||
}
|
||||
|
||||
// Templates returns all the templates in the store
|
||||
func (store *Store) Templates() []*templates.Template {
|
||||
return store.templates
|
||||
@@ -344,6 +353,20 @@ func (store *Store) areWorkflowOrTemplatesValid(filteredTemplatePaths map[string
|
||||
if isParsingError("Error occurred parsing template %s: %s\n", templatePath, err) {
|
||||
areTemplatesValid = false
|
||||
}
|
||||
} else if template == nil {
|
||||
// NOTE(dwisiswant0): possibly global matchers template.
|
||||
// This could definitely be handled better, for example by returning an
|
||||
// `ErrGlobalMatchersTemplate` during `templates.Parse` and checking it
|
||||
// with `errors.Is`.
|
||||
//
|
||||
// However, I’m not sure if every reference to it should be handled
|
||||
// that way. Returning a `templates.Template` pointer would mean it’s
|
||||
// an active template (sending requests), and adding a specific field
|
||||
// like `isGlobalMatchers` in `templates.Template` (then checking it
|
||||
// with a `*templates.Template.IsGlobalMatchersEnabled` method) would
|
||||
// just introduce more unknown issues - like during template
|
||||
// clustering, AFAIK.
|
||||
continue
|
||||
} else {
|
||||
if existingTemplatePath, found := templateIDPathMap[template.ID]; !found {
|
||||
templateIDPathMap[template.ID] = templatePath
|
||||
@@ -434,7 +457,7 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
// increment signed/unsigned counters
|
||||
if tmpl.Verified {
|
||||
if tmpl.TemplateVerifier == "" {
|
||||
templates.SignatureStats[templates.PDVerifier].Add(1)
|
||||
templates.SignatureStats[keys.PDVerifier].Add(1)
|
||||
} else {
|
||||
templates.SignatureStats[tmpl.TemplateVerifier].Add(1)
|
||||
}
|
||||
@@ -471,7 +494,8 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
return
|
||||
}
|
||||
// DAST only templates
|
||||
if store.config.ExecutorOptions.Options.DAST {
|
||||
// Skip DAST filter when loading auth templates
|
||||
if store.ID() != AuthStoreId && store.config.ExecutorOptions.Options.DAST {
|
||||
// check if the template is a DAST template
|
||||
if parsed.IsFuzzing() {
|
||||
loadTemplate(parsed)
|
||||
|
||||
@@ -107,7 +107,7 @@ func (e *Engine) executeTemplateWithTargets(ctx context.Context, template *templ
|
||||
currentInfo.Unlock()
|
||||
|
||||
// Skip if the host has had errors
|
||||
if e.executerOpts.HostErrorsCache != nil && e.executerOpts.HostErrorsCache.Check(contextargs.NewWithMetaInput(ctx, scannedValue)) {
|
||||
if e.executerOpts.HostErrorsCache != nil && e.executerOpts.HostErrorsCache.Check(e.executerOpts.ProtocolType.String(), contextargs.NewWithMetaInput(ctx, scannedValue)) {
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,7 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, ctx *scan
|
||||
}
|
||||
if err != nil {
|
||||
if w.Options.HostErrorsCache != nil {
|
||||
w.Options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
w.Options.HostErrorsCache.MarkFailed(w.Options.ProtocolType.String(), ctx.Input, err)
|
||||
}
|
||||
if len(template.Executers) == 1 {
|
||||
mainErr = err
|
||||
@@ -139,7 +139,8 @@ func (e *Engine) runWorkflowStep(template *workflows.WorkflowTemplate, ctx *scan
|
||||
defer swg.Done()
|
||||
|
||||
// create a new context with the same input but with unset callbacks
|
||||
subCtx := scan.NewScanContext(ctx.Context(), ctx.Input)
|
||||
// clone the Input so that other parallel executions won't overwrite the shared variables when subsequent templates are running
|
||||
subCtx := scan.NewScanContext(ctx.Context(), ctx.Input.Clone())
|
||||
if err := e.runWorkflowStep(subtemplate, subCtx, results, swg, w); err != nil {
|
||||
gologger.Warning().Msgf(workflowStepExecutionError, subtemplate.Template, err)
|
||||
}
|
||||
|
||||
@@ -6,12 +6,19 @@ import (
|
||||
"io"
|
||||
"mime"
|
||||
"mime/multipart"
|
||||
"net/textproto"
|
||||
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
)
|
||||
|
||||
type MultiPartForm struct {
|
||||
boundary string
|
||||
boundary string
|
||||
filesMetadata map[string]FileMetadata
|
||||
}
|
||||
|
||||
type FileMetadata struct {
|
||||
ContentType string
|
||||
Filename string
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -41,11 +48,40 @@ func (m *MultiPartForm) Encode(data KV) (string, error) {
|
||||
data.Iterate(func(key string, value any) bool {
|
||||
var fw io.Writer
|
||||
var err error
|
||||
|
||||
if filesArray, ok := value.([]interface{}); ok {
|
||||
fileMetadata, ok := m.filesMetadata[key]
|
||||
if !ok {
|
||||
Itererr = fmt.Errorf("file metadata not found for key %s", key)
|
||||
return false
|
||||
}
|
||||
|
||||
for _, file := range filesArray {
|
||||
h := make(textproto.MIMEHeader)
|
||||
h.Set("Content-Disposition",
|
||||
fmt.Sprintf(`form-data; name=%q; filename=%q`,
|
||||
key, fileMetadata.Filename))
|
||||
h.Set("Content-Type", fileMetadata.ContentType)
|
||||
|
||||
if fw, err = w.CreatePart(h); err != nil {
|
||||
Itererr = err
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err = fw.Write([]byte(file.(string))); err != nil {
|
||||
Itererr = err
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Add field
|
||||
if fw, err = w.CreateFormField(key); err != nil {
|
||||
Itererr = err
|
||||
return false
|
||||
}
|
||||
|
||||
if _, err = fw.Write([]byte(value.(string))); err != nil {
|
||||
Itererr = err
|
||||
return false
|
||||
@@ -98,6 +134,7 @@ func (m *MultiPartForm) Decode(data string) (KV, error) {
|
||||
result.Set(key, values[0])
|
||||
}
|
||||
}
|
||||
m.filesMetadata = make(map[string]FileMetadata)
|
||||
for key, files := range form.File {
|
||||
fileContents := []interface{}{}
|
||||
for _, fileHeader := range files {
|
||||
@@ -112,6 +149,11 @@ func (m *MultiPartForm) Decode(data string) (KV, error) {
|
||||
return KV{}, err
|
||||
}
|
||||
fileContents = append(fileContents, buffer.String())
|
||||
|
||||
m.filesMetadata[key] = FileMetadata{
|
||||
ContentType: fileHeader.Header.Get("Content-Type"),
|
||||
Filename: fileHeader.Filename,
|
||||
}
|
||||
}
|
||||
result.Set(key, fileContents)
|
||||
}
|
||||
|
||||
@@ -27,7 +27,8 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
globalAuth = "globalAuth"
|
||||
globalAuth = "globalAuth"
|
||||
DEFAULT_HTTP_SCHEME_HEADER = "Authorization"
|
||||
)
|
||||
|
||||
// GenerateRequestsFromSchema generates http requests from an OpenAPI 3.0 document object
|
||||
@@ -426,18 +427,20 @@ func GenerateParameterFromSecurityScheme(scheme *openapi3.SecuritySchemeRef) (*o
|
||||
if !generic.EqualsAny(scheme.Value.Scheme, "basic", "bearer") {
|
||||
return nil, errorutil.NewWithTag("openapi", "unsupported security scheme (%s) found in openapi file", scheme.Value.Scheme)
|
||||
}
|
||||
if scheme.Value.Name == "" {
|
||||
return nil, errorutil.NewWithTag("openapi", "security scheme (%s) name is empty", scheme.Value.Scheme)
|
||||
// HTTP authentication schemes basic or bearer use the Authorization header
|
||||
headerName := scheme.Value.Name
|
||||
if headerName == "" {
|
||||
headerName = DEFAULT_HTTP_SCHEME_HEADER
|
||||
}
|
||||
// create parameters using the scheme
|
||||
switch scheme.Value.Scheme {
|
||||
case "basic":
|
||||
h := openapi3.NewHeaderParameter(scheme.Value.Name)
|
||||
h := openapi3.NewHeaderParameter(headerName)
|
||||
h.Required = true
|
||||
h.Description = globalAuth // differentiator for normal variables and global auth
|
||||
return h, nil
|
||||
case "bearer":
|
||||
h := openapi3.NewHeaderParameter(scheme.Value.Name)
|
||||
h := openapi3.NewHeaderParameter(headerName)
|
||||
h.Required = true
|
||||
h.Description = globalAuth // differentiator for normal variables and global auth
|
||||
return h, nil
|
||||
|
||||
@@ -155,7 +155,7 @@ func NewClient(call goja.ConstructorCall, runtime *goja.Runtime) *goja.Object {
|
||||
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
|
||||
// client.Authenticate('user', 'password');
|
||||
// ```
|
||||
func (c *Client) Authenticate(username, password string) {
|
||||
func (c *Client) Authenticate(username, password string) bool {
|
||||
c.nj.Require(c.conn != nil, "no existing connection")
|
||||
if c.BaseDN == "" {
|
||||
c.BaseDN = fmt.Sprintf("dc=%s", strings.Join(strings.Split(c.Realm, "."), ",dc="))
|
||||
@@ -163,19 +163,21 @@ func (c *Client) Authenticate(username, password string) {
|
||||
if err := c.conn.NTLMBind(c.Realm, username, password); err == nil {
|
||||
// if bind with NTLMBind(), there is nothing
|
||||
// else to do, you are authenticated
|
||||
return
|
||||
return true
|
||||
}
|
||||
|
||||
var err error
|
||||
switch password {
|
||||
case "":
|
||||
if err := c.conn.UnauthenticatedBind(username); err != nil {
|
||||
if err = c.conn.UnauthenticatedBind(username); err != nil {
|
||||
c.nj.ThrowError(err)
|
||||
}
|
||||
default:
|
||||
if err := c.conn.Bind(username, password); err != nil {
|
||||
if err = c.conn.Bind(username, password); err != nil {
|
||||
c.nj.ThrowError(err)
|
||||
}
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// AuthenticateWithNTLMHash authenticates with the ldap server using the given username and NTLM hash
|
||||
@@ -185,14 +187,16 @@ func (c *Client) Authenticate(username, password string) {
|
||||
// const client = new ldap.Client('ldap://ldap.example.com', 'acme.com');
|
||||
// client.AuthenticateWithNTLMHash('pdtm', 'hash');
|
||||
// ```
|
||||
func (c *Client) AuthenticateWithNTLMHash(username, hash string) {
|
||||
func (c *Client) AuthenticateWithNTLMHash(username, hash string) bool {
|
||||
c.nj.Require(c.conn != nil, "no existing connection")
|
||||
if c.BaseDN == "" {
|
||||
c.BaseDN = fmt.Sprintf("dc=%s", strings.Join(strings.Split(c.Realm, "."), ",dc="))
|
||||
}
|
||||
if err := c.conn.NTLMBindWithHash(c.Realm, username, hash); err != nil {
|
||||
var err error
|
||||
if err = c.conn.NTLMBindWithHash(c.Realm, username, hash); err != nil {
|
||||
c.nj.ThrowError(err)
|
||||
}
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// Search accepts whatever filter and returns a list of maps having provided attributes
|
||||
@@ -279,9 +283,10 @@ func (c *Client) CollectMetadata() Metadata {
|
||||
}
|
||||
metadata.BaseDN = c.BaseDN
|
||||
|
||||
// Use scope as Base since Root DSE doesn't have subentries
|
||||
srMetadata := ldap.NewSearchRequest(
|
||||
"",
|
||||
ldap.ScopeWholeSubtree,
|
||||
ldap.ScopeBaseObject,
|
||||
ldap.NeverDerefAliases,
|
||||
0, 0, false,
|
||||
"(objectClass=*)",
|
||||
|
||||
@@ -53,7 +53,7 @@ func BuildDSN(opts MySQLOptions) (string, error) {
|
||||
}
|
||||
target := net.JoinHostPort(opts.Host, fmt.Sprintf("%d", opts.Port))
|
||||
var dsn strings.Builder
|
||||
dsn.WriteString(fmt.Sprintf("%v:%v", url.QueryEscape(opts.Username), url.QueryEscape(opts.Password)))
|
||||
dsn.WriteString(fmt.Sprintf("%v:%v", url.QueryEscape(opts.Username), opts.Password))
|
||||
dsn.WriteString("@")
|
||||
dsn.WriteString(fmt.Sprintf("%v(%v)", opts.Protocol, target))
|
||||
if opts.DbName != "" {
|
||||
|
||||
@@ -3,5 +3,7 @@ package keys
|
||||
|
||||
import _ "embed"
|
||||
|
||||
const PDVerifier = "projectdiscovery/nuclei-templates"
|
||||
|
||||
//go:embed nuclei.crt
|
||||
var NucleiCert []byte // public key for verifying digital signature of templates
|
||||
@@ -39,6 +39,11 @@ func (w *StandardWriter) formatScreen(output *ResultEvent) []byte {
|
||||
}
|
||||
}
|
||||
|
||||
if output.GlobalMatchers {
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(w.aurora.BrightMagenta("global").String())
|
||||
}
|
||||
|
||||
builder.WriteString("] [")
|
||||
builder.WriteString(w.aurora.BrightBlue(output.Type).String())
|
||||
builder.WriteString("] ")
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"maps"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -99,6 +100,15 @@ type InternalWrappedEvent struct {
|
||||
InteractshMatched atomic.Bool
|
||||
}
|
||||
|
||||
func (iwe *InternalWrappedEvent) CloneShallow() *InternalWrappedEvent {
|
||||
return &InternalWrappedEvent{
|
||||
InternalEvent: maps.Clone(iwe.InternalEvent),
|
||||
Results: nil,
|
||||
OperatorsResult: nil,
|
||||
UsesInteractsh: iwe.UsesInteractsh,
|
||||
}
|
||||
}
|
||||
|
||||
func (iwe *InternalWrappedEvent) HasOperatorResult() bool {
|
||||
iwe.RLock()
|
||||
defer iwe.RUnlock()
|
||||
@@ -174,6 +184,9 @@ type ResultEvent struct {
|
||||
MatcherStatus bool `json:"matcher-status"`
|
||||
// Lines is the line count for the specified match
|
||||
Lines []int `json:"matched-line,omitempty"`
|
||||
// GlobalMatchers identifies whether the matches was detected in the response
|
||||
// of another template's result event
|
||||
GlobalMatchers bool `json:"global-matchers,omitempty"`
|
||||
|
||||
// IssueTrackers is the metadata for issue trackers
|
||||
IssueTrackers map[string]IssueTrackerMetadata `json:"issue_trackers,omitempty"`
|
||||
@@ -190,6 +203,7 @@ type ResultEvent struct {
|
||||
FuzzingPosition string `json:"fuzzing_position,omitempty"`
|
||||
|
||||
FileToIndexPosition map[string]int `json:"-"`
|
||||
TemplateVerifier string `json:"-"`
|
||||
Error string `json:"error,omitempty"`
|
||||
}
|
||||
|
||||
@@ -263,7 +277,7 @@ func NewStandardWriter(options *types.Options) (*StandardWriter, error) {
|
||||
func (w *StandardWriter) Write(event *ResultEvent) error {
|
||||
// Enrich the result event with extra metadata on the template-path and url.
|
||||
if event.TemplatePath != "" {
|
||||
event.Template, event.TemplateURL = utils.TemplatePathURL(types.ToString(event.TemplatePath), types.ToString(event.TemplateID))
|
||||
event.Template, event.TemplateURL = utils.TemplatePathURL(types.ToString(event.TemplatePath), types.ToString(event.TemplateID), event.TemplateVerifier)
|
||||
}
|
||||
|
||||
if len(w.KeysToRedact) > 0 {
|
||||
@@ -435,7 +449,7 @@ func (w *StandardWriter) WriteFailure(wrappedEvent *InternalWrappedEvent) error
|
||||
// if no results were found, manually create a failure event
|
||||
event := wrappedEvent.InternalEvent
|
||||
|
||||
templatePath, templateURL := utils.TemplatePathURL(types.ToString(event["template-path"]), types.ToString(event["template-id"]))
|
||||
templatePath, templateURL := utils.TemplatePathURL(types.ToString(event["template-path"]), types.ToString(event["template-id"]), types.ToString(event["template-verifier"]))
|
||||
var templateInfo model.Info
|
||||
if event["template-info"] != nil {
|
||||
templateInfo = event["template-info"].(model.Info)
|
||||
|
||||
@@ -235,7 +235,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
gologger.Verbose().Msgf("[%s] Executed code on local machine %v", request.options.TemplateID, input.MetaInput.Input)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Code Protocol request variables: \n%s\n", vardump.DumpVariables(allvars))
|
||||
gologger.Debug().Msgf("Code Protocol request variables: %s\n", vardump.DumpVariables(allvars))
|
||||
}
|
||||
|
||||
if request.options.Options.Debug || request.options.Options.DebugRequests {
|
||||
@@ -348,6 +348,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(request.options.TemplateID),
|
||||
TemplatePath: types.ToString(request.options.TemplatePath),
|
||||
Info: request.options.TemplateInfo,
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Matched: types.ToString(wrapped.InternalEvent["input"]),
|
||||
Host: fields.Host,
|
||||
|
||||
@@ -39,3 +39,34 @@ func TestCodeProtocol(t *testing.T) {
|
||||
require.Nil(t, err, "could not run code request")
|
||||
require.NotEmpty(t, gotEvent, "could not get event items")
|
||||
}
|
||||
|
||||
func TestCodeProtocolForGO(t *testing.T) {
|
||||
options := testutils.DefaultOptions
|
||||
|
||||
testutils.Init(options)
|
||||
templateID := "testing-code"
|
||||
request := &Request{
|
||||
Engine: []string{"go"},
|
||||
Source: `package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main() {
|
||||
fmt.Println("hello world")
|
||||
}`,
|
||||
}
|
||||
executerOpts := testutils.NewMockExecuterOptions(options, &testutils.TemplateInfo{
|
||||
ID: templateID,
|
||||
Info: model.Info{SeverityHolder: severity.Holder{Severity: severity.Low}, Name: "hello-world"},
|
||||
})
|
||||
err := request.Compile(executerOpts)
|
||||
require.Nil(t, err, "could not compile code request")
|
||||
|
||||
var gotEvent output.InternalEvent
|
||||
ctxArgs := contextargs.NewWithInput(context.Background(), "")
|
||||
err = request.ExecuteWithResults(ctxArgs, nil, nil, func(event *output.InternalWrappedEvent) {
|
||||
gotEvent = event.InternalEvent
|
||||
})
|
||||
require.Nil(t, err, "could not run code request")
|
||||
require.NotEmpty(t, gotEvent, "could not get event items")
|
||||
}
|
||||
@@ -22,6 +22,13 @@ func (g *PayloadGenerator) validate(payloads map[string]interface{}, templatePat
|
||||
return errors.New("invalid number of lines in payload")
|
||||
}
|
||||
|
||||
// For historical reasons, "validate" checks to see if the payload file exist.
|
||||
// If we're using a custom helper function, then we need to skip any validation beyond just checking the string syntax.
|
||||
// Actually attempting to load the file will determine whether or not it exists.
|
||||
if g.options.LoadHelperFileFunction != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
// check if it's a file and try to load it
|
||||
if fileutil.FileExists(payloadType) {
|
||||
continue
|
||||
|
||||
@@ -0,0 +1,84 @@
|
||||
package globalmatchers
|
||||
|
||||
import (
|
||||
"maps"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
)
|
||||
|
||||
// Storage is a struct that holds the global matchers
|
||||
type Storage struct {
|
||||
requests []*Item
|
||||
}
|
||||
|
||||
// Callback is called when a global matcher is matched.
|
||||
// It receives internal event & result of the operator execution.
|
||||
type Callback func(event output.InternalEvent, result *operators.Result)
|
||||
|
||||
// Item is a struct that holds the global matchers
|
||||
// details for a template
|
||||
type Item struct {
|
||||
TemplateID string
|
||||
TemplatePath string
|
||||
TemplateInfo model.Info
|
||||
Operators []*operators.Operators
|
||||
}
|
||||
|
||||
// New creates a new storage for global matchers
|
||||
func New() *Storage {
|
||||
return &Storage{requests: make([]*Item, 0)}
|
||||
}
|
||||
|
||||
// hasStorage checks if the Storage is initialized
|
||||
func (s *Storage) hasStorage() bool {
|
||||
return s != nil
|
||||
}
|
||||
|
||||
// AddOperator adds a new operator to the global matchers
|
||||
func (s *Storage) AddOperator(item *Item) {
|
||||
if !s.hasStorage() {
|
||||
return
|
||||
}
|
||||
|
||||
s.requests = append(s.requests, item)
|
||||
}
|
||||
|
||||
// HasMatchers returns true if we have global matchers
|
||||
func (s *Storage) HasMatchers() bool {
|
||||
if !s.hasStorage() {
|
||||
return false
|
||||
}
|
||||
|
||||
return len(s.requests) > 0
|
||||
}
|
||||
|
||||
// Match matches the global matchers against the response
|
||||
func (s *Storage) Match(
|
||||
event output.InternalEvent,
|
||||
matchFunc operators.MatchFunc,
|
||||
extractFunc operators.ExtractFunc,
|
||||
isDebug bool,
|
||||
callback Callback,
|
||||
) {
|
||||
for _, item := range s.requests {
|
||||
for _, operator := range item.Operators {
|
||||
newEvent := maps.Clone(event)
|
||||
newEvent.Set("origin-template-id", event["template-id"])
|
||||
newEvent.Set("origin-template-info", event["template-info"])
|
||||
newEvent.Set("origin-template-path", event["template-path"])
|
||||
newEvent.Set("template-id", item.TemplateID)
|
||||
newEvent.Set("template-info", item.TemplateInfo)
|
||||
newEvent.Set("template-path", item.TemplatePath)
|
||||
newEvent.Set("global-matchers", true)
|
||||
|
||||
result, matched := operator.Execute(newEvent, matchFunc, extractFunc, isDebug)
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
|
||||
callback(newEvent, result)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,7 +24,7 @@ func CreateEventWithAdditionalOptions(request protocols.Request, outputEvent out
|
||||
// Dump response variables if ran in debug mode
|
||||
if vardump.EnableVarDump {
|
||||
protoName := cases.Title(language.English).String(request.Type().String())
|
||||
gologger.Debug().Msgf("%v Protocol response variables: \n%s\n", protoName, vardump.DumpVariables(outputEvent))
|
||||
gologger.Debug().Msgf("%v Protocol response variables: %s\n", protoName, vardump.DumpVariables(outputEvent))
|
||||
}
|
||||
for _, compiledOperator := range request.GetCompiledOperators() {
|
||||
if compiledOperator != nil {
|
||||
|
||||
@@ -20,10 +20,10 @@ import (
|
||||
// CacheInterface defines the signature of the hosterrorscache so that
|
||||
// users of Nuclei as embedded lib may implement their own cache
|
||||
type CacheInterface interface {
|
||||
SetVerbose(verbose bool) // log verbosely
|
||||
Close() // close the cache
|
||||
Check(ctx *contextargs.Context) bool // return true if the host should be skipped
|
||||
MarkFailed(ctx *contextargs.Context, err error) // record a failure (and cause) for the host
|
||||
SetVerbose(verbose bool) // log verbosely
|
||||
Close() // close the cache
|
||||
Check(protoType string, ctx *contextargs.Context) bool // return true if the host should be skipped
|
||||
MarkFailed(protoType string, ctx *contextargs.Context, err error) // record a failure (and cause) for the host
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -75,24 +75,34 @@ func (c *Cache) Close() {
|
||||
c.failedTargets.Purge()
|
||||
}
|
||||
|
||||
func (c *Cache) normalizeCacheValue(value string) string {
|
||||
finalValue := value
|
||||
if strings.HasPrefix(value, "http") {
|
||||
if parsed, err := url.Parse(value); err == nil {
|
||||
hostname := parsed.Host
|
||||
finalPort := parsed.Port()
|
||||
if finalPort == "" {
|
||||
if parsed.Scheme == "https" {
|
||||
finalPort = "443"
|
||||
} else {
|
||||
finalPort = "80"
|
||||
}
|
||||
hostname = net.JoinHostPort(parsed.Host, finalPort)
|
||||
// NormalizeCacheValue processes the input value and returns a normalized cache
|
||||
// value.
|
||||
func (c *Cache) NormalizeCacheValue(value string) string {
|
||||
var normalizedValue string = value
|
||||
|
||||
u, err := url.ParseRequestURI(value)
|
||||
if err != nil || u.Host == "" {
|
||||
u, err2 := url.ParseRequestURI("https://" + value)
|
||||
if err2 != nil {
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
normalizedValue = u.Host
|
||||
} else {
|
||||
port := u.Port()
|
||||
if port == "" {
|
||||
switch u.Scheme {
|
||||
case "https":
|
||||
normalizedValue = net.JoinHostPort(u.Host, "443")
|
||||
case "http":
|
||||
normalizedValue = net.JoinHostPort(u.Host, "80")
|
||||
}
|
||||
finalValue = hostname
|
||||
} else {
|
||||
normalizedValue = u.Host
|
||||
}
|
||||
}
|
||||
return finalValue
|
||||
|
||||
return normalizedValue
|
||||
}
|
||||
|
||||
// ErrUnresponsiveHost is returned when a host is unresponsive
|
||||
@@ -105,7 +115,7 @@ func (c *Cache) normalizeCacheValue(value string) string {
|
||||
// - URL: https?:// type
|
||||
// - Host:port type
|
||||
// - host type
|
||||
func (c *Cache) Check(ctx *contextargs.Context) bool {
|
||||
func (c *Cache) Check(protoType string, ctx *contextargs.Context) bool {
|
||||
finalValue := c.GetKeyFromContext(ctx, nil)
|
||||
|
||||
existingCacheItem, err := c.failedTargets.GetIFPresent(finalValue)
|
||||
@@ -128,8 +138,8 @@ func (c *Cache) Check(ctx *contextargs.Context) bool {
|
||||
}
|
||||
|
||||
// MarkFailed marks a host as failed previously
|
||||
func (c *Cache) MarkFailed(ctx *contextargs.Context, err error) {
|
||||
if !c.checkError(err) {
|
||||
func (c *Cache) MarkFailed(protoType string, ctx *contextargs.Context, err error) {
|
||||
if !c.checkError(protoType, err) {
|
||||
return
|
||||
}
|
||||
finalValue := c.GetKeyFromContext(ctx, err)
|
||||
@@ -166,7 +176,7 @@ func (c *Cache) GetKeyFromContext(ctx *contextargs.Context, err error) string {
|
||||
address = tmp.String()
|
||||
}
|
||||
}
|
||||
finalValue := c.normalizeCacheValue(address)
|
||||
finalValue := c.NormalizeCacheValue(address)
|
||||
return finalValue
|
||||
}
|
||||
|
||||
@@ -176,11 +186,13 @@ var reCheckError = regexp.MustCompile(`(no address found for host|could not reso
|
||||
// added to the host skipping table.
|
||||
// it first parses error and extracts the cause and checks for blacklisted
|
||||
// or common errors that should be skipped
|
||||
func (c *Cache) checkError(err error) bool {
|
||||
func (c *Cache) checkError(protoType string, err error) bool {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
if protoType != "http" {
|
||||
return false
|
||||
}
|
||||
kind := errkit.GetErrorKind(err, nucleierr.ErrTemplateLogic)
|
||||
switch kind {
|
||||
case nucleierr.ErrTemplateLogic:
|
||||
|
||||
@@ -11,12 +11,16 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
protoType = "http"
|
||||
)
|
||||
|
||||
func TestCacheCheck(t *testing.T) {
|
||||
cache := New(3, DefaultMaxHostsCount, nil)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
cache.MarkFailed(newCtxArgs("test"), fmt.Errorf("could not resolve host"))
|
||||
got := cache.Check(newCtxArgs("test"))
|
||||
cache.MarkFailed(protoType, newCtxArgs("test"), fmt.Errorf("could not resolve host"))
|
||||
got := cache.Check(protoType, newCtxArgs("test"))
|
||||
if i < 2 {
|
||||
// till 3 the host is not flagged to skip
|
||||
require.False(t, got)
|
||||
@@ -26,7 +30,7 @@ func TestCacheCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
value := cache.Check(newCtxArgs("test"))
|
||||
value := cache.Check(protoType, newCtxArgs("test"))
|
||||
require.Equal(t, true, value, "could not get checked value")
|
||||
}
|
||||
|
||||
@@ -34,8 +38,8 @@ func TestTrackErrors(t *testing.T) {
|
||||
cache := New(3, DefaultMaxHostsCount, []string{"custom error"})
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
cache.MarkFailed(newCtxArgs("custom"), fmt.Errorf("got: nested: custom error"))
|
||||
got := cache.Check(newCtxArgs("custom"))
|
||||
cache.MarkFailed(protoType, newCtxArgs("custom"), fmt.Errorf("got: nested: custom error"))
|
||||
got := cache.Check(protoType, newCtxArgs("custom"))
|
||||
if i < 2 {
|
||||
// till 3 the host is not flagged to skip
|
||||
require.False(t, got)
|
||||
@@ -44,7 +48,7 @@ func TestTrackErrors(t *testing.T) {
|
||||
require.True(t, got)
|
||||
}
|
||||
}
|
||||
value := cache.Check(newCtxArgs("custom"))
|
||||
value := cache.Check(protoType, newCtxArgs("custom"))
|
||||
require.Equal(t, true, value, "could not get checked value")
|
||||
}
|
||||
|
||||
@@ -86,7 +90,7 @@ func TestCacheMarkFailed(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
normalizedCacheValue := cache.GetKeyFromContext(newCtxArgs(test.host), nil)
|
||||
cache.MarkFailed(newCtxArgs(test.host), fmt.Errorf("no address found for host"))
|
||||
cache.MarkFailed(protoType, newCtxArgs(test.host), fmt.Errorf("no address found for host"))
|
||||
failedTarget, err := cache.failedTargets.Get(normalizedCacheValue)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, failedTarget)
|
||||
@@ -109,7 +113,7 @@ func TestCacheMarkFailedConcurrent(t *testing.T) {
|
||||
|
||||
// the cache is not atomic during items creation, so we pre-create them with counter to zero
|
||||
for _, test := range tests {
|
||||
normalizedValue := cache.normalizeCacheValue(test.host)
|
||||
normalizedValue := cache.NormalizeCacheValue(test.host)
|
||||
newItem := &cacheItem{errors: atomic.Int32{}}
|
||||
newItem.errors.Store(0)
|
||||
_ = cache.failedTargets.Set(normalizedValue, newItem)
|
||||
@@ -122,16 +126,16 @@ func TestCacheMarkFailedConcurrent(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
cache.MarkFailed(newCtxArgs(currentTest.host), fmt.Errorf("could not resolve host"))
|
||||
cache.MarkFailed(protoType, newCtxArgs(currentTest.host), fmt.Errorf("could not resolve host"))
|
||||
}()
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, test := range tests {
|
||||
require.True(t, cache.Check(newCtxArgs(test.host)))
|
||||
require.True(t, cache.Check(protoType, newCtxArgs(test.host)))
|
||||
|
||||
normalizedCacheValue := cache.normalizeCacheValue(test.host)
|
||||
normalizedCacheValue := cache.NormalizeCacheValue(test.host)
|
||||
failedTarget, err := cache.failedTargets.Get(normalizedCacheValue)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, failedTarget)
|
||||
|
||||
@@ -77,9 +77,8 @@ var muGlobalChange sync.Mutex
|
||||
|
||||
// Global setting
|
||||
func GlobalGuardBytesBufferAlloc() error {
|
||||
if muGlobalChange.TryLock() {
|
||||
if !muGlobalChange.TryLock() {
|
||||
return nil
|
||||
|
||||
}
|
||||
defer muGlobalChange.Unlock()
|
||||
|
||||
@@ -95,9 +94,8 @@ func GlobalGuardBytesBufferAlloc() error {
|
||||
|
||||
// Global setting
|
||||
func GlobalRestoreBytesBufferAlloc() {
|
||||
if muGlobalChange.TryLock() {
|
||||
if !muGlobalChange.TryLock() {
|
||||
return
|
||||
|
||||
}
|
||||
defer muGlobalChange.Unlock()
|
||||
|
||||
|
||||
@@ -1,53 +1,67 @@
|
||||
package vardump
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
"github.com/yassinebenaid/godump"
|
||||
)
|
||||
|
||||
// EnableVarDump enables var dump for debugging optionally
|
||||
var EnableVarDump bool
|
||||
// variables is a map of variables
|
||||
type variables = map[string]any
|
||||
|
||||
// DumpVariables writes the truncated dump of variables to a string
|
||||
// in a formatted key-value manner.
|
||||
//
|
||||
// The values are truncated to return 50 characters from start and end.
|
||||
func DumpVariables(data map[string]interface{}) string {
|
||||
var counter int
|
||||
// DumpVariables dumps the variables in a pretty format
|
||||
func DumpVariables(data variables) string {
|
||||
if !EnableVarDump {
|
||||
return ""
|
||||
}
|
||||
|
||||
buffer := &strings.Builder{}
|
||||
buffer.Grow(len(data) * 78) // grow buffer to an approximate size
|
||||
d := godump.Dumper{
|
||||
Indentation: " ",
|
||||
HidePrivateFields: false,
|
||||
ShowPrimitiveNamedTypes: true,
|
||||
}
|
||||
|
||||
builder := &strings.Builder{}
|
||||
// sort keys for deterministic output
|
||||
d.Theme = godump.Theme{
|
||||
String: godump.RGB{R: 138, G: 201, B: 38},
|
||||
Quotes: godump.RGB{R: 112, G: 214, B: 255},
|
||||
Bool: godump.RGB{R: 249, G: 87, B: 56},
|
||||
Number: godump.RGB{R: 10, G: 178, B: 242},
|
||||
Types: godump.RGB{R: 0, G: 150, B: 199},
|
||||
Address: godump.RGB{R: 205, G: 93, B: 0},
|
||||
PointerTag: godump.RGB{R: 110, G: 110, B: 110},
|
||||
Nil: godump.RGB{R: 219, G: 57, B: 26},
|
||||
Func: godump.RGB{R: 160, G: 90, B: 220},
|
||||
Fields: godump.RGB{R: 189, G: 176, B: 194},
|
||||
Chan: godump.RGB{R: 195, G: 154, B: 76},
|
||||
UnsafePointer: godump.RGB{R: 89, G: 193, B: 180},
|
||||
Braces: godump.RGB{R: 185, G: 86, B: 86},
|
||||
}
|
||||
|
||||
return d.Sprint(process(data, Limit))
|
||||
}
|
||||
|
||||
// process is a helper function that processes the variables
|
||||
// and returns a new map of variables
|
||||
func process(data variables, limit int) variables {
|
||||
keys := mapsutil.GetSortedKeys(data)
|
||||
vars := make(variables)
|
||||
|
||||
if limit == 0 {
|
||||
limit = 255
|
||||
}
|
||||
|
||||
for _, k := range keys {
|
||||
v := data[k]
|
||||
valueString := types.ToString(v)
|
||||
|
||||
counter++
|
||||
if len(valueString) > 50 {
|
||||
builder.Grow(56)
|
||||
builder.WriteString(valueString[0:25])
|
||||
builder.WriteString(" .... ")
|
||||
builder.WriteString(valueString[len(valueString)-25:])
|
||||
valueString = builder.String()
|
||||
builder.Reset()
|
||||
v := types.ToString(data[k])
|
||||
v = strings.ReplaceAll(strings.ReplaceAll(v, "\r", " "), "\n", " ")
|
||||
if len(v) > limit {
|
||||
v = v[:limit]
|
||||
v += " [...]"
|
||||
}
|
||||
valueString = strings.ReplaceAll(strings.ReplaceAll(valueString, "\r", " "), "\n", " ")
|
||||
|
||||
buffer.WriteString("\t")
|
||||
buffer.WriteString(strconv.Itoa(counter))
|
||||
buffer.WriteString(". ")
|
||||
buffer.WriteString(k)
|
||||
buffer.WriteString(" => ")
|
||||
buffer.WriteString(valueString)
|
||||
buffer.WriteString("\n")
|
||||
vars[k] = v
|
||||
}
|
||||
final := buffer.String()
|
||||
return final
|
||||
|
||||
return vars
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package vardump
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestDumpVariables(t *testing.T) {
|
||||
// Enable var dump for testing
|
||||
EnableVarDump = true
|
||||
|
||||
// Test case
|
||||
testVars := variables{
|
||||
"string": "test",
|
||||
"int": 42,
|
||||
"bool": true,
|
||||
"slice": []string{"a", "b", "c"},
|
||||
}
|
||||
|
||||
result := DumpVariables(testVars)
|
||||
|
||||
// Assertions
|
||||
assert.NotEmpty(t, result)
|
||||
assert.Contains(t, result, "string")
|
||||
assert.Contains(t, result, "test")
|
||||
assert.Contains(t, result, "int")
|
||||
assert.Contains(t, result, "42")
|
||||
assert.Contains(t, result, "bool")
|
||||
assert.Contains(t, result, "true")
|
||||
assert.Contains(t, result, "slice")
|
||||
assert.Contains(t, result, "a")
|
||||
assert.Contains(t, result, "b")
|
||||
assert.Contains(t, result, "c")
|
||||
|
||||
// Test with EnableVarDump set to false
|
||||
EnableVarDump = false
|
||||
result = DumpVariables(testVars)
|
||||
assert.Empty(t, result)
|
||||
}
|
||||
|
||||
func TestProcess(t *testing.T) {
|
||||
testVars := variables{
|
||||
"short": "short string",
|
||||
"long": strings.Repeat("a", 300),
|
||||
"number": 42,
|
||||
}
|
||||
|
||||
processed := process(testVars, 255)
|
||||
|
||||
assert.Equal(t, "short string", processed["short"])
|
||||
assert.Equal(t, strings.Repeat("a", 255)+" [...]", processed["long"])
|
||||
assert.Equal(t, "42", processed["number"])
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
package vardump
|
||||
|
||||
var (
|
||||
// EnableVarDump enables var dump for debugging optionally
|
||||
EnableVarDump bool
|
||||
// Limit is the maximum characters to be dumped
|
||||
Limit int = 255
|
||||
)
|
||||
@@ -114,6 +114,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
|
||||
@@ -108,7 +108,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, metadata,
|
||||
func (request *Request) execute(input *contextargs.Context, domain string, metadata, previous output.InternalEvent, vars map[string]interface{}, callback protocols.OutputEventCallback) error {
|
||||
var err error
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("DNS Protocol request variables: \n%s\n", vardump.DumpVariables(vars))
|
||||
gologger.Debug().Msgf("DNS Protocol request variables: %s\n", vardump.DumpVariables(vars))
|
||||
}
|
||||
|
||||
// Compile each request for the template based on the URL
|
||||
|
||||
@@ -104,6 +104,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Path: types.ToString(wrapped.InternalEvent["path"]),
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
|
||||
@@ -45,9 +45,24 @@ const (
|
||||
// ActionFilesInput performs an action on a file input.
|
||||
// name:files
|
||||
ActionFilesInput
|
||||
// ActionWaitLoad waits for the page to stop loading.
|
||||
// ActionWaitDOM waits for the HTML document has been completely loaded & parsed.
|
||||
// name:waitdom
|
||||
ActionWaitDOM
|
||||
// ActionWaitFCP waits for the first piece of content (text, image, etc.) is painted on the screen.
|
||||
// name:waitfcp
|
||||
ActionWaitFCP
|
||||
// ActionWaitFMP waits for page has rendered enough meaningful content to be useful to the user.
|
||||
// name:waitfmp
|
||||
ActionWaitFMP
|
||||
// ActionWaitIdle waits for the network is completely idle (no ongoing network requests).
|
||||
// name:waitidle
|
||||
ActionWaitIdle
|
||||
// ActionWaitLoad waits for the page and all its resources (like stylesheets and images) have finished loading.
|
||||
// name:waitload
|
||||
ActionWaitLoad
|
||||
// ActionWaitStable waits until the page is stable.
|
||||
// name:waitstable
|
||||
ActionWaitStable
|
||||
// ActionGetResource performs a get resource action on an element
|
||||
// name:getresource
|
||||
ActionGetResource
|
||||
@@ -102,7 +117,12 @@ var ActionStringToAction = map[string]ActionType{
|
||||
"time": ActionTimeInput,
|
||||
"select": ActionSelectInput,
|
||||
"files": ActionFilesInput,
|
||||
"waitdom": ActionWaitDOM,
|
||||
"waitfcp": ActionWaitFCP,
|
||||
"waitfmp": ActionWaitFMP,
|
||||
"waitidle": ActionWaitIdle,
|
||||
"waitload": ActionWaitLoad,
|
||||
"waitstable": ActionWaitStable,
|
||||
"getresource": ActionGetResource,
|
||||
"extract": ActionExtract,
|
||||
"setmethod": ActionSetMethod,
|
||||
@@ -129,7 +149,12 @@ var ActionToActionString = map[ActionType]string{
|
||||
ActionTimeInput: "time",
|
||||
ActionSelectInput: "select",
|
||||
ActionFilesInput: "files",
|
||||
ActionWaitDOM: "waitdom",
|
||||
ActionWaitFCP: "waitfcp",
|
||||
ActionWaitFMP: "waitfmp",
|
||||
ActionWaitIdle: "waitidle",
|
||||
ActionWaitLoad: "waitload",
|
||||
ActionWaitStable: "waitstable",
|
||||
ActionGetResource: "getresource",
|
||||
ActionExtract: "extract",
|
||||
ActionSetMethod: "setmethod",
|
||||
|
||||
@@ -94,8 +94,28 @@ func (p *Page) ExecuteActions(input *contextargs.Context, actions []*Action, var
|
||||
err = p.TimeInputElement(act, outData)
|
||||
case ActionSelectInput:
|
||||
err = p.SelectInputElement(act, outData)
|
||||
case ActionWaitDOM:
|
||||
event := proto.PageLifecycleEventNameDOMContentLoaded
|
||||
err = p.WaitPageLifecycleEvent(act, outData, event)
|
||||
case ActionWaitFCP:
|
||||
event := proto.PageLifecycleEventNameFirstContentfulPaint
|
||||
err = p.WaitPageLifecycleEvent(act, outData, event)
|
||||
case ActionWaitFMP:
|
||||
event := proto.PageLifecycleEventNameFirstMeaningfulPaint
|
||||
err = p.WaitPageLifecycleEvent(act, outData, event)
|
||||
case ActionWaitIdle:
|
||||
event := proto.PageLifecycleEventNameNetworkIdle
|
||||
err = p.WaitPageLifecycleEvent(act, outData, event)
|
||||
case ActionWaitLoad:
|
||||
err = p.WaitLoad(act, outData)
|
||||
event := proto.PageLifecycleEventNameLoad
|
||||
err = p.WaitPageLifecycleEvent(act, outData, event)
|
||||
case ActionWaitStable:
|
||||
err = p.WaitStable(act, outData)
|
||||
// NOTE(dwisiswant0): Mapping `ActionWaitLoad` to `Page.WaitStable`,
|
||||
// just in case waiting for the `proto.PageLifecycleEventNameLoad` event
|
||||
// doesn't meet expectations.
|
||||
// case ActionWaitLoad, ActionWaitStable:
|
||||
// err = p.WaitStable(act, outData)
|
||||
case ActionGetResource:
|
||||
err = p.GetResource(act, outData)
|
||||
case ActionExtract:
|
||||
@@ -204,6 +224,17 @@ func createBackOffSleeper(pollTimeout, timeout time.Duration) utils.Sleeper {
|
||||
}
|
||||
}
|
||||
|
||||
func getNavigationFunc(p *Page, act *Action, event proto.PageLifecycleEventName) (func(), error) {
|
||||
dur, err := getTimeout(p, act)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "Wrong timeout given")
|
||||
}
|
||||
|
||||
fn := p.page.Timeout(dur).WaitNavigation(event)
|
||||
|
||||
return fn, nil
|
||||
}
|
||||
|
||||
func getTimeout(p *Page, act *Action) (time.Duration, error) {
|
||||
return geTimeParameter(p, act, "timeout", 3, time.Second)
|
||||
}
|
||||
@@ -303,7 +334,7 @@ func (p *Page) NavigateURL(action *Action, out ActionData, allvars map[string]in
|
||||
allvars = generators.MergeMaps(allvars, defaultReqVars)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: \n%s\n", vardump.DumpVariables(allvars))
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: %s\n", vardump.DumpVariables(allvars))
|
||||
}
|
||||
|
||||
// Evaluate the target url with all variables
|
||||
@@ -518,20 +549,38 @@ func (p *Page) SelectInputElement(act *Action, out ActionData) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitLoad waits for the page to load
|
||||
func (p *Page) WaitLoad(act *Action, out ActionData) error {
|
||||
p.page.Timeout(2 * time.Second).WaitNavigation(proto.PageLifecycleEventNameFirstMeaningfulPaint)()
|
||||
|
||||
// Wait for the window.onload event and also wait for the network requests
|
||||
// to become idle for a maximum duration of 3 seconds. If the requests
|
||||
// do not finish,
|
||||
if err := p.page.WaitLoad(); err != nil {
|
||||
return errors.Wrap(err, "could not wait load event")
|
||||
// WaitPageLifecycleEvent waits for specified page lifecycle event name
|
||||
func (p *Page) WaitPageLifecycleEvent(act *Action, out ActionData, event proto.PageLifecycleEventName) error {
|
||||
fn, err := getNavigationFunc(p, act, event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
_ = p.page.WaitIdle(1 * time.Second)
|
||||
|
||||
fn()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WaitStable waits until the page is stable
|
||||
func (p *Page) WaitStable(act *Action, out ActionData) error {
|
||||
var dur time.Duration = time.Second // default stable page duration: 1s
|
||||
|
||||
timeout, err := getTimeout(p, act)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "Wrong timeout given")
|
||||
}
|
||||
|
||||
argDur := act.Data["duration"]
|
||||
if argDur != "" {
|
||||
dur, err = time.ParseDuration(argDur)
|
||||
if err != nil {
|
||||
dur = time.Second
|
||||
}
|
||||
}
|
||||
|
||||
return p.page.Timeout(timeout).WaitStable(dur)
|
||||
}
|
||||
|
||||
// GetResource gets a resource from an element from page.
|
||||
func (p *Page) GetResource(act *Action, out ActionData) error {
|
||||
element, err := p.pageElementBy(act.Data)
|
||||
|
||||
@@ -201,7 +201,7 @@ func TestActionScreenshot(t *testing.T) {
|
||||
filePath := filepath.Join(os.TempDir(), "test.png")
|
||||
actions := []*Action{
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionWaitFMP}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionScreenshot}, Data: map[string]string{"to": filePath}},
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ func TestActionScreenshotToDir(t *testing.T) {
|
||||
|
||||
actions := []*Action{
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionNavigate}, Data: map[string]string{"url": "{{BaseURL}}"}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionWaitLoad}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionWaitFMP}},
|
||||
{ActionType: ActionTypeHolder{ActionType: ActionScreenshot}, Data: map[string]string{"to": filePath, "mkdir": "true"}},
|
||||
}
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Path: fields.Path,
|
||||
|
||||
@@ -122,7 +122,7 @@ func (request *Request) executeRequestWithPayloads(input *contextargs.Context, p
|
||||
defer instance.Close()
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: \n%s\n", vardump.DumpVariables(payloads))
|
||||
gologger.Debug().Msgf("Headless Protocol request variables: %s\n", vardump.DumpVariables(payloads))
|
||||
}
|
||||
|
||||
instance.SetInteractsh(request.options.Interactsh)
|
||||
|
||||
@@ -204,7 +204,7 @@ func (r *requestGenerator) Make(ctx context.Context, input *contextargs.Context,
|
||||
finalVars := generators.MergeMaps(allVars, payloads)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("HTTP Protocol request variables: \n%s\n", vardump.DumpVariables(finalVars))
|
||||
gologger.Debug().Msgf("HTTP Protocol request variables: %s\n", vardump.DumpVariables(finalVars))
|
||||
}
|
||||
|
||||
// Note: If possible any changes to current logic (i.e evaluate -> then parse URL)
|
||||
|
||||
@@ -223,6 +223,9 @@ type Request struct {
|
||||
// FuzzPreConditionOperator is the operator between multiple PreConditions for fuzzing Default is OR
|
||||
FuzzPreConditionOperator string `yaml:"pre-condition-operator,omitempty" json:"pre-condition-operator,omitempty" jsonschema:"title=condition between the filters,description=Operator to use between multiple per-conditions,enum=and,enum=or"`
|
||||
fuzzPreConditionOperator matchers.ConditionType `yaml:"-" json:"-"`
|
||||
// description: |
|
||||
// GlobalMatchers marks matchers as static and applies globally to all result events from other templates
|
||||
GlobalMatchers bool `yaml:"global-matchers,omitempty" json:"global-matchers,omitempty" jsonschema:"title=global matchers,description=marks matchers as static and applies globally to all result events from other templates"`
|
||||
}
|
||||
|
||||
func (e Request) JSONSchemaExtend(schema *jsonschema.Schema) {
|
||||
|
||||
@@ -166,10 +166,15 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
if types.ToString(wrapped.InternalEvent["path"]) != "" {
|
||||
fields.Path = types.ToString(wrapped.InternalEvent["path"])
|
||||
}
|
||||
var isGlobalMatchers bool
|
||||
if value, ok := wrapped.InternalEvent["global-matchers"]; ok {
|
||||
isGlobalMatchers = value.(bool)
|
||||
}
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
@@ -182,6 +187,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
Timestamp: time.Now(),
|
||||
MatcherStatus: true,
|
||||
IP: fields.Ip,
|
||||
GlobalMatchers: isGlobalMatchers,
|
||||
Request: types.ToString(wrapped.InternalEvent["request"]),
|
||||
Response: request.truncateResponse(wrapped.InternalEvent["response"]),
|
||||
CURLCommand: types.ToString(wrapped.InternalEvent["curl-command"]),
|
||||
|
||||
@@ -973,13 +973,22 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
// prune signature internal values if any
|
||||
request.pruneSignatureInternalValues(generatedRequest.meta)
|
||||
|
||||
event := eventcreator.CreateEventWithAdditionalOptions(request, generators.MergeMaps(generatedRequest.dynamicValues, finalEvent), request.options.Options.Debug || request.options.Options.DebugResponse, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
interimEvent := generators.MergeMaps(generatedRequest.dynamicValues, finalEvent)
|
||||
isDebug := request.options.Options.Debug || request.options.Options.DebugResponse
|
||||
event := eventcreator.CreateEventWithAdditionalOptions(request, interimEvent, isDebug, func(internalWrappedEvent *output.InternalWrappedEvent) {
|
||||
internalWrappedEvent.OperatorsResult.PayloadValues = generatedRequest.meta
|
||||
})
|
||||
|
||||
if hasInteractMatchers {
|
||||
event.UsesInteractsh = true
|
||||
}
|
||||
|
||||
if request.options.GlobalMatchers.HasMatchers() {
|
||||
request.options.GlobalMatchers.Match(interimEvent, request.Match, request.Extract, isDebug, func(event output.InternalEvent, result *operators.Result) {
|
||||
callback(eventcreator.CreateEventWithOperatorResults(request, event, result))
|
||||
})
|
||||
}
|
||||
|
||||
// if requrlpattern is enabled, only then it is reflected in result event else it is empty string
|
||||
// consult @Ice3man543 before changing this logic (context: vuln_hash)
|
||||
if request.options.ExportReqURLPattern {
|
||||
@@ -1042,6 +1051,10 @@ func (request *Request) validateNFixEvent(input *contextargs.Context, gr *genera
|
||||
|
||||
// addCNameIfAvailable adds the cname to the event if available
|
||||
func (request *Request) addCNameIfAvailable(hostname string, outputEvent map[string]interface{}) {
|
||||
if protocolstate.Dialer == nil {
|
||||
return
|
||||
}
|
||||
|
||||
data, err := protocolstate.Dialer.GetDNSData(hostname)
|
||||
if err == nil {
|
||||
switch len(data.CNAME) {
|
||||
@@ -1173,14 +1186,14 @@ func (request *Request) markUnresponsiveAddress(input *contextargs.Context, err
|
||||
return
|
||||
}
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(input, err)
|
||||
request.options.HostErrorsCache.MarkFailed(request.options.ProtocolType.String(), input, err)
|
||||
}
|
||||
}
|
||||
|
||||
// isUnresponsiveAddress checks if the error is a unreponsive based on its execution history
|
||||
func (request *Request) isUnresponsiveAddress(input *contextargs.Context) bool {
|
||||
if request.options.HostErrorsCache != nil {
|
||||
return request.options.HostErrorsCache.Check(input)
|
||||
return request.options.HostErrorsCache.Check(request.options.ProtocolType.String(), input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -161,7 +161,7 @@ func (request *Request) executeAllFuzzingRules(input *contextargs.Context, value
|
||||
func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest, input *contextargs.Context, callback protocols.OutputEventCallback) bool {
|
||||
hasInteractMatchers := interactsh.HasMatchers(request.CompiledOperators)
|
||||
hasInteractMarkers := len(gr.InteractURLs) > 0
|
||||
if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(input) {
|
||||
if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(request.options.ProtocolType.String(), input) {
|
||||
return false
|
||||
}
|
||||
request.options.RateLimitTake()
|
||||
@@ -215,7 +215,7 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
}
|
||||
if requestErr != nil {
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(input, requestErr)
|
||||
request.options.HostErrorsCache.MarkFailed(request.options.ProtocolType.String(), input, requestErr)
|
||||
}
|
||||
gologger.Verbose().Msgf("[%s] Error occurred in request: %s\n", request.options.TemplateID, requestErr)
|
||||
}
|
||||
|
||||
@@ -319,7 +319,7 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV
|
||||
templateCtx.Merge(payloadValues)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Javascript Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("JavaScript Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
if request.PreCondition != "" {
|
||||
@@ -766,6 +766,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
|
||||
@@ -85,6 +85,10 @@ type Request struct {
|
||||
// SelfContained specifies if the request is self-contained.
|
||||
SelfContained bool `yaml:"-" json:"-"`
|
||||
|
||||
// description: |
|
||||
// StopAtFirstMatch stops the execution of the requests and template as soon as a match is found.
|
||||
StopAtFirstMatch bool `yaml:"stop-at-first-match,omitempty" json:"stop-at-first-match,omitempty" jsonschema:"title=stop at first match,description=Stop the execution after a match is found"`
|
||||
|
||||
// description: |
|
||||
// ports is post processed list of ports to scan (obtained from Port)
|
||||
ports []string `yaml:"-" json:"-"`
|
||||
|
||||
@@ -100,13 +100,14 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
fields.Ip = types.ToString(wrapped.InternalEvent["ip"])
|
||||
}
|
||||
data := &output.ResultEvent{
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
URL: fields.URL,
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
URL: fields.URL,
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
ExtractedResults: wrapped.OperatorsResult.OutputExtracts,
|
||||
Metadata: wrapped.OperatorsResult.PayloadValues,
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
@@ -99,6 +100,16 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
gologger.Verbose().Msgf("[%v] got errors while checking open ports: %s\n", request.options.TemplateID, err)
|
||||
}
|
||||
|
||||
// stop at first match if requested
|
||||
atomicBool := &atomic.Bool{}
|
||||
shouldStopAtFirstMatch := request.StopAtFirstMatch || request.options.StopAtFirstMatch || request.options.Options.StopAtFirstMatch
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
if event != nil && event.HasOperatorResult() {
|
||||
atomicBool.Store(true)
|
||||
}
|
||||
callback(event)
|
||||
}
|
||||
|
||||
for _, port := range ports {
|
||||
input := target.Clone()
|
||||
// use network port updates input with new port requested in template file
|
||||
@@ -107,9 +118,12 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
if err := input.UseNetworkPort(port, request.ExcludePorts); err != nil {
|
||||
gologger.Debug().Msgf("Could not network port from constants: %s\n", err)
|
||||
}
|
||||
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, callback); err != nil {
|
||||
if err := request.executeOnTarget(input, visitedAddresses, metadata, previous, wrappedCallback); err != nil {
|
||||
return err
|
||||
}
|
||||
if shouldStopAtFirstMatch && atomicBool.Load() {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
@@ -141,6 +155,16 @@ func (request *Request) executeOnTarget(input *contextargs.Context, visited maps
|
||||
variablesMap := request.options.Variables.Evaluate(variables)
|
||||
variables = generators.MergeMaps(variablesMap, variables, request.options.Constants)
|
||||
|
||||
// stop at first match if requested
|
||||
atomicBool := &atomic.Bool{}
|
||||
shouldStopAtFirstMatch := request.StopAtFirstMatch || request.options.StopAtFirstMatch || request.options.Options.StopAtFirstMatch
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
if event != nil && event.HasOperatorResult() {
|
||||
atomicBool.Store(true)
|
||||
}
|
||||
callback(event)
|
||||
}
|
||||
|
||||
for _, kv := range request.addresses {
|
||||
select {
|
||||
case <-input.Context().Done():
|
||||
@@ -154,12 +178,13 @@ func (request *Request) executeOnTarget(input *contextargs.Context, visited maps
|
||||
continue
|
||||
}
|
||||
visited.Set(actualAddress, struct{}{})
|
||||
|
||||
if err = request.executeAddress(variables, actualAddress, address, input, kv.tls, previous, callback); err != nil {
|
||||
if err = request.executeAddress(variables, actualAddress, address, input, kv.tls, previous, wrappedCallback); err != nil {
|
||||
outputEvent := request.responseToDSLMap("", "", "", address, "")
|
||||
callback(&output.InternalWrappedEvent{InternalEvent: outputEvent})
|
||||
gologger.Warning().Msgf("[%v] Could not make network request for (%s) : %s\n", request.options.TemplateID, actualAddress, err)
|
||||
continue
|
||||
}
|
||||
if shouldStopAtFirstMatch && atomicBool.Load() {
|
||||
break
|
||||
}
|
||||
}
|
||||
return err
|
||||
@@ -283,36 +308,37 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
interimValues := generators.MergeMaps(variables, payloads)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Network Protocol request variables: \n%s\n", vardump.DumpVariables(interimValues))
|
||||
gologger.Debug().Msgf("Network Protocol request variables: %s\n", vardump.DumpVariables(interimValues))
|
||||
}
|
||||
|
||||
inputEvents := make(map[string]interface{})
|
||||
|
||||
for _, input := range request.Inputs {
|
||||
data := []byte(input.Data)
|
||||
dataInBytes := []byte(input.Data)
|
||||
var err error
|
||||
|
||||
if request.options.Interactsh != nil {
|
||||
var transformedData string
|
||||
transformedData, interactshURLs = request.options.Interactsh.Replace(string(data), []string{})
|
||||
data = []byte(transformedData)
|
||||
}
|
||||
|
||||
finalData, err := expressions.EvaluateByte(data, interimValues)
|
||||
dataInBytes, err = expressions.EvaluateByte(dataInBytes, interimValues)
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not evaluate template expressions")
|
||||
}
|
||||
|
||||
reqBuilder.Write(finalData)
|
||||
data := string(dataInBytes)
|
||||
if request.options.Interactsh != nil {
|
||||
data, interactshURLs = request.options.Interactsh.Replace(data, []string{})
|
||||
dataInBytes = []byte(data)
|
||||
}
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(string(finalData)); err != nil {
|
||||
reqBuilder.Write(dataInBytes)
|
||||
|
||||
if err := expressions.ContainsUnresolvedVariables(data); err != nil {
|
||||
gologger.Warning().Msgf("[%s] Could not make network request for %s: %v\n", request.options.TemplateID, actualAddress, err)
|
||||
return nil
|
||||
}
|
||||
|
||||
if input.Type.GetType() == hexType {
|
||||
finalData, err = hex.DecodeString(string(finalData))
|
||||
dataInBytes, err = hex.DecodeString(data)
|
||||
if err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
@@ -320,7 +346,7 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := conn.Write(finalData); err != nil {
|
||||
if _, err := conn.Write(dataInBytes); err != nil {
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not write request to server")
|
||||
@@ -504,14 +530,14 @@ func (request *Request) markUnresponsiveAddress(input *contextargs.Context, err
|
||||
return
|
||||
}
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(input, err)
|
||||
request.options.HostErrorsCache.MarkFailed(request.options.ProtocolType.String(), input, err)
|
||||
}
|
||||
}
|
||||
|
||||
// isUnresponsiveAddress checks if the error is a unreponsive based on its execution history
|
||||
func (request *Request) isUnresponsiveAddress(input *contextargs.Context) bool {
|
||||
if request.options.HostErrorsCache != nil {
|
||||
return request.options.HostErrorsCache.Check(input)
|
||||
return request.options.HostErrorsCache.Check(request.options.ProtocolType.String(), input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -142,6 +142,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Path: types.ToString(wrapped.InternalEvent["path"]),
|
||||
Matched: types.ToString(wrapped.InternalEvent["matched"]),
|
||||
|
||||
@@ -25,6 +25,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/projectfile"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/hosterrorscache"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/interactsh"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/excludematchers"
|
||||
@@ -61,6 +62,8 @@ type ExecutorOptions struct {
|
||||
TemplatePath string
|
||||
// TemplateInfo contains information block of the template request
|
||||
TemplateInfo model.Info
|
||||
// TemplateVerifier is the verifier for the template
|
||||
TemplateVerifier string
|
||||
// RawTemplate is the raw template for the request
|
||||
RawTemplate []byte
|
||||
// Output is a writer interface for writing output events from executer.
|
||||
@@ -124,6 +127,8 @@ type ExecutorOptions struct {
|
||||
// ExportReqURLPattern exports the request URL pattern
|
||||
// in ResultEvent it contains the exact url pattern (ex: {{BaseURL}}/{{randstr}}/xyz) used in the request
|
||||
ExportReqURLPattern bool
|
||||
// GlobalMatchers is the storage for global matchers with http passive templates
|
||||
GlobalMatchers *globalmatchers.Storage
|
||||
}
|
||||
|
||||
// todo: centralizing components is not feasible with current clogged architecture
|
||||
|
||||
@@ -222,7 +222,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
payloadValues = generators.MergeMaps(variablesMap, payloadValues, request.options.Constants)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("SSL Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("SSL Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
finalAddress, dataErr := expressions.EvaluateByte([]byte(request.Address), payloadValues)
|
||||
@@ -347,11 +347,33 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
// description. Multiple definitions are separated by commas.
|
||||
// Definitions not having a name (generated on runtime) are prefixed & suffixed by <>.
|
||||
var RequestPartDefinitions = map[string]string{
|
||||
"type": "Type is the type of request made",
|
||||
"response": "JSON SSL protocol handshake details",
|
||||
"not_after": "Timestamp after which the remote cert expires",
|
||||
"host": "Host is the input to the template",
|
||||
"matched": "Matched is the input which was matched upon",
|
||||
"template-id": "ID of the template executed",
|
||||
"template-info": "Info Block of the template executed",
|
||||
"template-path": "Path of the template executed",
|
||||
"host": "Host is the input to the template",
|
||||
"port": "Port is the port of the host",
|
||||
"matched": "Matched is the input which was matched upon",
|
||||
"type": "Type is the type of request made",
|
||||
"timestamp": "Timestamp is the time when the request was made",
|
||||
"response": "JSON SSL protocol handshake details",
|
||||
"cipher": "Cipher is the encryption algorithm used",
|
||||
"domains": "Domains are the list of domain names in the certificate",
|
||||
"fingerprint_hash": "Fingerprint hash is the unique identifier of the certificate",
|
||||
"ip": "IP is the IP address of the server",
|
||||
"issuer_cn": "Issuer CN is the common name of the certificate issuer",
|
||||
"issuer_dn": "Issuer DN is the distinguished name of the certificate issuer",
|
||||
"issuer_org": "Issuer organization is the organization of the certificate issuer",
|
||||
"not_after": "Timestamp after which the remote cert expires",
|
||||
"not_before": "Timestamp before which the certificate is not valid",
|
||||
"probe_status": "Probe status indicates if the probe was successful",
|
||||
"serial": "Serial is the serial number of the certificate",
|
||||
"sni": "SNI is the server name indication used in the handshake",
|
||||
"subject_an": "Subject AN is the list of subject alternative names",
|
||||
"subject_cn": "Subject CN is the common name of the certificate subject",
|
||||
"subject_dn": "Subject DN is the distinguished name of the certificate subject",
|
||||
"subject_org": "Subject organization is the organization of the certificate subject",
|
||||
"tls_connection": "TLS connection is the type of TLS connection used",
|
||||
"tls_version": "TLS version is the version of the TLS protocol used",
|
||||
}
|
||||
|
||||
// Match performs matching operation for a matcher on model and returns:
|
||||
@@ -398,6 +420,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(wrapped.InternalEvent["template-id"]),
|
||||
TemplatePath: types.ToString(wrapped.InternalEvent["template-path"]),
|
||||
Info: wrapped.InternalEvent["template-info"].(model.Info),
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
|
||||
@@ -207,7 +207,7 @@ func (request *Request) executeRequestWithPayloads(target *contextargs.Context,
|
||||
}
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Websocket Protocol request variables: \n%s\n", vardump.DumpVariables(payloadValues))
|
||||
gologger.Debug().Msgf("WebSocket Protocol request variables: %s\n", vardump.DumpVariables(payloadValues))
|
||||
}
|
||||
|
||||
finalAddress, dataErr := expressions.EvaluateByte([]byte(request.Address), payloadValues)
|
||||
@@ -402,6 +402,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(request.options.TemplateID),
|
||||
TemplatePath: types.ToString(request.options.TemplatePath),
|
||||
Info: request.options.TemplateInfo,
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: fields.Host,
|
||||
Port: fields.Port,
|
||||
|
||||
@@ -99,7 +99,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
variables := generators.MergeMaps(vars, defaultVars, optionVars, dynamicValues, request.options.Constants)
|
||||
|
||||
if vardump.EnableVarDump {
|
||||
gologger.Debug().Msgf("Whois Protocol request variables: \n%s\n", vardump.DumpVariables(variables))
|
||||
gologger.Debug().Msgf("Whois Protocol request variables: %s\n", vardump.DumpVariables(variables))
|
||||
}
|
||||
|
||||
// and replace placeholders
|
||||
@@ -177,6 +177,7 @@ func (request *Request) MakeResultEventItem(wrapped *output.InternalWrappedEvent
|
||||
TemplateID: types.ToString(request.options.TemplateID),
|
||||
TemplatePath: types.ToString(request.options.TemplatePath),
|
||||
Info: request.options.TemplateInfo,
|
||||
TemplateVerifier: request.options.TemplateVerifier,
|
||||
Type: types.ToString(wrapped.InternalEvent["type"]),
|
||||
Host: types.ToString(wrapped.InternalEvent["host"]),
|
||||
Metadata: wrapped.OperatorsResult.PayloadValues,
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
package mongo
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"go.mongodb.org/mongo-driver/mongo"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
mongooptions "go.mongodb.org/mongo-driver/mongo/options"
|
||||
)
|
||||
|
||||
type Exporter struct {
|
||||
options *Options
|
||||
mutex *sync.Mutex
|
||||
rows []output.ResultEvent
|
||||
collection *mongo.Collection
|
||||
connection *mongo.Client
|
||||
}
|
||||
|
||||
// Options contains the configuration options for MongoDB exporter client
|
||||
type Options struct {
|
||||
// ConnectionString is the connection string to the MongoDB database
|
||||
ConnectionString string `yaml:"connection-string"`
|
||||
// CollectionName is the name of the MongoDB collection in which to store the results
|
||||
CollectionName string `yaml:"collection-name"`
|
||||
// OmitRaw excludes the Request and Response from the results (helps with filesize)
|
||||
OmitRaw bool `yaml:"omit-raw"`
|
||||
// BatchSize determines the number of results to be kept in memory before writing it to the database or 0 to
|
||||
// persist all in memory and write all results at the end (default)
|
||||
BatchSize int `yaml:"batch-size"`
|
||||
}
|
||||
|
||||
// New creates a new MongoDB exporter integration client based on options.
|
||||
func New(options *Options) (*Exporter, error) {
|
||||
exporter := &Exporter{
|
||||
mutex: &sync.Mutex{},
|
||||
options: options,
|
||||
rows: []output.ResultEvent{},
|
||||
}
|
||||
|
||||
// If the environment variable for the connection string is set, then use that instead. This allows for easier
|
||||
// management of sensitive items such as credentials
|
||||
envConnectionString := os.Getenv("MONGO_CONNECTION_STRING")
|
||||
if envConnectionString != "" {
|
||||
options.ConnectionString = envConnectionString
|
||||
gologger.Info().Msgf("Using connection string from environment variable MONGO_CONNECTION_STRING")
|
||||
}
|
||||
|
||||
// Create the connection to the database
|
||||
clientOptions := mongooptions.Client().ApplyURI(options.ConnectionString)
|
||||
|
||||
// Create a new client and connect to the MongoDB server
|
||||
client, err := mongo.Connect(context.TODO(), clientOptions)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Error creating MongoDB client: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Ensure the connection is valid
|
||||
err = client.Ping(context.Background(), nil)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Error connecting to MongoDB: %s", err)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Get the database from the connection string to set the database and collection
|
||||
parsed, err := url.Parse(options.ConnectionString)
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Error parsing connection string: %s", options.ConnectionString)
|
||||
return nil, err
|
||||
}
|
||||
|
||||
databaseName := strings.TrimPrefix(parsed.Path, "/")
|
||||
|
||||
if databaseName == "" {
|
||||
return nil, errors.New("error getting database name from connection string")
|
||||
}
|
||||
|
||||
exporter.connection = client
|
||||
exporter.collection = client.Database(databaseName).Collection(options.CollectionName)
|
||||
|
||||
return exporter, nil
|
||||
}
|
||||
|
||||
// Export writes a result document to the configured MongoDB collection
|
||||
// in the database configured by the connection string
|
||||
func (exporter *Exporter) Export(event *output.ResultEvent) error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
if exporter.options.OmitRaw {
|
||||
event.Request = ""
|
||||
event.Response = ""
|
||||
}
|
||||
|
||||
// Add the row to the queue to be processed
|
||||
exporter.rows = append(exporter.rows, *event)
|
||||
|
||||
// If the batch size is greater than 0 and the number of rows has reached the batch, flush it to the database
|
||||
if exporter.options.BatchSize > 0 && len(exporter.rows) >= exporter.options.BatchSize {
|
||||
err := exporter.WriteRows()
|
||||
if err != nil {
|
||||
// The error is already logged, return it to bubble up to the caller
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// WriteRows writes all rows from the rows list to the MongoDB collection and removes them from the list
|
||||
func (exporter *Exporter) WriteRows() error {
|
||||
// Loop through the rows and write them, removing them as they're entered
|
||||
for len(exporter.rows) > 0 {
|
||||
data := exporter.rows[0]
|
||||
|
||||
// Write the data to the database
|
||||
_, err := exporter.collection.InsertOne(context.TODO(), data)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Error inserting record into MongoDB collection: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
// Remove the item from the list
|
||||
exporter.rows = exporter.rows[1:]
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (exporter *Exporter) Close() error {
|
||||
exporter.mutex.Lock()
|
||||
defer exporter.mutex.Unlock()
|
||||
|
||||
// Write all pending rows
|
||||
err := exporter.WriteRows()
|
||||
if err != nil {
|
||||
// The error is already logged, return it to bubble up to the caller
|
||||
return err
|
||||
}
|
||||
|
||||
// Close the database connection
|
||||
err = exporter.connection.Disconnect(context.TODO())
|
||||
if err != nil {
|
||||
gologger.Error().Msgf("Error disconnecting from MongoDB: %s", err)
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/jsonexporter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/jsonl"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/mongo"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/sarif"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/splunk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/filters"
|
||||
@@ -44,6 +45,8 @@ type Options struct {
|
||||
JSONExporter *jsonexporter.Options `yaml:"json"`
|
||||
// JSONLExporter contains configuration options for JSONL Exporter Module
|
||||
JSONLExporter *jsonl.Options `yaml:"jsonl"`
|
||||
// MongoDBExporter containers the configuration options for the MongoDB Exporter Module
|
||||
MongoDBExporter *mongo.Options `yaml:"mongodb"`
|
||||
|
||||
HttpClient *retryablehttp.Client `yaml:"-"`
|
||||
OmitRaw bool `yaml:"-"`
|
||||
|
||||
@@ -2,6 +2,7 @@ package reporting
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/mongo"
|
||||
"os"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
@@ -166,6 +167,13 @@ func New(options *Options, db string, doNotDedupe bool) (Client, error) {
|
||||
}
|
||||
client.exporters = append(client.exporters, exporter)
|
||||
}
|
||||
if options.MongoDBExporter != nil {
|
||||
exporter, err := mongo.New(options.MongoDBExporter)
|
||||
if err != nil {
|
||||
return nil, errorutil.NewWithErr(err).Wrap(ErrExportClientCreation)
|
||||
}
|
||||
client.exporters = append(client.exporters, exporter)
|
||||
}
|
||||
|
||||
if doNotDedupe {
|
||||
return client, nil
|
||||
@@ -212,6 +220,7 @@ func CreateConfigIfNotExists() error {
|
||||
SplunkExporter: &splunk.Options{},
|
||||
JSONExporter: &json_exporter.Options{},
|
||||
JSONLExporter: &jsonl.Options{},
|
||||
MongoDBExporter: &mongo.Options{},
|
||||
}
|
||||
reportingFile, err := os.Create(reportingConfig)
|
||||
if err != nil {
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
type ScanContextOption func(*ScanContext)
|
||||
@@ -30,7 +31,7 @@ type ScanContext struct {
|
||||
OnWarning func(string)
|
||||
|
||||
// unexported state fields
|
||||
errors []error
|
||||
error error
|
||||
warnings []string
|
||||
events []*output.InternalWrappedEvent
|
||||
results []*output.ResultEvent
|
||||
@@ -52,8 +53,8 @@ func (s *ScanContext) Context() context.Context {
|
||||
return s.ctx
|
||||
}
|
||||
|
||||
func (s *ScanContext) GenerateErrorMessage() string {
|
||||
return joinErrors(s.errors)
|
||||
func (s *ScanContext) GenerateErrorMessage() error {
|
||||
return s.error
|
||||
}
|
||||
|
||||
// GenerateResult returns final results slice from all events
|
||||
@@ -94,13 +95,16 @@ func (s *ScanContext) LogError(err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.OnError != nil {
|
||||
s.OnError(err)
|
||||
}
|
||||
s.errors = append(s.errors, err)
|
||||
if s.error == nil {
|
||||
s.error = err
|
||||
} else {
|
||||
s.error = errkit.Append(s.error, err)
|
||||
}
|
||||
|
||||
errorMessage := s.GenerateErrorMessage()
|
||||
errorMessage := s.GenerateErrorMessage().Error()
|
||||
|
||||
for _, result := range s.results {
|
||||
result.Error = errorMessage
|
||||
@@ -129,14 +133,3 @@ func (s *ScanContext) LogWarning(format string, args ...any) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// joinErrors joins multiple errors and returns a single error string
|
||||
func joinErrors(errors []error) string {
|
||||
var errorMessages []string
|
||||
for _, e := range errors {
|
||||
if e != nil {
|
||||
errorMessages = append(errorMessages, e.Error())
|
||||
}
|
||||
}
|
||||
return strings.Join(errorMessages, "; ")
|
||||
}
|
||||
@@ -251,28 +251,30 @@ func (e *ClusterExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
event.InternalEvent = make(map[string]interface{})
|
||||
}
|
||||
for _, operator := range e.operators {
|
||||
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
|
||||
event.InternalEvent["template-id"] = operator.templateID
|
||||
event.InternalEvent["template-path"] = operator.templatePath
|
||||
event.InternalEvent["template-info"] = operator.templateInfo
|
||||
clonedEvent := event.CloneShallow()
|
||||
|
||||
result, matched := operator.operator.Execute(clonedEvent.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
|
||||
clonedEvent.InternalEvent["template-id"] = operator.templateID
|
||||
clonedEvent.InternalEvent["template-path"] = operator.templatePath
|
||||
clonedEvent.InternalEvent["template-info"] = operator.templateInfo
|
||||
|
||||
if result == nil && !matched && e.options.Options.MatcherStatus {
|
||||
if err := e.options.Output.WriteFailure(event); err != nil {
|
||||
if err := e.options.Output.WriteFailure(clonedEvent); err != nil {
|
||||
gologger.Warning().Msgf("Could not write failure event to output: %s\n", err)
|
||||
}
|
||||
continue
|
||||
}
|
||||
if matched && result != nil {
|
||||
event.OperatorsResult = result
|
||||
event.Results = e.requests.MakeResultEvent(event)
|
||||
clonedEvent.OperatorsResult = result
|
||||
clonedEvent.Results = e.requests.MakeResultEvent(clonedEvent)
|
||||
results = true
|
||||
|
||||
_ = writer.WriteResult(event, e.options.Output, e.options.Progress, e.options.IssuesClient)
|
||||
_ = writer.WriteResult(clonedEvent, e.options.Output, e.options.Progress, e.options.IssuesClient)
|
||||
}
|
||||
}
|
||||
})
|
||||
if err != nil && e.options.HostErrorsCache != nil {
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
e.options.HostErrorsCache.MarkFailed(e.options.ProtocolType.String(), ctx.Input, err)
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
@@ -290,14 +292,16 @@ func (e *ClusterExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.R
|
||||
}
|
||||
err := e.requests.ExecuteWithResults(inputItem, dynamicValues, nil, func(event *output.InternalWrappedEvent) {
|
||||
for _, operator := range e.operators {
|
||||
result, matched := operator.operator.Execute(event.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
|
||||
clonedEvent := event.CloneShallow()
|
||||
|
||||
result, matched := operator.operator.Execute(clonedEvent.InternalEvent, e.requests.Match, e.requests.Extract, e.options.Options.Debug || e.options.Options.DebugResponse)
|
||||
if matched && result != nil {
|
||||
event.OperatorsResult = result
|
||||
event.InternalEvent["template-id"] = operator.templateID
|
||||
event.InternalEvent["template-path"] = operator.templatePath
|
||||
event.InternalEvent["template-info"] = operator.templateInfo
|
||||
event.Results = e.requests.MakeResultEvent(event)
|
||||
scanCtx.LogEvent(event)
|
||||
clonedEvent.OperatorsResult = result
|
||||
clonedEvent.InternalEvent["template-id"] = operator.templateID
|
||||
clonedEvent.InternalEvent["template-path"] = operator.templatePath
|
||||
clonedEvent.InternalEvent["template-info"] = operator.templateInfo
|
||||
clonedEvent.Results = e.requests.MakeResultEvent(clonedEvent)
|
||||
scanCtx.LogEvent(clonedEvent)
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -306,7 +310,7 @@ func (e *ClusterExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.R
|
||||
}
|
||||
|
||||
if err != nil && e.options.HostErrorsCache != nil {
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
e.options.HostErrorsCache.MarkFailed(e.options.ProtocolType.String(), ctx.Input, err)
|
||||
}
|
||||
return scanCtx.GenerateResult(), err
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/globalmatchers"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/offlinehttp"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/signer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec"
|
||||
@@ -36,8 +37,7 @@ var (
|
||||
)
|
||||
|
||||
const (
|
||||
Unsigned = "unsigned"
|
||||
PDVerifier = "projectdiscovery/nuclei-templates"
|
||||
Unsigned = "unsigned"
|
||||
)
|
||||
|
||||
func init() {
|
||||
@@ -82,6 +82,18 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Executo
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if template.isGlobalMatchersEnabled() {
|
||||
item := &globalmatchers.Item{
|
||||
TemplateID: template.ID,
|
||||
TemplatePath: filePath,
|
||||
TemplateInfo: template.Info,
|
||||
}
|
||||
for _, request := range template.RequestsHTTP {
|
||||
item.Operators = append(item.Operators, request.CompiledOperators)
|
||||
}
|
||||
options.GlobalMatchers.AddOperator(item)
|
||||
return nil, nil
|
||||
}
|
||||
// Compile the workflow request
|
||||
if len(template.Workflows) > 0 {
|
||||
compiled := &template.Workflow
|
||||
@@ -97,6 +109,25 @@ func Parse(filePath string, preprocessor Preprocessor, options protocols.Executo
|
||||
return template, nil
|
||||
}
|
||||
|
||||
// isGlobalMatchersEnabled checks if any of requests in the template
|
||||
// have global matchers enabled. It iterates through all requests and
|
||||
// returns true if at least one request has global matchers enabled;
|
||||
// otherwise, it returns false.
|
||||
//
|
||||
// Note: This method only checks the `RequestsHTTP`
|
||||
// field of the template, which is specific to http-protocol-based
|
||||
// templates.
|
||||
//
|
||||
// TODO: support all protocols.
|
||||
func (template *Template) isGlobalMatchersEnabled() bool {
|
||||
for _, request := range template.RequestsHTTP {
|
||||
if request.GlobalMatchers {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseSelfContainedRequests parses the self contained template requests.
|
||||
func (template *Template) parseSelfContainedRequests() {
|
||||
if template.Signature.Value.String() != "" {
|
||||
@@ -420,7 +451,7 @@ func parseTemplate(data []byte, options protocols.ExecutorOptions) (*Template, e
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
options.TemplateVerifier = template.TemplateVerifier
|
||||
if !(template.Verified && verifier.Identifier() == "projectdiscovery/nuclei-templates") {
|
||||
template.Options.RawTemplate = data
|
||||
}
|
||||
|
||||
@@ -459,7 +459,7 @@ func init() {
|
||||
Value: "HTTP response headers in name:value format",
|
||||
},
|
||||
}
|
||||
HTTPRequestDoc.Fields = make([]encoder.Doc, 36)
|
||||
HTTPRequestDoc.Fields = make([]encoder.Doc, 37)
|
||||
HTTPRequestDoc.Fields[0].Name = "path"
|
||||
HTTPRequestDoc.Fields[0].Type = "[]string"
|
||||
HTTPRequestDoc.Fields[0].Note = ""
|
||||
@@ -668,6 +668,11 @@ func init() {
|
||||
HTTPRequestDoc.Fields[35].Note = ""
|
||||
HTTPRequestDoc.Fields[35].Description = "FuzzPreConditionOperator is the operator between multiple PreConditions for fuzzing Default is OR"
|
||||
HTTPRequestDoc.Fields[35].Comments[encoder.LineComment] = "FuzzPreConditionOperator is the operator between multiple PreConditions for fuzzing Default is OR"
|
||||
HTTPRequestDoc.Fields[36].Name = "global-matchers"
|
||||
HTTPRequestDoc.Fields[36].Type = "bool"
|
||||
HTTPRequestDoc.Fields[36].Note = ""
|
||||
HTTPRequestDoc.Fields[36].Description = "GlobalMatchers marks matchers as static and applies globally to all result events from other templates"
|
||||
HTTPRequestDoc.Fields[36].Comments[encoder.LineComment] = "GlobalMatchers marks matchers as static and applies globally to all result events from other templates"
|
||||
|
||||
GENERATORSAttackTypeHolderDoc.Type = "generators.AttackTypeHolder"
|
||||
GENERATORSAttackTypeHolderDoc.Comments[encoder.LineComment] = " AttackTypeHolder is used to hold internal type of the protocol"
|
||||
@@ -1329,7 +1334,7 @@ func init() {
|
||||
Value: "Full Network protocol data",
|
||||
},
|
||||
}
|
||||
NETWORKRequestDoc.Fields = make([]encoder.Doc, 10)
|
||||
NETWORKRequestDoc.Fields = make([]encoder.Doc, 11)
|
||||
NETWORKRequestDoc.Fields[0].Name = "id"
|
||||
NETWORKRequestDoc.Fields[0].Type = "string"
|
||||
NETWORKRequestDoc.Fields[0].Note = ""
|
||||
@@ -1388,6 +1393,11 @@ func init() {
|
||||
NETWORKRequestDoc.Fields[9].Comments[encoder.LineComment] = "ReadAll determines if the data stream should be read till the end regardless of the size"
|
||||
|
||||
NETWORKRequestDoc.Fields[9].AddExample("", false)
|
||||
NETWORKRequestDoc.Fields[10].Name = "stop-at-first-match"
|
||||
NETWORKRequestDoc.Fields[10].Type = "bool"
|
||||
NETWORKRequestDoc.Fields[10].Note = ""
|
||||
NETWORKRequestDoc.Fields[10].Description = "StopAtFirstMatch stops the execution of the requests and template as soon as a match is found."
|
||||
NETWORKRequestDoc.Fields[10].Comments[encoder.LineComment] = "StopAtFirstMatch stops the execution of the requests and template as soon as a match is found."
|
||||
|
||||
NETWORKInputDoc.Type = "network.Input"
|
||||
NETWORKInputDoc.Comments[encoder.LineComment] = ""
|
||||
@@ -1603,7 +1613,12 @@ func init() {
|
||||
"time",
|
||||
"select",
|
||||
"files",
|
||||
"waitdom",
|
||||
"waitfcp",
|
||||
"waitfmp",
|
||||
"waitidle",
|
||||
"waitload",
|
||||
"waitstable",
|
||||
"getresource",
|
||||
"extract",
|
||||
"setmethod",
|
||||
@@ -1652,25 +1667,113 @@ func init() {
|
||||
}
|
||||
SSLRequestDoc.PartDefinitions = []encoder.KeyValue{
|
||||
{
|
||||
Key: "type",
|
||||
Value: "Type is the type of request made",
|
||||
Key: "template-id",
|
||||
Value: "ID of the template executed",
|
||||
},
|
||||
{
|
||||
Key: "response",
|
||||
Value: "JSON SSL protocol handshake details",
|
||||
Key: "template-info",
|
||||
Value: "Info Block of the template executed",
|
||||
},
|
||||
{
|
||||
Key: "not_after",
|
||||
Value: "Timestamp after which the remote cert expires",
|
||||
Key: "template-path",
|
||||
Value: "Path of the template executed",
|
||||
},
|
||||
{
|
||||
Key: "host",
|
||||
Value: "Host is the input to the template",
|
||||
},
|
||||
{
|
||||
Key: "port",
|
||||
Value: "Port is the port of the host",
|
||||
},
|
||||
{
|
||||
Key: "matched",
|
||||
Value: "Matched is the input which was matched upon",
|
||||
},
|
||||
{
|
||||
Key: "type",
|
||||
Value: "Type is the type of request made",
|
||||
},
|
||||
{
|
||||
Key: "timestamp",
|
||||
Value: "Timestamp is the time when the request was made",
|
||||
},
|
||||
{
|
||||
Key: "response",
|
||||
Value: "JSON SSL protocol handshake details",
|
||||
},
|
||||
{
|
||||
Key: "cipher",
|
||||
Value: "Cipher is the encryption algorithm used",
|
||||
},
|
||||
{
|
||||
Key: "domains",
|
||||
Value: "Domains are the list of domain names in the certificate",
|
||||
},
|
||||
{
|
||||
Key: "fingerprint_hash",
|
||||
Value: "Fingerprint hash is the unique identifier of the certificate",
|
||||
},
|
||||
{
|
||||
Key: "ip",
|
||||
Value: "IP is the IP address of the server",
|
||||
},
|
||||
{
|
||||
Key: "issuer_cn",
|
||||
Value: "Issuer CN is the common name of the certificate issuer",
|
||||
},
|
||||
{
|
||||
Key: "issuer_dn",
|
||||
Value: "Issuer DN is the distinguished name of the certificate issuer",
|
||||
},
|
||||
{
|
||||
Key: "issuer_org",
|
||||
Value: "Issuer organization is the organization of the certificate issuer",
|
||||
},
|
||||
{
|
||||
Key: "not_after",
|
||||
Value: "Timestamp after which the remote cert expires",
|
||||
},
|
||||
{
|
||||
Key: "not_before",
|
||||
Value: "Timestamp before which the certificate is not valid",
|
||||
},
|
||||
{
|
||||
Key: "probe_status",
|
||||
Value: "Probe status indicates if the probe was successful",
|
||||
},
|
||||
{
|
||||
Key: "serial",
|
||||
Value: "Serial is the serial number of the certificate",
|
||||
},
|
||||
{
|
||||
Key: "sni",
|
||||
Value: "SNI is the server name indication used in the handshake",
|
||||
},
|
||||
{
|
||||
Key: "subject_an",
|
||||
Value: "Subject AN is the list of subject alternative names",
|
||||
},
|
||||
{
|
||||
Key: "subject_cn",
|
||||
Value: "Subject CN is the common name of the certificate subject",
|
||||
},
|
||||
{
|
||||
Key: "subject_dn",
|
||||
Value: "Subject DN is the distinguished name of the certificate subject",
|
||||
},
|
||||
{
|
||||
Key: "subject_org",
|
||||
Value: "Subject organization is the organization of the certificate subject",
|
||||
},
|
||||
{
|
||||
Key: "tls_connection",
|
||||
Value: "TLS connection is the type of TLS connection used",
|
||||
},
|
||||
{
|
||||
Key: "tls_version",
|
||||
Value: "TLS version is the version of the TLS protocol used",
|
||||
},
|
||||
}
|
||||
SSLRequestDoc.Fields = make([]encoder.Doc, 9)
|
||||
SSLRequestDoc.Fields[0].Name = "id"
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"github.com/pkg/errors"
|
||||
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/keys"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/stats"
|
||||
@@ -105,7 +106,7 @@ func parseWorkflowTemplate(workflow *workflows.WorkflowTemplate, preprocessor Pr
|
||||
// increment signed/unsigned counters
|
||||
if template.Verified {
|
||||
if template.TemplateVerifier == "" {
|
||||
SignatureStats[PDVerifier].Add(1)
|
||||
SignatureStats[keys.PDVerifier].Add(1)
|
||||
} else {
|
||||
SignatureStats[template.TemplateVerifier].Add(1)
|
||||
}
|
||||
|
||||
@@ -167,7 +167,7 @@ func (m *MockOutputWriter) WriteFailure(wrappedEvent *output.InternalWrappedEven
|
||||
|
||||
// create event
|
||||
event := wrappedEvent.InternalEvent
|
||||
templatePath, templateURL := utils.TemplatePathURL(types.ToString(event["template-path"]), types.ToString(event["template-id"]))
|
||||
templatePath, templateURL := utils.TemplatePathURL(types.ToString(event["template-path"]), types.ToString(event["template-id"]), types.ToString(event["template-verifier"]))
|
||||
var templateInfo model.Info
|
||||
if ti, ok := event["template-info"].(model.Info); ok {
|
||||
templateInfo = ti
|
||||
|
||||
@@ -20,7 +20,6 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/flow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/generic"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/multiproto"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
@@ -175,7 +174,15 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
if !event.HasOperatorResult() && event.InternalEvent != nil {
|
||||
lastMatcherEvent = event
|
||||
} else {
|
||||
if writer.WriteResult(event, e.options.Output, e.options.Progress, e.options.IssuesClient) {
|
||||
var isGlobalMatchers bool
|
||||
isGlobalMatchers, _ = event.InternalEvent["global-matchers"].(bool)
|
||||
// NOTE(dwisiswant0): Don't store `matched` on a `global-matchers` template.
|
||||
// This will end up generating 2 events from the same `scan.ScanContext` if
|
||||
// one of the templates has `global-matchers` enabled. This way,
|
||||
// non-`global-matchers` templates can enter the `writeFailureCallback`
|
||||
// func to log failure output.
|
||||
wr := writer.WriteResult(event, e.options.Output, e.options.Progress, e.options.IssuesClient)
|
||||
if wr && !isGlobalMatchers {
|
||||
matched.Store(true)
|
||||
} else {
|
||||
lastMatcherEvent = event
|
||||
@@ -207,7 +214,9 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
ctx.LogError(errx)
|
||||
|
||||
if lastMatcherEvent != nil {
|
||||
lastMatcherEvent.InternalEvent["error"] = tryParseCause(fmt.Errorf("%s", ctx.GenerateErrorMessage()))
|
||||
lastMatcherEvent.Lock()
|
||||
lastMatcherEvent.InternalEvent["error"] = getErrorCause(ctx.GenerateErrorMessage())
|
||||
lastMatcherEvent.Unlock()
|
||||
writeFailureCallback(lastMatcherEvent, e.options.Options.MatcherStatus)
|
||||
}
|
||||
|
||||
@@ -222,7 +231,7 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
Info: e.options.TemplateInfo,
|
||||
Type: e.getTemplateType(),
|
||||
Host: ctx.Input.MetaInput.Input,
|
||||
Error: tryParseCause(fmt.Errorf("%s", ctx.GenerateErrorMessage())),
|
||||
Error: getErrorCause(ctx.GenerateErrorMessage()),
|
||||
},
|
||||
},
|
||||
OperatorsResult: &operators.Result{
|
||||
@@ -235,31 +244,27 @@ func (e *TemplateExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
return executed.Load() || matched.Load(), errx
|
||||
}
|
||||
|
||||
// tryParseCause tries to parse the cause of given error
|
||||
// getErrorCause tries to parse the cause of given error
|
||||
// this is legacy support due to use of errorutil in existing libraries
|
||||
// but this should not be required once all libraries are updated
|
||||
func tryParseCause(err error) string {
|
||||
errStr := ""
|
||||
errX := errkit.FromError(err)
|
||||
if errX != nil {
|
||||
var errCause error
|
||||
|
||||
if len(errX.Errors()) > 1 {
|
||||
errCause = errX.Errors()[0]
|
||||
}
|
||||
if errCause == nil {
|
||||
errCause = errX
|
||||
}
|
||||
|
||||
msg := strings.Trim(errCause.Error(), "{} ")
|
||||
parts := strings.Split(msg, ":")
|
||||
errCause = errkit.New("%s", parts[len(parts)-1])
|
||||
errKind := errkit.GetErrorKind(err, nucleierr.ErrTemplateLogic).String()
|
||||
errStr = errCause.Error()
|
||||
errStr = strings.TrimSpace(strings.Replace(errStr, "errKind="+errKind, "", -1))
|
||||
func getErrorCause(err error) string {
|
||||
if err == nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
return errStr
|
||||
errx := errkit.FromError(err)
|
||||
var cause error
|
||||
for _, e := range errx.Errors() {
|
||||
if e != nil && strings.Contains(e.Error(), "context deadline exceeded") {
|
||||
continue
|
||||
}
|
||||
cause = e
|
||||
break
|
||||
}
|
||||
if cause == nil {
|
||||
cause = errkit.Append(errkit.New("could not get error cause"), errx)
|
||||
}
|
||||
// parseScanError prettifies the error message and removes everything except the cause
|
||||
return parseScanError(cause.Error())
|
||||
}
|
||||
|
||||
// ExecuteWithResults executes the protocol requests and returns results instead of writing them.
|
||||
|
||||
@@ -85,7 +85,7 @@ func (g *Generic) ExecuteWithResults(ctx *scan.ScanContext) error {
|
||||
if err != nil {
|
||||
ctx.LogError(err)
|
||||
if g.options.HostErrorsCache != nil {
|
||||
g.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
g.options.HostErrorsCache.MarkFailed(g.options.ProtocolType.String(), ctx.Input, err)
|
||||
}
|
||||
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, ctx.Input.MetaInput.PrettyPrint(), err)
|
||||
}
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
package tmplexec
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"regexp"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/flow"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/generic"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/tmplexec/multiproto"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -30,3 +35,37 @@ type TemplateEngine interface {
|
||||
// Name returns name of template engine
|
||||
Name() string
|
||||
}
|
||||
|
||||
var (
|
||||
// A temporary fix to remove errKind from error message
|
||||
// this is because errkit is not used everywhere yet
|
||||
reNoKind = regexp.MustCompile(`([\[][^][]+[\]]|errKind=[^ ]+) `)
|
||||
)
|
||||
|
||||
// parseScanError parses given scan error and only returning the cause
|
||||
// instead of inefficient one
|
||||
func parseScanError(msg string) string {
|
||||
if msg == "" {
|
||||
return ""
|
||||
}
|
||||
if strings.HasPrefix(msg, "ReadStatusLine:") {
|
||||
// last index is actual error (from rawhttp)
|
||||
parts := strings.Split(msg, ":")
|
||||
msg = strings.TrimSpace(parts[len(parts)-1])
|
||||
}
|
||||
if strings.Contains(msg, "read ") {
|
||||
// same here
|
||||
parts := strings.Split(msg, ":")
|
||||
msg = strings.TrimSpace(parts[len(parts)-1])
|
||||
}
|
||||
e := errkit.FromError(errors.New(msg))
|
||||
for _, err := range e.Errors() {
|
||||
if err != nil && strings.Contains(err.Error(), "context deadline exceeded") {
|
||||
continue
|
||||
}
|
||||
msg = reNoKind.ReplaceAllString(err.Error(), "")
|
||||
return msg
|
||||
}
|
||||
wrapped := errkit.Append(errkit.New("failed to get error cause"), e).Error()
|
||||
return reNoKind.ReplaceAllString(wrapped, "")
|
||||
}
|
||||
@@ -23,6 +23,9 @@ var (
|
||||
ErrNoMoreRequests = io.EOF
|
||||
)
|
||||
|
||||
// LoadHelperFileFunction can be used to load a helper file.
|
||||
type LoadHelperFileFunction func(helperFile, templatePath string, catalog catalog.Catalog) (io.ReadCloser, error)
|
||||
|
||||
// Options contains the configuration options for nuclei scanner.
|
||||
type Options struct {
|
||||
// Tags contains a list of tags to execute templates for. Multiple paths
|
||||
@@ -203,6 +206,8 @@ type Options struct {
|
||||
VerboseVerbose bool
|
||||
// ShowVarDump displays variable dump
|
||||
ShowVarDump bool
|
||||
// VarDumpLimit limits the number of characters displayed in var dump
|
||||
VarDumpLimit int
|
||||
// No-Color disables the colored output.
|
||||
NoColor bool
|
||||
// UpdateTemplates updates the templates installed at startup (also used by cloud to update datasources)
|
||||
@@ -384,6 +389,8 @@ type Options struct {
|
||||
ScanID string
|
||||
// ScanName is the name of the scan to be uploaded
|
||||
ScanName string
|
||||
// ScanUploadFile is the jsonl file to upload scan results to cloud
|
||||
ScanUploadFile string
|
||||
// TeamID is the team ID to use for cloud upload
|
||||
TeamID string
|
||||
// JsConcurrency is the number of concurrent js routines to run
|
||||
@@ -406,6 +413,9 @@ type Options struct {
|
||||
HttpApiEndpoint string
|
||||
// ListTemplateProfiles lists all available template profiles
|
||||
ListTemplateProfiles bool
|
||||
// LoadHelperFileFunction is a function that will be used to execute LoadHelperFile.
|
||||
// If none is provided, then the default implementation will be used.
|
||||
LoadHelperFileFunction LoadHelperFileFunction
|
||||
// timeouts contains various types of timeouts used in nuclei
|
||||
// these timeouts are derived from dial-timeout (-timeout) with known multipliers
|
||||
// This is internally managed and does not need to be set by user by explicitly setting
|
||||
@@ -538,10 +548,21 @@ func (options *Options) ParseHeadlessOptionalArguments() map[string]string {
|
||||
return optionalArguments
|
||||
}
|
||||
|
||||
// LoadHelperFile loads a helper file needed for the template
|
||||
// LoadHelperFile loads a helper file needed for the template.
|
||||
//
|
||||
// If LoadHelperFileFunction is set, then that function will be used.
|
||||
// Otherwise, the default implementation will be used, which respects the sandbox rules and only loads files from allowed directories.
|
||||
func (options *Options) LoadHelperFile(helperFile, templatePath string, catalog catalog.Catalog) (io.ReadCloser, error) {
|
||||
if options.LoadHelperFileFunction != nil {
|
||||
return options.LoadHelperFileFunction(helperFile, templatePath, catalog)
|
||||
}
|
||||
return options.defaultLoadHelperFile(helperFile, templatePath, catalog)
|
||||
}
|
||||
|
||||
// defaultLoadHelperFile loads a helper file needed for the template
|
||||
// this respects the sandbox rules and only loads files from
|
||||
// allowed directories
|
||||
func (options *Options) LoadHelperFile(helperFile, templatePath string, catalog catalog.Catalog) (io.ReadCloser, error) {
|
||||
func (options *Options) defaultLoadHelperFile(helperFile, templatePath string, catalog catalog.Catalog) (io.ReadCloser, error) {
|
||||
if !options.AllowLocalFileAccess {
|
||||
// if global file access is disabled try loading with restrictions
|
||||
absPath, err := options.GetValidAbsPath(helperFile, templatePath)
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/keys"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -12,16 +13,13 @@ const (
|
||||
)
|
||||
|
||||
// TemplatePathURL returns the Path and URL for the provided template
|
||||
func TemplatePathURL(fullPath, templateId string) (string, string) {
|
||||
var templateDirectory string
|
||||
func TemplatePathURL(fullPath, templateId, templateVerifier string) (path string, url string) {
|
||||
configData := config.DefaultConfig
|
||||
if configData.TemplatesDirectory != "" && strings.HasPrefix(fullPath, configData.TemplatesDirectory) {
|
||||
templateDirectory = configData.TemplatesDirectory
|
||||
} else {
|
||||
return "", ""
|
||||
path = strings.TrimPrefix(strings.TrimPrefix(fullPath, configData.TemplatesDirectory), "/")
|
||||
}
|
||||
|
||||
finalPath := strings.TrimPrefix(strings.TrimPrefix(fullPath, templateDirectory), "/")
|
||||
templateURL := TemplatesRepoURL + templateId
|
||||
return finalPath, templateURL
|
||||
if templateVerifier == keys.PDVerifier {
|
||||
url = TemplatesRepoURL + templateId
|
||||
}
|
||||
return
|
||||
}
|
||||
|
After Width: | Height: | Size: 799 KiB |
|
After Width: | Height: | Size: 799 KiB |
|
After Width: | Height: | Size: 44 KiB |
|
After Width: | Height: | Size: 2.1 MiB |
|
After Width: | Height: | Size: 1.1 MiB |
|
After Width: | Height: | Size: 1.5 MiB |
|
After Width: | Height: | Size: 46 KiB |
|
After Width: | Height: | Size: 2.7 MiB |
|
After Width: | Height: | Size: 1.0 MiB |