mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2026-11-08 02:08:33 -10:00
Compare commits
121
Commits
v3.2.6
...
bugfix-proxy
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
13cb549351 | ||
|
|
381ebba6a2 | ||
|
|
87b9fbd675 | ||
|
|
91d3b668fd | ||
|
|
9a441c23cc | ||
|
|
61029d05c3 | ||
|
|
94b2cb3473 | ||
|
|
9f35b86f56 | ||
|
|
e61ca0c42d | ||
|
|
1c51a6bef6 | ||
|
|
cc03b7591a | ||
|
|
c16050a5fe | ||
|
|
f42ff92e3e | ||
|
|
6d87aeeeaa | ||
|
|
20709834c2 | ||
|
|
1359c2e7b9 | ||
|
|
a37761d841 | ||
|
|
9ebdd733ea | ||
|
|
d7d26ca9b5 | ||
|
|
c175474df5 | ||
|
|
930f9ab02d | ||
|
|
f8842b1f04 | ||
|
|
b274fe5ed1 | ||
|
|
0eddafe2a9 | ||
|
|
4720d8c8ec | ||
|
|
9f1faa0a29 | ||
|
|
8b3379aa4c | ||
|
|
baf7e36658 | ||
|
|
51b1d7cfe4 | ||
|
|
1e85e70d99 | ||
|
|
52975373ff | ||
|
|
cfd7b6f455 | ||
|
|
27eedc0ac6 | ||
|
|
e29ceeecc4 | ||
|
|
5fd3f0f65d | ||
|
|
8978ebafa2 | ||
|
|
61f391ee8a | ||
|
|
ac0107c242 | ||
|
|
bc16ae85ae | ||
|
|
9f1414e3e8 | ||
|
|
26c84e3747 | ||
|
|
e13c7cc967 | ||
|
|
9415e13f48 | ||
|
|
0b6ad3597f | ||
|
|
1c355c54ad | ||
|
|
9f3f7fce06 | ||
|
|
cd40a6aeda | ||
|
|
df57f0a6e3 | ||
|
|
61da7f95a0 | ||
|
|
aced7ae42a | ||
|
|
eb4f614528 | ||
|
|
4a6f55c4a2 | ||
|
|
86dabe1a98 | ||
|
|
56e9b7fcbc | ||
|
|
1e538c726c | ||
|
|
f0b34f94cd | ||
|
|
fd2311d034 | ||
|
|
04a630266e | ||
|
|
5428d059f5 | ||
|
|
b5e40cf777 | ||
|
|
bcd10b41e5 | ||
|
|
464aa8de82 | ||
|
|
a0bedbcb16 | ||
|
|
425b7d0cb5 | ||
|
|
8720e4f863 | ||
|
|
f8a4cf50c6 | ||
|
|
8011012c42 | ||
|
|
4ae0b39f53 | ||
|
|
a2a85fb002 | ||
|
|
b8ea0f9800 | ||
|
|
2a40eda45e | ||
|
|
7f0f8ffed7 | ||
|
|
75d0af1aea | ||
|
|
527f18a6e6 | ||
|
|
281b24ed50 | ||
|
|
3307517a6c | ||
|
|
46e2a54bfe | ||
|
|
718aca6e0e | ||
|
|
f43588dcd2 | ||
|
|
b4f0bd6a2f | ||
|
|
8e0bb8fe34 | ||
|
|
23bd0336fb | ||
|
|
4fc16e36e1 | ||
|
|
46e4810efb | ||
|
|
f6332583b7 | ||
|
|
7a4969d2a2 | ||
|
|
f12343bf35 | ||
|
|
f266542dd4 | ||
|
|
8cffeecd4f | ||
|
|
9dde504a2d | ||
|
|
56dcda2116 | ||
|
|
e92746ddcd | ||
|
|
48ed1dde38 | ||
|
|
9cc335a34c | ||
|
|
5d30eb5d96 | ||
|
|
bfddce4fbb | ||
|
|
4170e1cbb8 | ||
|
|
f1b739f2a5 | ||
|
|
06b0219839 | ||
|
|
3d5605530b | ||
|
|
aaf5d262b2 | ||
|
|
b820fb9464 | ||
|
|
47ca8fe842 | ||
|
|
9adfc531c7 | ||
|
|
4874729809 | ||
|
|
16ce116fed | ||
|
|
bdeb66b2ad | ||
|
|
59e4372539 | ||
|
|
c2c0f0672f | ||
|
|
c9f3689112 | ||
|
|
2800cb6744 | ||
|
|
af9cbec832 | ||
|
|
43cacf12e3 | ||
|
|
2d68ac2063 | ||
|
|
d6424ea5d0 | ||
|
|
0f983d8204 | ||
|
|
6067b78bcf | ||
|
|
fed10a11e8 | ||
|
|
eb73228a02 | ||
|
|
098392b604 | ||
|
|
907f5a0538 |
No files matched your search
@@ -21,7 +21,7 @@ jobs:
|
||||
|
||||
- uses: goreleaser/goreleaser-action@v4
|
||||
with:
|
||||
args: "release --rm-dist"
|
||||
args: "release --clean"
|
||||
version: latest
|
||||
env:
|
||||
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
||||
+1
-1
@@ -39,5 +39,5 @@ integration_tests/fuzzplayground
|
||||
/nuclei-stats
|
||||
/nuclei-stats-*
|
||||
/scan-charts
|
||||
|
||||
/**/debug-*
|
||||
|
||||
@@ -30,7 +30,10 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_JP.md">日本語</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
---
|
||||
@@ -179,6 +182,8 @@ OUTPUT:
|
||||
|
||||
CONFIGURATIONS:
|
||||
-config string path to the nuclei configuration file
|
||||
-tp, -profile string template profile config file to run
|
||||
-tpl, -profile-list list community template profiles
|
||||
-fr, -follow-redirects enable following redirects for http templates
|
||||
-fhr, -follow-host-redirects follow redirects on the same host
|
||||
-mr, -max-redirects int max number of redirects to follow for http templates (default 10)
|
||||
@@ -205,10 +210,12 @@ CONFIGURATIONS:
|
||||
-i, -interface string network interface to use for network scan
|
||||
-at, -attack-type string type of payload combinations to perform (batteringram,pitchfork,clusterbomb)
|
||||
-sip, -source-ip string source ip address to use for network scan
|
||||
-rsr, -response-size-read int max response size to read in bytes (default 10485760)
|
||||
-rsr, -response-size-read int max response size to read in bytes
|
||||
-rss, -response-size-save int max response size to read in bytes (default 1048576)
|
||||
-rrt, -response-read-timeout value response read timeout in seconds (default 5s)
|
||||
-reset reset removes all nuclei configuration and data files (including nuclei-templates)
|
||||
-tlsi, -tls-impersonate enable experimental client hello (ja3) tls randomization
|
||||
-hae, -http-api-endpoint string experimental http api endpoint
|
||||
|
||||
INTERACTSH:
|
||||
-iserver, -interactsh-server string interactsh server url for self-hosted instance (default: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
@@ -220,10 +227,13 @@ INTERACTSH:
|
||||
-ni, -no-interactsh disable interactsh server for OAST testing, exclude OAST based templates
|
||||
|
||||
FUZZING:
|
||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||
-fuzz enable loading fuzzing templates (Deprecated: use -dast instead)
|
||||
-dast only run DAST templates
|
||||
-ft, -fuzzing-type string overrides fuzzing type set in template (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string overrides fuzzing mode set in template (multiple, single)
|
||||
-fuzz enable loading fuzzing templates (Deprecated: use -dast instead)
|
||||
-dast enable / run dast (fuzz) nuclei templates
|
||||
-dfp, -display-fuzz-points display fuzz points in the output for debugging
|
||||
-fuzz-param-frequency int frequency of uninteresting parameters for fuzzing before skipping (default 10)
|
||||
-fa, -fuzz-aggression string fuzzing aggression level controls payload count for fuzz (low, medium, high) (default "low")
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover enable uncover engine
|
||||
@@ -235,13 +245,15 @@ UNCOVER:
|
||||
|
||||
RATE-LIMIT:
|
||||
-rl, -rate-limit int maximum number of requests to send per second (default 150)
|
||||
-rlm, -rate-limit-minute int maximum number of requests to send per minute
|
||||
-rld, -rate-limit-duration value maximum number of requests to send per second (default 1s)
|
||||
-rlm, -rate-limit-minute int maximum number of requests to send per minute (DEPRECATED)
|
||||
-bs, -bulk-size int maximum number of hosts to be analyzed in parallel per template (default 25)
|
||||
-c, -concurrency int maximum number of templates to be executed in parallel (default 25)
|
||||
-hbs, -headless-bulk-size int maximum number of headless hosts to be analyzed in parallel per template (default 10)
|
||||
-headc, -headless-concurrency int maximum number of headless templates to be executed in parallel (default 10)
|
||||
-jsc, -js-concurrency int maximum number of javascript runtimes to be executed in parallel (default 120)
|
||||
-pc, -payload-concurrency int max payload concurrency for each template (default 25)
|
||||
-prc, -probe-concurrency int http probe concurrency with httpx (default 50)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int time to wait in seconds before timeout (default 10)
|
||||
|
||||
+2
-1
@@ -32,7 +32,8 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+369
@@ -0,0 +1,369 @@
|
||||
<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">Escáner de vulnerabilidades rápido y personalizable basado en un sencillo DSL basado en YAML.</h4>
|
||||
|
||||
|
||||
<p align="center">
|
||||
<img src="https://img.shields.io/github/go-mod/go-version/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases"><img src="https://img.shields.io/github/downloads/projectdiscovery/nuclei/total">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/graphs/contributors"><img src="https://img.shields.io/github/contributors-anon/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/releases/"><img src="https://img.shields.io/github/release/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/issues"><img src="https://img.shields.io/github/issues-raw/projectdiscovery/nuclei">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/discussions"><img src="https://img.shields.io/github/discussions/projectdiscovery/nuclei">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="https://img.shields.io/discord/695645237418131507.svg?logo=discord"></a>
|
||||
<a href="https://twitter.com/pdnuclei"><img src="https://img.shields.io/twitter/follow/pdnuclei.svg?logo=twitter"></a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="#how-it-works">Cómo funciona</a> •
|
||||
<a href="#install-nuclei">Instalación</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">Documentación</a> •
|
||||
<a href="#credits">Créditos</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">Preguntas Frecuentes</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Únete a Discord</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Nuclei se utiliza para enviar peticiones a múltiples objetivos basándose en una plantilla, lo que resulta en cero falsos positivos y proporciona un escaneo rápido en un gran número de hosts. Nuclei ofrece escaneos para una variedad de protocolos, incluyendo TCP, DNS, HTTP, SSL, File, Whois, Websocket, Headless, Code, etc. Con plantillas potentes y flexibles, Nuclei puede utilizarse para modelar todo tipo de comprobaciones de seguridad.
|
||||
|
||||
Tenemos un [repositorio dedicado](https://github.com/projectdiscovery/nuclei-templates) que alberga varios tipos de plantillas de vulnerabilidades, contribuidas por **más de 300** investigadores y ingenieros de seguridad.
|
||||
|
||||
## Cómo funciona
|
||||
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
|
||||
|
||||
| :exclamation: **Descargo de responsabilidad** |
|
||||
|---------------------------------|
|
||||
| **Este proyecto está en desarrollo activo**. Es de esperar que se produzcan cambios importantes con las nuevas versiones. Consulte el registro de cambios de la versión antes de actualizar. |
|
||||
| Este proyecto fue principalmente desarrollado para ser utilizado como una herramienta CLI independiente. **Ejecutar nuclei como un servicio puede suponer riesgos de seguridad.** Se recomienda utilizarlo con precaución y tomar medidas de seguridad adicionales. |
|
||||
|
||||
# Instalación de Nuclei
|
||||
|
||||
Nuclei requiere **go1.21** para instalarse correctamente. Ejecute el siguiente comando para instalar la última versión -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**Más métodos de instalación [pueden encontrarse aquí](https://docs.projectdiscovery.io/tools/nuclei/install).**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
### Plantillas de Nuclei
|
||||
|
||||
Nuclei cuenta con soporte incorporado para la descarga/actualización automática de plantillas desde la versión [v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2) en adelante. El proyecto [**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates) proporciona una lista de plantillas listas para usar, aportadas por la comunidad, y que se actualizan constantemente.
|
||||
|
||||
También puedes utilizar la bandera `update-templates` para actualizar las plantillas de Nuclei en cualquier momento; puedes escribir tus propias pruebas para tu flujo de trabajo y necesidades individuales siguiendo la [guía de plantillas](https://docs.projectdiscovery.io/templates/) de Nuclei.
|
||||
|
||||
La sintaxis de referencia YAML DSL está disponible [aquí](SYNTAX-REFERENCE.md).
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### Uso
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
Esto mostrará ayuda sobre la herramienta. Aquí están todas las opciones que soporta.
|
||||
|
||||
|
||||
```console
|
||||
Nuclei es un escáner de vulnerabilidades rápido y basado en plantillas
|
||||
que se centra en su amplia configurabilidad, extensibilidad y facilidad de uso.
|
||||
|
||||
Usage:
|
||||
./nuclei [flags]
|
||||
|
||||
Flags:
|
||||
TARGET:
|
||||
-u, -target string[] URLs/hosts a escanear
|
||||
-l, -list string ruta al archivo que contiene la lista de URLs/hosts a escanear (uno por línea)
|
||||
-eh, -exclude-hosts string[] hosts a excluir para escanear de la lista de entrada (ip, cidr, hostname)
|
||||
-resume string reanudar el escaneo usando resume.cfg (la clusterización quedará inhabilitada)
|
||||
-sa, -scan-all-ips escanear todas las IP asociadas al registro dns
|
||||
-iv, -ip-version string[] versión IP a escanear del nombre de host (4,6) - (por defecto 4)
|
||||
|
||||
TARGET-FORMAT:
|
||||
-im, -input-mode string modo del archivo de entrada (list, burp, jsonl, yaml, openapi, swagger) (por defecto "list")
|
||||
-ro, -required-only utilizar solo campos requeridos en el formato de entrada al generar peticiones
|
||||
-sfv, -skip-format-validation saltar la validación de formato (como variables faltantes) al procesar el archivo de entrada
|
||||
|
||||
TEMPLATES:
|
||||
-nt, -new-templates ejecutar sólo las nuevas plantillas añadidas en la última versión de nuclei-templates
|
||||
-ntv, -new-templates-version string[] ejecutar las nuevas plantillas añadidas en la versión especificada
|
||||
-as, -automatic-scan escaneo web automático utilizando la detección de tecnología de wappalyzer para mapeo de etiquetas
|
||||
-t, -templates string[] lista de plantillas o directorio de plantillas a ejecutar (separadas por comas, file)
|
||||
-turl, -template-url string[] url de plantilla o lista que contiene urls de plantillas a ejecutar (separadas por comas, file)
|
||||
-w, -workflows string[] lista de flujos de trabajo o directorio de flujos de trabajo a ejecutar (separadas por comas, file)
|
||||
-wurl, -workflow-url string[] url de flujo de trabajo o lista que contiene urls de flujo de trabajo para ejecutar (separadas por comas, file)
|
||||
-validate valida las plantillas pasadas a nuclei
|
||||
-nss, -no-strict-syntax deshabilita la comprobación de sintaxis estricta en las plantillas
|
||||
-td, -template-display muestra el contenido de las plantillas
|
||||
-tl lista todas las plantillas disponibles
|
||||
-tgl lista todas las etiquetas disponibles
|
||||
-sign firma las plantillas con la clave privada definida en la variable de entorno NUCLEI_SIGNATURE_PRIVATE_KEY
|
||||
-code habilita la carga de plantillas basadas en protocolos de código
|
||||
-dut, -disable-unsigned-templates deshabilita la ejecución de plantillas no firmadas o plantillas con firma no coincidente
|
||||
|
||||
FILTERING:
|
||||
-a, -author string[] plantillas a ejecutar basadas en autores (separadas por comas, file)
|
||||
-tags string[] plantillas a ejecutar basadas en etiquetas (separadas por comas, file)
|
||||
-etags, -exclude-tags string[] plantillas a excluir basadas en etiquetas (separadas por comas, file)
|
||||
-itags, -include-tags string[] etiquetas a ejecutar incluso si están excluidas ya sea por defecto o por configuración
|
||||
-id, -template-id string[] plantillas a ejecutar basadas en IDs de plantilla (comma-separated, file, allow-wildcard)
|
||||
-eid, -exclude-id string[] plantillas a excluir basadas en IDs de plantilla (separadas por comas, file)
|
||||
-it, -include-templates string[] ruta al archivo de plantilla o directorio a ejecutar incluso si están excluidas ya sea por defecto o por configuración
|
||||
-et, -exclude-templates string[] ruta al archivo de plantilla o directorio a excluir (separadas por comas, file)
|
||||
-em, -exclude-matchers string[] matchers de plantilla a excluir en el resultado
|
||||
-s, -severity value[] plantillas a ejecutar basadas en criticidad. Valores posibles: info, bajo, medio, alto, crítico, desconocido
|
||||
-es, -exclude-severity value[] plantillas a excluir basadas en criticidad. Valores posibles: info, bajo, medio, alto, crítico, desconocido
|
||||
-pt, -type value[] plantillas a ejecutar basadas en tipo de protocolo. Valores posibles: dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-ept, -exclude-type value[] plantillas a excluir basadas en tipo de protocolo. Valores posibles: dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-tc, -template-condition string[] plantillas a ejecutar basadas en condición de expresión
|
||||
|
||||
OUTPUT:
|
||||
-o, -output string archivo de salida donde guardar las incidencias/vulnerabilidades detectadas
|
||||
-sresp, -store-resp almacenar todas las peticiones/respuestas enviadas por nuclei en el directorio de salida
|
||||
-srd, -store-resp-dir string almacenar todas las peticiones/respuestas enviadas por nuclei en un directorio personalizado (por defecto "output")
|
||||
-silent mostrar resultados únicamente
|
||||
-nc, -no-color deshabilitar la coloración del contenido de salida (códigos de escape ANSI)
|
||||
-j, -jsonl escribir la salida en formato JSONL(ines)
|
||||
-irr, -include-rr -omit-raw incluir pares peticiones/respuesta en las salidas JSON, JSONL y Markdown (sólo para hallazgos) [OBSOLETO usar -omit-raw] (por defecto true)
|
||||
-or, -omit-raw omitir los pares peticiones/respuesta en las salidas JSON, JSONL y Markdown (sólo para hallazgos)
|
||||
-ot, -omit-template omitir plantilla codificada en la salida JSON, JSONL
|
||||
-nm, -no-meta deshabilitar la impresión de metadatos de resultados en la salida cli
|
||||
-ts, -timestamp habilitar la impresión de la marca de tiempo en la salida cli
|
||||
-rdb, -report-db string base de datos de informes de nuclei (utilizarla siempre para persistir los datos de los informes)
|
||||
-ms, -matcher-status mostrar el estado de fallo de coincidencia
|
||||
-me, -markdown-export string directorio para exportar resultados en formato markdown
|
||||
-se, -sarif-export string archivo para exportar resultados en formato SARIF
|
||||
-je, -json-export string archivo para exportar resultados en formato JSON
|
||||
-jle, -jsonl-export string archivo para exportar resultados en formato JSONL(ines)
|
||||
|
||||
CONFIGURATIONS:
|
||||
-config string ruta al archivo de configuración de nuclei
|
||||
-fr, -follow-redirects habilitar el seguimiento de redirecciones para plantillas http
|
||||
-fhr, -follow-host-redirects seguir redirecciones en el mismo host
|
||||
-mr, -max-redirects int número máximo de redirecciones a seguir para plantillas http (por defecto 10)
|
||||
-dr, -disable-redirects deshabilitar redirecciones para plantillas http
|
||||
-rc, -report-config string archivo de configuración del módulo de informes de nuclei
|
||||
-H, -header string[] encabezado/cookie personalizado a incluir en todas las peticiones http en formato header:value (cli, file)
|
||||
-V, -var value variables personalizadas en formato key=value
|
||||
-r, -resolvers string archivo que contiene lista de resolutores para nuclei
|
||||
-sr, -system-resolvers utilizar resolución de DNS del sistema como fallback de error
|
||||
-dc, -disable-clustering deshabilitar la clusterización de peticiones
|
||||
-passive habilitar el modo de procesamiento pasivo de respuestas HTTP
|
||||
-fh2, -force-http2 forzar la conexión http2 en las peticiones
|
||||
-ev, -env-vars habilitar el uso de variables de entorno en la plantilla
|
||||
-cc, -client-cert string archivo de certificado de cliente (codificado en PEM) utilizado para autenticarse contra los hosts escaneados
|
||||
-ck, -client-key string archivo de clave de cliente (codificado en PEM) utilizado para autenticarse contra los hosts escaneados
|
||||
-ca, -client-ca string archivo de autoridad de certificación de cliente (codificado en PEM) utilizado para autenticarse contra los hosts escaneados
|
||||
-sml, -show-match-line mostrar líneas de coincidencia para plantillas de archivo, funciona solo con extractores
|
||||
-ztls utilizar la biblioteca ztls con autofallback a estándar para tls13 [Obsoleto] autofallback a ztls está habilitado por defecto
|
||||
-sni string nombre de host tls sni a usar (por defecto: nombre de dominio de entrada)
|
||||
-dt, -dialer-timeout value tiempo de espera para peticiones de red
|
||||
-dka, -dialer-keep-alive value duración de keep-alive para peticiones de red
|
||||
-lfa, -allow-local-file-access permite el acceso a archivos (carga útil) en cualquier lugar del sistema
|
||||
-lna, -restrict-local-network-access bloquea conexiones a la red local / privada
|
||||
-i, -interface string interfaz de red a usar para el escaneo de red
|
||||
-at, -attack-type string tipo de combinaciones de carga útil a realizar (batteringram, pitchfork, clusterbomb)
|
||||
-sip, -source-ip string dirección ip de origen a usar para el escaneo de red
|
||||
-rsr, -response-size-read int tamaño máximo de respuesta a leer en bytes (por defecto 10485760)
|
||||
-rss, -response-size-save int tamaño máximo de respuesta a guardar en bytes (por defecto 1048576)
|
||||
-reset reset elimina todos los archivos de configuración y datos de nuclei (incluidas las nuclei-templates)
|
||||
-tlsi, -tls-impersonate habilitar client hello (ja3) tls randomization experimental
|
||||
|
||||
INTERACTSH:
|
||||
-iserver, -interactsh-server string url del servidor interactsh para instancia autoalojada (por defecto: oast.pro,oast.live,oast.site,oast.online,oast.fun,oast.me)
|
||||
-itoken, -interactsh-token string token de autenticación del servidor interactsh autoalojado
|
||||
-interactions-cache-size int número de peticiones a mantener en la caché de interacciones (por defecto 5000)
|
||||
-interactions-eviction int número de segundos a esperar antes de eliminar las solicitudes de la caché (por defecto 60)
|
||||
-interactions-poll-duration int número de segundos a esperar antes de cada solicitud de polling de interacciones (por defecto 5)
|
||||
-interactions-cooldown-period int tiempo adicional para el polling de interacciones antes de salir (por defecto 5)
|
||||
-ni, -no-interactsh desactivar el servidor interactsh para pruebas OAST, excluir plantillas basadas en OAST
|
||||
|
||||
FUZZING:
|
||||
-ft, -fuzzing-type string sobrescribe el tipo de fuzzing establecido en la plantilla (replace, prefix, postfix, infix)
|
||||
-fm, -fuzzing-mode string sobrescribe el modo de fuzzing establecido en la plantilla (multiple, single)
|
||||
-fuzz habilita la carga de plantillas de fuzzing (Obsoleto: usar -dast en su lugar)
|
||||
-dast solo ejecuta plantillas DAST
|
||||
|
||||
UNCOVER:
|
||||
-uc, -uncover habilita el motor uncover
|
||||
-uq, -uncover-query string[] consulta de búsqueda uncover
|
||||
-ue, -uncover-engine string[] motor de búsqueda uncover (shodan,censys,fofa,shodan-idb,quake,hunter,zoomeye,netlas,criminalip,publicwww,hunterhow) (por defecto shodan)
|
||||
-uf, -uncover-field string campos uncover a devolver (ip,port,host) (por defecto "ip:port")
|
||||
-ul, -uncover-limit int resultados uncover a devolver (por defecto 100)
|
||||
-ur, -uncover-ratelimit int sobrescribe el límite de velocidad de los motores con el límite de velocidad del motor uncover (por defecto 60 req/min) (por defecto 60)
|
||||
|
||||
RATE-LIMIT:
|
||||
-rl, -rate-limit int número máximo de peticiones a enviar por segundo (por defecto 150)
|
||||
-rlm, -rate-limit-minute int número máximo de peticiones a enviar por minuto
|
||||
-bs, -bulk-size int número máximo de hosts a ser analizados en paralelo por plantilla (por defecto 25)
|
||||
-c, -concurrency int número máximo de plantillas a ejecutar en paralelo (por defecto 25)
|
||||
-hbs, -headless-bulk-size int número máximo de hosts headless a ser analizados en paralelo por plantilla (por defecto 10)
|
||||
-headc, -headless-concurrency int número máximo de plantillas headless a ejecutar en paralelo (por defecto 10)
|
||||
-jsc, -js-concurrency int número máximo de entornos de ejecución de JavaScript a ejecutar en paralelo (por defecto 120)
|
||||
-pc, -payload-concurrency int concurrencia máxima de carga útil para cada plantilla (por defecto 25)
|
||||
|
||||
OPTIMIZATIONS:
|
||||
-timeout int tiempo de espera en segundos (por defecto 10)
|
||||
-retries int número de veces que se reintenta una petición fallida (por defecto 1)
|
||||
-ldp, -leave-default-ports dejar puertos HTTP/HTTPS predeterminados (por ejemplo, host:80,host:443)
|
||||
-mhe, -max-host-error int errores máximos para un host antes de omitirlo del escaneo (por defecto 30)
|
||||
-te, -track-error string[] agrega el error dado a la lista de seguimiento de errores máximos por host (standard, file)
|
||||
-nmhe, -no-mhe deshabilita la omisión del host del escaneo basado en errores
|
||||
-project utiliza una carpeta de proyecto para evitar enviar la misma petición varias veces
|
||||
-project-path string establece una ruta de proyecto específica (por defecto "/tmp")
|
||||
-spm, -stop-at-first-match detiene el procesamiento de las peticiones HTTP después de la primera coincidencia (puede romper la lógica de la plantilla/flujo de trabajo)
|
||||
-stream modo transmisión - comienza a trabajar sin ordenar la entrada
|
||||
-ss, -scan-strategy value estrategia a utilizar mientras se escanea (auto/host-spray/template-spray) (por defecto auto)
|
||||
-irt, -input-read-timeout value tiempo de espera en la lectura de entrada (por defecto 3m0s)
|
||||
-nh, -no-httpx deshabilita análisis httpx para entradas que no son URL
|
||||
-no-stdin deshabilita el procesamiento de la entrada estándar
|
||||
|
||||
HEADLESS:
|
||||
-headless habilita las plantillas que requieren soporte de navegadores sin interfaz gráfica (headless browser) (el usuario root en Linux deshabilitará el sandbox)
|
||||
-page-timeout int segundos para esperar cada página en modo sin interfaz (por defecto 20)
|
||||
-sb, -show-browser muestra el navegador en la pantalla al ejecutar plantillas con modo sin interfaz
|
||||
-ho, -headless-options string[] inicia Chrome en modo sin interfaz con opciones adicionales
|
||||
-sc, -system-chrome utiliza el navegador Chrome instalado localmente en lugar del instalado por nuclei
|
||||
-lha, -list-headless-action lista de acciones sin interfaz disponibles
|
||||
|
||||
DEBUG:
|
||||
-debug muestra todas las peticiones y respuestas
|
||||
-dreq, -debug-req muestra todas las peticiones enviadas
|
||||
-dresp, -debug-resp muestra todas las respuestas recibidas
|
||||
-p, -proxy string[] lista de proxies http/socks5 a utilizar (separados por comas o archivo de entrada)
|
||||
-pi, -proxy-internal proxy para todas las peticiones internas
|
||||
-ldf, -list-dsl-function lista todas las firmas de función DSL admitidas
|
||||
-tlog, -trace-log string archivo a escribir el registro de traza de peticiones enviadas
|
||||
-elog, -error-log string archivo a escribir el registro de error de peticiones enviadas
|
||||
-version muestra la versión de nuclei
|
||||
-hm, -hang-monitor habilita la monitorización de bloqueos de nuclei
|
||||
-v, -verbose muestra salida detallada
|
||||
-profile-mem string archivo opcional de volcado de memoria de nuclei
|
||||
-vv muestra las plantillas cargadas para el escaneo
|
||||
-svd, -show-var-dump muestra el volcado de variables para depuración
|
||||
-ep, -enable-pprof habilita el servidor de depuración pprof
|
||||
-tv, -templates-version muestra la versión de las plantillas nuclei (nuclei-templates) instaladas
|
||||
-hc, -health-check ejecuta comprobación de diagnóstico
|
||||
|
||||
UPDATE:
|
||||
-up, -update actualiza el motor de nuclei a la última versión lanzada
|
||||
-ut, -update-templates actualiza nuclei-templates a la última versión lanzada
|
||||
-ud, -update-template-dir string directorio personalizado para instalar/actualizar nuclei-templates
|
||||
-duc, -disable-update-check deshabilita la comprobación automática de actualizaciones de nuclei/templates
|
||||
|
||||
STATISTICS:
|
||||
-stats muestra estadísticas sobre el escaneo en ejecución
|
||||
-sj, -stats-json muestra estadísticas en formato JSONL(ines)
|
||||
-si, -stats-interval int número de segundos a esperar entre mostrar una actualización de estadísticas (por defecto 5)
|
||||
-mp, -metrics-port int puerto para exponer métricas de nuclei (por defecto 9092)
|
||||
|
||||
CLOUD:
|
||||
-auth configura la clave de API del cloud de projectdiscovery (pdcp)
|
||||
-cup, -cloud-upload sube los resultados del escaneo al dashboard de pdcp
|
||||
-sid, -scan-id string sube los resultados del escaneo al ID de escaneo dado
|
||||
|
||||
AUTHENTICATION:
|
||||
-sf, -secret-file string[] ruta al archivo de configuración que contiene los secrets para el escaneo autenticado de nuclei
|
||||
-ps, -prefetch-secrets precarga los secrets del archivo de secrets
|
||||
|
||||
|
||||
EXAMPLES:
|
||||
Ejecutar nuclei en un solo host:
|
||||
$ nuclei -target example.com
|
||||
|
||||
Ejecutar nuclei con directorios de plantillas específicos:
|
||||
$ nuclei -target example.com -t http/cves/ -t ssl
|
||||
|
||||
Ejecutar nuclei contra una lista de hosts:
|
||||
$ nuclei -list hosts.txt
|
||||
|
||||
Ejecutar nuclei con una salida JSON:
|
||||
$ nuclei -target example.com -json-export output.json
|
||||
|
||||
Ejecutar nuclei con salidas Markdown ordenadas (con variables de entorno):
|
||||
$ MARKDOWN_EXPORT_SORT_MODE=template nuclei -target example.com -markdown-export nuclei_report/
|
||||
|
||||
Documentación adicional disponible en: https://docs.nuclei.sh/getting-started/running
|
||||
```
|
||||
|
||||
### Ejecutando Nuclei
|
||||
|
||||
Consulta https://docs.projectdiscovery.io/tools/nuclei/running para obtener detalles sobre cómo ejecutar Nuclei.
|
||||
|
||||
### Uso de Nuclei desde código Go
|
||||
|
||||
La guía completa sobre cómo usar Nuclei como biblioteca/SDK está disponible en [godoc](https://pkg.go.dev/github.com/projectdiscovery/nuclei/v3/lib#section-readme).
|
||||
|
||||
|
||||
### Recursos
|
||||
|
||||
Puedes acceder a la documentación principal de Nuclei en https://docs.projectdiscovery.io/tools/nuclei/, y obtener más información sobre Nuclei en la nube con [ProjectDiscovery Cloud Platform](https://cloud.projectdiscovery.io).
|
||||
|
||||
¡Consulta https://docs.projectdiscovery.io/tools/nuclei/resources para obtener más recursos y videos sobre Nuclei!
|
||||
|
||||
### Créditos
|
||||
|
||||
Gracias a todos los increíbles [contribuyentes de la comunidad que enviaron PRs](https://github.com/projectdiscovery/nuclei/graphs/contributors) y mantienen este proyecto actualizado. :heart:
|
||||
|
||||
Si tienes una idea o algún tipo de mejora, eres bienvenido a contribuir y participar en el Proyecto, siéntete libre de enviar tu 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>
|
||||
|
||||
|
||||
También echa un vistazo a los siguientes proyectos de código abierto similares que pueden adaptarse a tu flujo de trabajo:
|
||||
|
||||
[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)
|
||||
|
||||
### Licencia
|
||||
|
||||
Nuclei se distribuye bajo la [Licencia MIT](https://github.com/projectdiscovery/nuclei/blob/main/LICENSE.md)
|
||||
|
||||
<h1 align="left">
|
||||
<a href="https://discord.gg/projectdiscovery"><img src="static/Join-Discord.png" width="380" alt="Join Discord"></a> <a href="https://docs.projectdiscovery.io"><img src="static/check-nuclei-documentation.png" width="380" alt="Check Nuclei Documentation"></a>
|
||||
</h1>
|
||||
+2
-1
@@ -32,7 +32,8 @@
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">Korean</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">Indonesia</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">Spanish</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+172
@@ -0,0 +1,172 @@
|
||||
<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">シンプルなYAMLベースの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">動作原理</a> •
|
||||
<a href="#install-nuclei">インストール</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/">ドキュメント</a> •
|
||||
<a href="#credits">クレジット</a> •
|
||||
<a href="https://docs.projectdiscovery.io/tools/nuclei/faq">FAQ</a> •
|
||||
<a href="https://discord.gg/projectdiscovery">Discordに参加</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">英語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中国語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">韓国語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ID.md">インドネシア語</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">スペイン語</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
Nucleiは、テンプレートに基づいてターゲット間でリクエストを送信するために使用され、偽陽性がゼロであり、多数のホストで高速なスキャンを提供します。Nucleiは、TCP、DNS、HTTP、SSL、ファイル、Whois、Websocket、Headless、Codeなど、さまざまなプロトコルのスキャンを提供します。強力で柔軟なテンプレートを使用して、Nucleiはすべての種類のセキュリティチェックをモデル化するために使用できます。
|
||||
|
||||
**300人以上の** セキュリティ研究者およびエンジニアが提供するさまざまなタイプの脆弱性テンプレートを収容する[専用リポジトリ](https://github.com/projectdiscovery/nuclei-templates)を持っています。
|
||||
|
||||
## 動作原理
|
||||
|
||||
<h3 align="center">
|
||||
<img src="static/nuclei-flow.jpg" alt="nuclei-flow" width="700px"></a>
|
||||
</h3>
|
||||
|
||||
| :exclamation: **免責事項** |
|
||||
|---------------------------------|
|
||||
| **このプロジェクトは積極的に開発されています**。リリースによって重大な変更が発生することがあります。更新する前にリリースの変更ログを確認してください。 |
|
||||
| このプロジェクトは主にスタンドアロンのCLIツールとして使用されることを目的として構築されました。**Nucleiをサービスとして実行すると、セキュリティリスクが生じる可能性があります。**注意して使用し、追加のセキュリティ対策を講じることをお勧めします。 |
|
||||
|
||||
# Nucleiのインストール
|
||||
|
||||
Nucleiを正常にインストールするには、**go1.21**が必要です。以下のコマンドを実行して最新バージョンをインストールしてください -
|
||||
|
||||
```sh
|
||||
go install -v github.com/projectdiscovery/nuclei/v3/cmd/nuclei@latest
|
||||
```
|
||||
|
||||
<details>
|
||||
<summary>Brew</summary>
|
||||
|
||||
```sh
|
||||
brew install nuclei
|
||||
```
|
||||
|
||||
</details>
|
||||
<details>
|
||||
<summary>Docker</summary>
|
||||
|
||||
```sh
|
||||
docker pull projectdiscovery/nuclei:latest
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
**より多くのインストール方法は[こちら](https://docs.projectdiscovery.io/tools/nuclei/install)で見つけることができます。**
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
|
||||
### Nucleiテンプレート
|
||||
|
||||
Nucleiは、バージョン[v2.5.2](https://github.com/projectdiscovery/nuclei/releases/tag/v2.5.2)以降、デフォルトでテンプレートの自動ダウンロード/更新をサポートしています。[**Nuclei-Templates**](https://github.com/projectdiscovery/nuclei-templates)プロジェクトは、常に更新されるコミュニティ提供の即時使用可能なテンプレートのリストを提供します。
|
||||
|
||||
`update-templates`フラグを使用して、いつでもNucleiテンプレートを更新することができます。Nucleiの[テンプレートガイド](https://docs.projectdiscovery.io/templates/)に従って、個々のワークフローとニーズに合わせた独自のチェックを作成することができます。
|
||||
|
||||
YAML DSLの構文リファレンスは[こちら](SYNTAX-REFERENCE.md)で確認できます。
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
### 使用方法
|
||||
|
||||
```sh
|
||||
nuclei -h
|
||||
```
|
||||
|
||||
これにより、ツールのヘルプが表示されます。ここには、サポートされているすべてのスイッチがあります。
|
||||
|
||||
```console
|
||||
Nucleiは、広範な設定可能性、大規模な拡張性、および使いやすさに焦点を当てた、
|
||||
高速でテンプレートベースの脆弱性スキャナーです。
|
||||
|
||||
使用法:
|
||||
./nuclei [flags]
|
||||
|
||||
フラグ:
|
||||
ターゲット:
|
||||
-u, -target string[] スキャンする対象のURL/ホスト
|
||||
-l, -list string スキャンする対象のURL/ホストのリストが含まれているファイルへのパス(1行に1つ)
|
||||
-resume string resume.cfgを使用してスキャンを再開(クラスタリングは無効になります)
|
||||
-sa, -scan-all-ips DNSレコードに関連付けられているすべてのIPをスキャン
|
||||
-iv, -ip-version string[] ホスト名のスキャンするIPバージョン(4,6)-(デフォルトは4)
|
||||
|
||||
テンプレート:
|
||||
-nt, -new-templates 最新のnuclei-templatesリリースに追加された新しいテンプレートのみを実行
|
||||
-ntv, -new-templates-version string[] 特定のバージョンに追加された新しいテンプレートを実行
|
||||
-as, -automatic-scan wappalyzer技術検出をタグマッピングに使用した自動Webスキャン
|
||||
-t, -templates string[] 実行するテンプレートまたはテンプレートディレクトリのリスト(カンマ区切り、ファイル)
|
||||
-turl, -template-url string[] 実行するテンプレートのURLまたはテンプレートURLのリスト(カンマ区切り、ファイル)
|
||||
-w, -workflows string[] 実行するワークフローまたはワークフローディレクトリのリスト(カンマ区切り、ファイル)
|
||||
-wurl, -workflow-url string[] 実行するワークフローのURLまたはワークフローURLのリスト(カンマ区切り、ファイル)
|
||||
-validate Nucleiに渡されたテンプレートを検証
|
||||
-nss, -no-strict-syntax テンプレートで厳密な構文チェックを無効にする
|
||||
-td, -template-display テンプレートの内容を表示
|
||||
-tl 利用可能なすべてのテンプレートをリスト
|
||||
-sign NUCLEI_SIGNATURE_PRIVATE_KEY環境変数で定義された秘密鍵でテンプレートに署名
|
||||
-code コードプロトコルベースのテンプレートのロードを有効にする
|
||||
|
||||
フィルタリング:
|
||||
-a, -author string[] 作者に基づいて実行するテンプレート(カンマ区切り、ファイル)
|
||||
-tags string[] タグに基づいて実行するテンプレート(カンマ区切り、ファイル)
|
||||
-etags, -exclude-tags string[] タグに基づいて除外するテンプレート(カンマ区切り、ファイル)
|
||||
-itags, -include-tags string[] デフォルトまたは設定によって除外されている場合でも実行する必要があるタグ
|
||||
-id, -template-id string[] テンプレートIDに基づいて実行するテンプレート(カンマ区切り、ファイル)
|
||||
-eid, -exclude-id string[] テンプレートIDに基づいて除外するテンプレート(カンマ区切り、ファイル)
|
||||
-it, -include-templates string[] デフォルトまたは設定によって除外されている場合でも実行する必要があるテンプレート
|
||||
-et, -exclude-templates string[] 除外するテンプレートまたはテンプレートディレクトリへのパス(カンマ区切り、ファイル)
|
||||
-em, -exclude-matchers string[] 結果で除外するテンプレートマッチャー
|
||||
-s, -severity value[] 重大度に基づいて実行するテンプレート。可能な値:info, low, medium, high, critical, unknown
|
||||
-es, -exclude-severity value[] 重大度に基づいて除外するテンプレート。可能な値:info, low, medium, high, critical, unknown
|
||||
-pt, -type value[] プロトコルタイプに基づいて実行するテンプレート。可能な値:dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-ept, -exclude-type value[] プロトコルタイプに基づいて除外するテンプレート。可能な値:dns, file, http, headless, tcp, workflow, ssl, websocket, whois, code, javascript
|
||||
-tc, -template-condition string[] 式条件に基づいて実行するテンプレート
|
||||
|
||||
出力:
|
||||
-o, -output string 発見された問題/脆弱性を書き込む出力ファイル
|
||||
-sresp, -store-resp Nucleiを通じて渡されたすべてのリクエスト/レスポンスを出力ディレクトリに保存
|
||||
-srd, -store-resp-dir string Nucleiを通じて渡されたすべてのリクエスト/レスポンスをカスタムディレクトリに保存(デフォルトは「output」)
|
||||
-silent 結果のみを表示
|
||||
-nc, -no-color 出力内容の着色を無効にする(ANSIエスケープコード)
|
||||
-j, -jsonl JSONL(ines)形式で出力を書き込む
|
||||
-irr, -include-rr -omit-raw JSON、JSONL、Markdown出力にリクエスト/レスポンスペアを含める(発見のみ)[非推奨 -omit-raw使用](デフォルトはtrue)
|
||||
-or, -omit-raw JSON、JSONL、Markdown出力でリクエスト/レスポンスペアを省略する(発見のみ)
|
||||
-ot, -omit-template JSON、JSONL出力でエンコードされたテンプレートを省略
|
||||
-nm, -no-meta CLI出力で結果のメタデータの印刷を無効にする
|
||||
-ts, -timestamp CLI出力にタイムスタンプを印刷することを有効にする
|
||||
-rdb, -report-db string Nucleiレポートデータベース(レポートデータを永続化するために常にこれを使用)
|
||||
-ms, -matcher-status マッチ失敗のステータスを表示
|
||||
-me, -markdown-export string Markdown形式で結果をエクスポートするディレクトリ
|
||||
-se, -sarif-export string SARIF形式で結果をエクスポートするファイル
|
||||
-je, -json-export string JSON形式で結果をエクスポートするファイル
|
||||
-jle, -jsonl-export string JSONL(ine)形式で結果をエクスポートするファイル
|
||||
|
||||
設定:
|
||||
-config string Nucleiの設定ファイルへのパス
|
||||
-fr, -follow-redirects HTTPテンプレートのリダイレクトをフォローすることを有効にする
|
||||
-fhr, -follow-host-redirects
|
||||
+2
-1
@@ -30,7 +30,8 @@
|
||||
<p align="center">
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README.md">English</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_CN.md">中文</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">한국어</a>
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_KR.md">한국어</a> •
|
||||
<a href="https://github.com/projectdiscovery/nuclei/blob/main/README_ES.md">스페인어</a>
|
||||
</p>
|
||||
|
||||
---
|
||||
|
||||
+38
-1
@@ -1800,13 +1800,50 @@ Valid values:
|
||||
|
||||
Part is the part of request to fuzz.
|
||||
|
||||
query fuzzes the query part of url. More parts will be added later.
|
||||
|
||||
Valid values:
|
||||
|
||||
|
||||
- <code>query</code>
|
||||
|
||||
- <code>header</code>
|
||||
|
||||
- <code>path</code>
|
||||
|
||||
- <code>body</code>
|
||||
|
||||
- <code>cookie</code>
|
||||
|
||||
- <code>request</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
<div class="dd">
|
||||
|
||||
<code>parts</code> <i>[]string</i>
|
||||
|
||||
</div>
|
||||
<div class="dt">
|
||||
|
||||
Parts is the list of parts to fuzz. If multiple parts need to be
|
||||
defined while excluding some, this should be used instead of singular part.
|
||||
|
||||
|
||||
Valid values:
|
||||
|
||||
|
||||
- <code>query</code>
|
||||
|
||||
- <code>header</code>
|
||||
|
||||
- <code>path</code>
|
||||
|
||||
- <code>body</code>
|
||||
|
||||
- <code>cookie</code>
|
||||
|
||||
- <code>request</code>
|
||||
</div>
|
||||
|
||||
<hr />
|
||||
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
logutil "github.com/projectdiscovery/utils/log"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
var httpTestcases = []TestCaseInfo{
|
||||
@@ -509,7 +510,7 @@ func (h *httpPostMultipartBody) Execute(filePath string) error {
|
||||
var routerErr error
|
||||
|
||||
router.POST("/", func(w http.ResponseWriter, r *http.Request, _ httprouter.Params) {
|
||||
if err := r.ParseMultipartForm(1 * 1024); err != nil {
|
||||
if err := r.ParseMultipartForm(unitutils.Mega); err != nil {
|
||||
routerErr = err
|
||||
return
|
||||
}
|
||||
|
||||
@@ -16,12 +16,12 @@ var profileLoaderTestcases = []TestCaseInfo{
|
||||
type profileLoaderByRelFile struct{}
|
||||
|
||||
func (h *profileLoaderByRelFile) Execute(testName string) error {
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "kev.yml")
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "cloud.yml")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) < 267 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 267, len(results))
|
||||
if len(results) <= 10 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 10, len(results))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -29,16 +29,17 @@ func (h *profileLoaderByRelFile) Execute(testName string) error {
|
||||
type profileLoaderById struct{}
|
||||
|
||||
func (h *profileLoaderById) Execute(testName string) error {
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "kev")
|
||||
results, err := testutils.RunNucleiWithArgsAndGetResults(false, "-tl", "-tp", "cloud")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) < 267 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 267, len(results))
|
||||
if len(results) <= 10 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 10, len(results))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// this profile with load kevs
|
||||
type customProfileLoader struct{}
|
||||
|
||||
func (h *customProfileLoader) Execute(filepath string) error {
|
||||
@@ -46,8 +47,8 @@ func (h *customProfileLoader) Execute(filepath string) error {
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("failed to load template with id")
|
||||
}
|
||||
if len(results) < 267 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 267, len(results))
|
||||
if len(results) < 1 {
|
||||
return fmt.Errorf("incorrect result: expected more results than %d, got %v", 1, len(results))
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -13,6 +13,7 @@ var sslTestcases = []TestCaseInfo{
|
||||
{Path: "protocols/ssl/custom-cipher.yaml", TestCase: &sslCustomCipher{}},
|
||||
{Path: "protocols/ssl/custom-version.yaml", TestCase: &sslCustomVersion{}},
|
||||
{Path: "protocols/ssl/ssl-with-vars.yaml", TestCase: &sslWithVars{}},
|
||||
{Path: "protocols/ssl/multi-req.yaml", TestCase: &sslMultiReq{}},
|
||||
}
|
||||
|
||||
type sslBasic struct{}
|
||||
@@ -118,3 +119,23 @@ func (h *sslWithVars) Execute(filePath string) error {
|
||||
|
||||
return expectResultsCount(results, 1)
|
||||
}
|
||||
|
||||
type sslMultiReq struct{}
|
||||
|
||||
func (h *sslMultiReq) Execute(filePath string) error {
|
||||
ts := testutils.NewTCPServer(&tls.Config{}, defaultStaticPort, func(conn net.Conn) {
|
||||
defer conn.Close()
|
||||
data := make([]byte, 4)
|
||||
if _, err := conn.Read(data); err != nil {
|
||||
return
|
||||
}
|
||||
})
|
||||
defer ts.Close()
|
||||
|
||||
results, err := testutils.RunNucleiTemplateAndGetResults(filePath, ts.URL, debug, "-V")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return expectResultsCount(results, 2)
|
||||
}
|
||||
+12
-4
@@ -38,6 +38,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils/monitor"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
updateutils "github.com/projectdiscovery/utils/update"
|
||||
)
|
||||
|
||||
@@ -141,7 +142,10 @@ func main() {
|
||||
nucleiRunner.Close()
|
||||
gologger.Info().Msgf("Creating resume file: %s\n", resumeFileName)
|
||||
err := nucleiRunner.SaveResumeConfig(resumeFileName)
|
||||
return errorutil.NewWithErr(err).Msgf("couldn't create crash resume file")
|
||||
if err != nil {
|
||||
return errorutil.NewWithErr(err).Msgf("couldn't create crash resume file")
|
||||
}
|
||||
return nil
|
||||
})
|
||||
}
|
||||
|
||||
@@ -200,7 +204,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
*/
|
||||
|
||||
flagSet.CreateGroup("input", "Target",
|
||||
flagSet.StringSliceVarP(&options.Targets, "target", "u", nil, "target URLs/hosts to scan", goflags.StringSliceOptions),
|
||||
flagSet.StringSliceVarP(&options.Targets, "target", "u", nil, "target URLs/hosts to scan", goflags.CommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVarP(&options.TargetsFilePath, "list", "l", "", "path to file containing a list of target URLs/hosts to scan (one per line)"),
|
||||
flagSet.StringSliceVarP(&options.ExcludeTargets, "exclude-hosts", "eh", nil, "hosts to exclude to scan from the input list (ip, cidr, hostname)", goflags.FileCommaSeparatedStringSliceOptions),
|
||||
flagSet.StringVar(&options.Resume, "resume", "", "resume scan using resume.cfg (clustering will be disabled)"),
|
||||
@@ -301,7 +305,7 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.AttackType, "attack-type", "at", "", "type of payload combinations to perform (batteringram,pitchfork,clusterbomb)"),
|
||||
flagSet.StringVarP(&options.SourceIP, "source-ip", "sip", "", "source ip address to use for network scan"),
|
||||
flagSet.IntVarP(&options.ResponseReadSize, "response-size-read", "rsr", 0, "max response size to read in bytes"),
|
||||
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", 1*1024*1024, "max response size to read in bytes"),
|
||||
flagSet.IntVarP(&options.ResponseSaveSize, "response-size-save", "rss", unitutils.Mega, "max response size to read in bytes"),
|
||||
flagSet.DurationVarP(&options.ResponseReadTimeout, "response-read-timeout", "rrt", time.Duration(5*time.Second), "response read timeout in seconds"),
|
||||
flagSet.CallbackVar(resetCallback, "reset", "reset removes all nuclei configuration and data files (including nuclei-templates)"),
|
||||
flagSet.BoolVarP(&options.TlsImpersonate, "tls-impersonate", "tlsi", false, "enable experimental client hello (ja3) tls randomization"),
|
||||
@@ -323,6 +327,9 @@ on extensive configurability, massive extensibility and ease of use.`)
|
||||
flagSet.StringVarP(&options.FuzzingMode, "fuzzing-mode", "fm", "", "overrides fuzzing mode set in template (multiple, single)"),
|
||||
flagSet.BoolVar(&fuzzFlag, "fuzz", false, "enable loading fuzzing templates (Deprecated: use -dast instead)"),
|
||||
flagSet.BoolVar(&options.DAST, "dast", false, "enable / run dast (fuzz) nuclei templates"),
|
||||
flagSet.BoolVarP(&options.DisplayFuzzPoints, "display-fuzz-points", "dfp", false, "display fuzz points in the output for debugging"),
|
||||
flagSet.IntVar(&options.FuzzParamFrequency, "fuzz-param-frequency", 10, "frequency of uninteresting parameters for fuzzing before skipping"),
|
||||
flagSet.StringVarP(&options.FuzzAggressionLevel, "fuzz-aggression", "fa", "low", "fuzzing aggression level controls payload count for fuzz (low, medium, high)"),
|
||||
)
|
||||
|
||||
flagSet.CreateGroup("uncover", "Uncover",
|
||||
@@ -413,7 +420,8 @@ 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.BoolVarP(&options.EnableCloudUpload, "cloud-upload", "cup", false, "upload scan results to pdcp dashboard"),
|
||||
flagSet.StringVarP(&options.ScanID, "scan-id", "sid", "", "upload scan results to given scan id"),
|
||||
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.CreateGroup("Authentication", "Authentication",
|
||||
|
||||
@@ -16,16 +16,16 @@ require (
|
||||
github.com/json-iterator/go v1.1.12
|
||||
github.com/julienschmidt/httprouter v1.3.0
|
||||
github.com/logrusorgru/aurora v2.0.3+incompatible
|
||||
github.com/miekg/dns v1.1.57
|
||||
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.0.20
|
||||
github.com/projectdiscovery/fastdialer v0.0.69
|
||||
github.com/projectdiscovery/hmap v0.0.41
|
||||
github.com/projectdiscovery/interactsh v1.1.9
|
||||
github.com/projectdiscovery/rawhttp v0.1.47
|
||||
github.com/projectdiscovery/retryabledns v1.0.58
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.58
|
||||
github.com/projectdiscovery/fastdialer v0.1.5
|
||||
github.com/projectdiscovery/hmap v0.0.47
|
||||
github.com/projectdiscovery/interactsh v1.2.0
|
||||
github.com/projectdiscovery/rawhttp v0.1.51
|
||||
github.com/projectdiscovery/retryabledns v1.0.65
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.66
|
||||
github.com/projectdiscovery/yamldoc-go v1.0.4
|
||||
github.com/remeh/sizedwaitgroup v1.0.0
|
||||
github.com/rs/xid v1.5.0
|
||||
@@ -35,18 +35,18 @@ require (
|
||||
github.com/spf13/cast v1.5.1
|
||||
github.com/syndtr/goleveldb v1.0.0
|
||||
github.com/valyala/fasttemplate v1.2.2
|
||||
github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222
|
||||
github.com/weppos/publicsuffix-go v0.30.2
|
||||
github.com/xanzy/go-gitlab v0.84.0
|
||||
go.uber.org/multierr v1.11.0
|
||||
golang.org/x/net v0.24.0
|
||||
golang.org/x/oauth2 v0.11.0
|
||||
golang.org/x/text v0.14.0
|
||||
golang.org/x/net v0.26.0
|
||||
golang.org/x/oauth2 v0.18.0
|
||||
golang.org/x/text v0.16.0
|
||||
gopkg.in/yaml.v2 v2.4.0
|
||||
)
|
||||
|
||||
require (
|
||||
code.gitea.io/sdk/gitea v0.17.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0
|
||||
github.com/DataDog/gostackparse v0.6.0
|
||||
github.com/Masterminds/semver/v3 v3.2.1
|
||||
@@ -73,35 +73,35 @@ require (
|
||||
github.com/h2non/filetype v1.1.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.1
|
||||
github.com/lib/pq v1.10.9
|
||||
github.com/mattn/go-sqlite3 v1.14.22
|
||||
github.com/mholt/archiver v3.1.1+incompatible
|
||||
github.com/ory/dockertest/v3 v3.10.0
|
||||
github.com/praetorian-inc/fingerprintx v1.1.9
|
||||
github.com/projectdiscovery/dsl v0.0.52
|
||||
github.com/projectdiscovery/dsl v0.1.3
|
||||
github.com/projectdiscovery/fasttemplate v0.0.2
|
||||
github.com/projectdiscovery/go-smb2 v0.0.0-20240129202741-052cc450c6cb
|
||||
github.com/projectdiscovery/goflags v0.1.50
|
||||
github.com/projectdiscovery/goflags v0.1.56
|
||||
github.com/projectdiscovery/gologger v1.1.12
|
||||
github.com/projectdiscovery/gostruct v0.0.2
|
||||
github.com/projectdiscovery/gozero v0.0.2
|
||||
github.com/projectdiscovery/httpx v1.6.0
|
||||
github.com/projectdiscovery/httpx v1.6.3
|
||||
github.com/projectdiscovery/mapcidr v1.1.34
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5
|
||||
github.com/projectdiscovery/ratelimit v0.0.39
|
||||
github.com/projectdiscovery/ratelimit v0.0.45
|
||||
github.com/projectdiscovery/rdap v0.9.1-0.20221108103045-9865884d1917
|
||||
github.com/projectdiscovery/sarif v0.0.1
|
||||
github.com/projectdiscovery/tlsx v1.1.6
|
||||
github.com/projectdiscovery/uncover v1.0.7
|
||||
github.com/projectdiscovery/useragent v0.0.48
|
||||
github.com/projectdiscovery/utils v0.0.92
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.120
|
||||
github.com/projectdiscovery/uncover v1.0.8
|
||||
github.com/projectdiscovery/useragent v0.0.56
|
||||
github.com/projectdiscovery/utils v0.1.4
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.6
|
||||
github.com/redis/go-redis/v9 v9.1.0
|
||||
github.com/seh-msft/burpxml v1.0.1
|
||||
github.com/stretchr/testify v1.9.0
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706
|
||||
golang.org/x/term v0.19.0
|
||||
golang.org/x/term v0.21.0
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
moul.io/http2curl v1.0.0
|
||||
)
|
||||
@@ -109,11 +109,11 @@ require (
|
||||
require (
|
||||
aead.dev/minisign v0.2.0 // indirect
|
||||
dario.cat/mergo v1.0.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 // indirect
|
||||
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/andybalholm/brotli v1.1.0 // indirect
|
||||
@@ -124,7 +124,7 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.14.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/bahlo/generic-list-go v0.2.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.8.0 // indirect
|
||||
github.com/bits-and-blooms/bitset v1.13.0 // indirect
|
||||
github.com/bits-and-blooms/bloom/v3 v3.5.0 // indirect
|
||||
github.com/buger/jsonparser v1.1.1 // indirect
|
||||
github.com/bytedance/sonic v1.9.1 // indirect
|
||||
@@ -134,7 +134,7 @@ require (
|
||||
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
|
||||
github.com/cloudflare/circl v1.3.7 // indirect
|
||||
github.com/cloudflare/circl v1.3.8 // indirect
|
||||
github.com/containerd/continuity v0.4.2 // indirect
|
||||
github.com/cyphar/filepath-securejoin v0.2.4 // indirect
|
||||
github.com/davidmz/go-pageant v1.0.2 // indirect
|
||||
@@ -143,9 +143,10 @@ require (
|
||||
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
|
||||
github.com/fatih/color v1.15.0 // indirect
|
||||
github.com/fatih/color v1.16.0 // indirect
|
||||
github.com/free5gc/util v1.0.5-0.20230511064842-2e120956883b // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
|
||||
github.com/gaissmai/bart v0.9.5 // indirect
|
||||
github.com/geoffgarside/ber v1.1.0 // indirect
|
||||
github.com/gin-contrib/sse v0.1.0 // indirect
|
||||
github.com/gin-gonic/gin v1.9.1 // indirect
|
||||
@@ -156,6 +157,7 @@ require (
|
||||
github.com/go-sourcemap/sourcemap v2.1.4+incompatible // indirect
|
||||
github.com/goccy/go-json v0.10.2 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/google/certificate-transparency-go v1.1.4 // indirect
|
||||
@@ -175,7 +177,7 @@ require (
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kataras/jwt v0.1.10 // indirect
|
||||
github.com/klauspost/compress v1.17.6 // indirect
|
||||
github.com/klauspost/compress v1.17.8 // indirect
|
||||
github.com/klauspost/pgzip v1.2.6 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/logrusorgru/aurora/v4 v4.0.0 // indirect
|
||||
@@ -197,23 +199,21 @@ require (
|
||||
github.com/perimeterx/marshmallow v1.1.5 // indirect
|
||||
github.com/pierrec/lz4/v4 v4.1.21 // indirect
|
||||
github.com/pjbgf/sha1cd v0.3.0 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
|
||||
github.com/projectdiscovery/asnmap v1.1.0 // indirect
|
||||
github.com/projectdiscovery/cdncheck v1.0.9 // indirect
|
||||
github.com/projectdiscovery/freeport v0.0.5 // indirect
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb // indirect
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 // indirect
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 // indirect
|
||||
github.com/quic-go/quic-go v0.42.0 // indirect
|
||||
github.com/refraction-networking/utls v1.6.1 // indirect
|
||||
github.com/refraction-networking/utls v1.6.6 // indirect
|
||||
github.com/sashabaranov/go-openai v1.15.3 // indirect
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible // indirect
|
||||
github.com/shoenig/go-m1cpu v0.1.6 // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/skeema/knownhosts v1.2.1 // indirect
|
||||
github.com/tidwall/btree v1.7.0 // indirect
|
||||
github.com/tidwall/buntdb v1.3.0 // indirect
|
||||
github.com/tidwall/gjson v1.17.0 // indirect
|
||||
github.com/tidwall/buntdb v1.3.1 // indirect
|
||||
github.com/tidwall/gjson v1.17.1 // indirect
|
||||
github.com/tidwall/grect v0.1.4 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
@@ -234,7 +234,7 @@ require (
|
||||
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.6.0 // indirect
|
||||
golang.org/x/sync v0.7.0 // indirect
|
||||
gopkg.in/djherbis/times.v1 v1.3.0 // indirect
|
||||
mellium.im/sasl v0.3.1 // indirect
|
||||
)
|
||||
@@ -262,13 +262,13 @@ require (
|
||||
github.com/gobwas/pool v0.2.1 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
|
||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/go-querystring v1.1.0 // indirect
|
||||
github.com/google/uuid v1.3.1 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/gorilla/css v1.0.1 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 // indirect
|
||||
github.com/hdm/jarm-go v0.0.7 // indirect
|
||||
github.com/itchyny/timefmt-go v0.1.5 // indirect
|
||||
github.com/klauspost/cpuid/v2 v2.2.5 // indirect
|
||||
@@ -285,13 +285,13 @@ require (
|
||||
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.8
|
||||
github.com/projectdiscovery/networkpolicy v0.0.9
|
||||
github.com/rivo/uniseg v0.4.6 // 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
|
||||
github.com/trivago/tgo v1.0.7
|
||||
github.com/ulikunitz/xz v0.5.11 // indirect
|
||||
github.com/ulikunitz/xz v0.5.12 // indirect
|
||||
github.com/valyala/bytebufferpool v1.0.0 // indirect
|
||||
github.com/yl2chen/cidranger v1.0.2 // indirect
|
||||
github.com/ysmood/goob v0.4.0 // indirect
|
||||
@@ -299,17 +299,17 @@ require (
|
||||
github.com/ysmood/leakless v0.8.0 // indirect
|
||||
github.com/yusufpapurcu/wmi v1.2.4 // indirect
|
||||
github.com/zmap/rc2 v0.0.0-20190804163417-abaa70531248 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c // indirect
|
||||
go.etcd.io/bbolt v1.3.8 // indirect
|
||||
github.com/zmap/zcrypto v0.0.0-20240512203510-0fef58d9a9db // indirect
|
||||
go.etcd.io/bbolt v1.3.10 // indirect
|
||||
go.uber.org/zap v1.25.0 // indirect
|
||||
goftp.io/server/v2 v2.0.1 // indirect
|
||||
golang.org/x/crypto v0.22.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a
|
||||
golang.org/x/mod v0.14.0 // indirect
|
||||
golang.org/x/sys v0.19.0 // indirect
|
||||
golang.org/x/crypto v0.24.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/time v0.5.0 // indirect
|
||||
golang.org/x/tools v0.17.0
|
||||
google.golang.org/appengine v1.6.7 // indirect
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d
|
||||
google.golang.org/appengine v1.6.8 // indirect
|
||||
google.golang.org/protobuf v1.33.0 // indirect
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6 // indirect
|
||||
gopkg.in/corvus-ch/zbase32.v1 v1.0.0 // indirect
|
||||
|
||||
@@ -41,14 +41,14 @@ dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7
|
||||
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a h1:3i+FJ7IpSZHL+VAjtpQeZCRhrpP0odl5XfoLBY4fxJ8=
|
||||
git.mills.io/prologic/smtpd v0.0.0-20210710122116-a525b76c287a/go.mod h1:C7hXLmFmPYPjIDGfQl1clsmQ5TMEQfmzWTrJk475bUs=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0/go.mod h1:h6H6c8enJmmocHUbLiiGY6sx7f9i+X3m1CHdd5c6Rdw=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0 h1:8q4SaHjFsClSvuVne0ID/5Ka8u3fcIHyqkLjcFpNRHQ=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0/go.mod h1:bjGvMhVMb+EEm3VRNQawDMUyMMjo+S5ewNjflkep/0Q=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1 h1:E+OJmp2tPvt1W+amx48v1eqbjDYsgN+RzP4q16yV5eM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.11.1/go.mod h1:a6xsAQUZg+VsS3TJ05SRp524Hs4pZ/AeFSr5ENf0Yjo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0/go.mod h1:HcM1YX14R7CJcghJGOYCgdezslRSVzqwLf/q+4Y2r/0=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0 h1:vcYCAze6p19qBW7MhZybIsqD8sMV8js0NyQM8JDnVtg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9OrhHJoDD8ZDq51FHgXjqtP9z6bEwBq9U=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0 h1:U2rTu3Ef+7w9FHKIAXM6ZyqF3UOWJZ12zIm8zECAFfg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.6.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0 h1:jBQA3cKT4L2rWMpgE7Yt3Hwh2aUj8KXjIGLxjHeYNNo=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.8.0/go.mod h1:4OG6tQ9EOP/MT0NMjDlRzWoVFxfu9rN9B2X+tlSVktg=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4=
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg=
|
||||
@@ -57,8 +57,8 @@ github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 h1:L/gRVlceqvL25
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 h1:mFRzDkZVAjdal+s7s0MwaRv9igoPqLRdzOLzw/8Xvq8=
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358/go.mod h1:chxPXzSsl7ZWRAuOIE23GDNzjWuZquvFlgA8xmpunjU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/DataDog/gostackparse v0.6.0 h1:egCGQviIabPwsyoWpGvIBGrEnNWez35aEO7OJ1vBI4o=
|
||||
@@ -186,8 +186,9 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bits-and-blooms/bitset v1.8.0 h1:FD+XqgOZDUxxZ8hzoBFuV9+cGWY9CslN6d5MS5JVb4c=
|
||||
github.com/bits-and-blooms/bitset v1.8.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bitset v1.13.0 h1:bAQ9OPNFYbGHV6Nez0tmNI0RiEu7/hxlYJRUA0wFAVE=
|
||||
github.com/bits-and-blooms/bitset v1.13.0/go.mod h1:7hO7Gc7Pp1vODcmWvKMRA9BNmbv6a/7QIWpPxHddWR8=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.5.0 h1:AKDvi1V3xJCmSR6QhcBfHbCN4Vf8FfxeWkMNQfmAGhY=
|
||||
github.com/bits-and-blooms/bloom/v3 v3.5.0/go.mod h1:Y8vrn7nk1tPIlmLtW2ZPV+W7StdVMor6bC1xgpjMZFs=
|
||||
github.com/bluele/gcache v0.0.2 h1:WcbfdXICg7G/DGBh1PFfcirkWOQV+v077yF1pSy3DGw=
|
||||
@@ -235,8 +236,8 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk
|
||||
github.com/cloudflare/cfssl v1.6.4 h1:NMOvfrEjFfC63K3SGXgAnFdsgkmiq4kATme5BfcqrO8=
|
||||
github.com/cloudflare/cfssl v1.6.4/go.mod h1:8b3CQMxfWPAeom3zBnGJ6sd+G1NkL5TXqmDXacb+1J0=
|
||||
github.com/cloudflare/circl v1.1.0/go.mod h1:prBCrKB9DV4poKZY1l9zBXg2QJY7mvgRvtMxxK7fi4I=
|
||||
github.com/cloudflare/circl v1.3.7 h1:qlCDlTPz2n9fu58M0Nh1J/JzcFpfgkFHHX3O35r5vcU=
|
||||
github.com/cloudflare/circl v1.3.7/go.mod h1:sRTcRWXGLrKw6yIGJ+l7amYJFfAXbZG0kBSc8r4zxgA=
|
||||
github.com/cloudflare/circl v1.3.8 h1:j+V8jJt09PoeMFIu2uh5JUyEaIHTXVOHslFoLNAKqwI=
|
||||
github.com/cloudflare/circl v1.3.8/go.mod h1:PDRU+oXvdD7KCtgKxW95M5Z8BpSCJXQORiZFnBQS5QU=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08 h1:ox2F0PSMlrAAiAdknSRMDrAr8mfxPCfSZolH+/qQnyQ=
|
||||
github.com/cnf/structhash v0.0.0-20201127153200-e1b16c1ebc08/go.mod h1:pCxVEbcm3AMg7ejXyorUXi6HQCzOIBf7zEDVPtw0/U4=
|
||||
@@ -273,7 +274,6 @@ github.com/dlclark/regexp2 v1.4.1-0.20201116162257-a2a8dda75c91/go.mod h1:2pZnwu
|
||||
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/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
|
||||
github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
|
||||
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=
|
||||
@@ -310,8 +310,8 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fatih/color v1.15.0 h1:kOqh6YHBtK8aywxGerMG2Eq3H6Qgoqeo13Bk2Mv/nBs=
|
||||
github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw=
|
||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||
github.com/fatih/structs v1.1.0 h1:Q7juDM0QtcnhCpeyLGQKyg4TOIghuNXrkL32pHAUMxo=
|
||||
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
|
||||
github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw=
|
||||
@@ -327,6 +327,8 @@ github.com/fsnotify/fsnotify v1.6.0 h1:n+5WquG0fcWoWp6xPWfHdbskMCQaFnG6PfBrh1Ky4
|
||||
github.com/fsnotify/fsnotify v1.6.0/go.mod h1:sl3t1tCWJFWoRz9R8WJCbQihKKwmorjAbSClcnxKAGw=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2 h1:w5qFW6JKBz9Y393Y4q372O9A7cUSequkh1Q7OhCmWKU=
|
||||
github.com/gabriel-vasile/mimetype v1.4.2/go.mod h1:zApsH/mKG4w07erKIaJPFiX0Tsq9BFQgN3qGY5GnNgA=
|
||||
github.com/gaissmai/bart v0.9.5 h1:vy+r4Px6bjZ+v2QYXAsg63vpz9IfzdW146A8Cn4GPIo=
|
||||
github.com/gaissmai/bart v0.9.5/go.mod h1:KHeYECXQiBjTzQz/om2tqn3sZF1J7hw9m6z41ftj3fg=
|
||||
github.com/geoffgarside/ber v1.1.0 h1:qTmFG4jJbwiSzSXoNJeHcOprVzZ8Ulde2Rrrifu5U9w=
|
||||
github.com/geoffgarside/ber v1.1.0/go.mod h1:jVPKeCbj6MvQZhwLYsGwaGI52oUorHoHKNecGT85ZCc=
|
||||
github.com/getkin/kin-openapi v0.123.0 h1:zIik0mRwFNLyvtXK274Q6ut+dPh6nlxBp0x7mNrPhs8=
|
||||
@@ -367,8 +369,6 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
|
||||
github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=
|
||||
github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
|
||||
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
|
||||
github.com/go-openapi/jsonpointer v0.20.2 h1:mQc3nmndL8ZBzStEo3JYF8wzmeWffDH4VbXz58sAx6Q=
|
||||
@@ -394,8 +394,6 @@ github.com/go-sql-driver/mysql v1.4.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG
|
||||
github.com/go-sql-driver/mysql v1.7.1 h1:lUIinVbN1DY0xBg0eMOzmmtGoHwWBbvnWubQUrtU8EI=
|
||||
github.com/go-sql-driver/mysql v1.7.1/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=
|
||||
github.com/go-test/deep v1.0.8 h1:TDsG77qcSprGbC6vTN8OuXp5g+J+b5Pcguhf7Zt61VM=
|
||||
github.com/go-test/deep v1.0.8/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
|
||||
github.com/goburrow/cache v0.1.4 h1:As4KzO3hgmzPlnaMniZU9+VmoNYseUhuELbxy9mRBfw=
|
||||
@@ -420,6 +418,8 @@ github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzq
|
||||
github.com/golang-jwt/jwt/v4 v4.4.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0 h1:7cYmW1XlMY7h7ii7UhUyChSgS5wUJEnm9uZVTGqOWzg=
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY=
|
||||
github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0=
|
||||
github.com/golang-sql/sqlexp v0.1.0 h1:ZCD6MBpcuOVfGVqsEmY5/4FtYiKz6tSyUv9LPEDei6A=
|
||||
@@ -454,8 +454,9 @@ github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.2/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
||||
@@ -504,8 +505,8 @@ github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm4
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=
|
||||
github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=
|
||||
github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
|
||||
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8=
|
||||
@@ -530,13 +531,13 @@ github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brv
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=
|
||||
github.com/hashicorp/go-hclog v0.9.2 h1:CG6TE5H9/JXsFWJCfoIVpKFIkFe6ysEuHirp4DxCsHI=
|
||||
github.com/hashicorp/go-hclog v0.9.2/go.mod h1:5CU+agLiy3J7N7QjHK5d05KxGsuXiQLrjA0H7acj2lQ=
|
||||
github.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=
|
||||
github.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2 h1:AcYqCvkpalPnPF2pn0KamgwamS42TqUDDYFRKq/RAd0=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.2/go.mod h1:Jy/gPYAdjqffZ/yFGCFV2doI5wjtH1ewM9u8iYVjtX8=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7 h1:C8hUCYzor8PIfXHa4UrZkU4VvK8o9ISHxT2Q8+VepXU=
|
||||
github.com/hashicorp/go-retryablehttp v0.7.7/go.mod h1:pkQpWZeYWskR+D1tR2O5OcBFOxfA7DoAO6xtkuQnHTk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
@@ -630,8 +631,8 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
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.6 h1:60eq2E/jlfwQXtvZEeBUYADs+BwKBWURIY+Gj2eRGjI=
|
||||
github.com/klauspost/compress v1.17.6/go.mod h1:/dCuZOvVtNoHsyb+cuJD3itjs3NbnF6KH9zAO4BDxPM=
|
||||
github.com/klauspost/compress v1.17.8 h1:YcnTYrq7MikUT7k0Yb5eceMmALQPYBW/Xltxn0NAMnU=
|
||||
github.com/klauspost/compress v1.17.8/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
|
||||
github.com/klauspost/cpuid v1.2.0/go.mod h1:Pj4uuM528wm8OyEC2QMXAi2YiTZ96dNQPGgoMS4s3ek=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
@@ -662,8 +663,8 @@ github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
|
||||
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa h1:KQKuQDgA3DZX6C396lt3WDYB9Um1gLITLbvficVbqXk=
|
||||
github.com/leslie-qiwa/flat v0.0.0-20230424180412-f9d1cf014baa/go.mod h1:HbwNE4XGwjgtUELkvQaAOjWrpianHYZdQVNqSdYW3UM=
|
||||
github.com/lib/pq v1.10.1 h1:6VXZrLU0jHBYyAqrSPa+MgPfnSvTPuMgK+k0o5kVFWo=
|
||||
github.com/lib/pq v1.10.1/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
github.com/libdns/libdns v0.2.1 h1:Wu59T7wSHRgtA0cfxC+n1c/e+O3upJGWytknkmFEDis=
|
||||
github.com/libdns/libdns v0.2.1/go.mod h1:yQCXzk1lEZmmCPa857bnk4TsOiqYasqpyOEeSObbb40=
|
||||
github.com/lightstep/lightstep-tracer-common/golang/gogo v0.0.0-20190605223551-bc2310a04743/go.mod h1:qklhhLq1aX+mtWk9cPHPzaBjWImj5ULL6C7HFJtXQMM=
|
||||
@@ -714,8 +715,8 @@ github.com/microcosm-cc/bluemonday v1.0.26 h1:xbqSvqzQMeEHCqMi64VAs4d8uy6Mequs3r
|
||||
github.com/microcosm-cc/bluemonday v1.0.26/go.mod h1:JyzOCs9gkyQyjs+6h10UEVSe02CGwkhd72Xdqh78TWs=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/dns v1.1.35/go.mod h1:KNUDUusw/aVsxyTYZM1oqvCicbwhgbNgztCETuNZ7xM=
|
||||
github.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=
|
||||
github.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=
|
||||
github.com/miekg/dns v1.1.59 h1:C9EXc/UToRwKLhK5wKU/I4QVsBUc8kE6MkHBkeypWZs=
|
||||
github.com/miekg/dns v1.1.59/go.mod h1:nZpewl5p6IvctfgrckopVx2OlSEHPRO/U4SYkRklrEk=
|
||||
github.com/minio/minio-go/v6 v6.0.46/go.mod h1:qD0lajrGW49lKZLtXKtCB4X/qkMf0a5tBvN2PaZg7Gg=
|
||||
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7 h1:yRZGarbxsRytL6EGgbqK2mCY+Lk5MWKQYKJT2gEglhc=
|
||||
github.com/minio/selfupdate v0.6.1-0.20230907112617-f11e74f84ca7/go.mod h1:bO02GTIPCMQFTEvE5h4DjYB58bCoZ35XLeBf0buTDdM=
|
||||
@@ -773,8 +774,6 @@ github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+W
|
||||
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
|
||||
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=
|
||||
github.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=
|
||||
github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
|
||||
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
|
||||
@@ -813,8 +812,8 @@ github.com/pierrec/lz4/v4 v4.1.21/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFu
|
||||
github.com/pjbgf/sha1cd v0.3.0 h1:4D5XXmUUBUl/xQ6IjCkEAbqXskkq/4O7LmGn0AqMDs4=
|
||||
github.com/pjbgf/sha1cd v0.3.0/go.mod h1:nZ1rrWOcGJ5uZgEEVL1VUM9iRQiZvWdbZjkKyFzPPsI=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
@@ -835,30 +834,30 @@ github.com/projectdiscovery/cdncheck v1.0.9 h1:BS15gzj9gb5AVSKqTDzPamfSgStu7nJQO
|
||||
github.com/projectdiscovery/cdncheck v1.0.9/go.mod h1:18SSl1w7rMj53CGeRIZTbDoa286a6xZIxGbaiEo4Fxs=
|
||||
github.com/projectdiscovery/clistats v0.0.20 h1:5jO5SLiRJ7f0nDV0ndBNmBeesbROouPooH+DGMgoWq4=
|
||||
github.com/projectdiscovery/clistats v0.0.20/go.mod h1:GJ2av0KnOvK0AISQnP8hyDclYIji1LVkx2l0pwnzAu4=
|
||||
github.com/projectdiscovery/dsl v0.0.52 h1:jvIvF+qN8+MbI1MHtWJJKfWqAZQlCExL3ob7SddQbZE=
|
||||
github.com/projectdiscovery/dsl v0.0.52/go.mod h1:xfcHwhy2HSaeGgh+1wqzOoCGm2XTdh5JzjBRBVHEMvI=
|
||||
github.com/projectdiscovery/fastdialer v0.0.69 h1:BfFQTyTB1hrw9sWCw4CjQfbmlpvnJCPZEmKtxcwJGbU=
|
||||
github.com/projectdiscovery/fastdialer v0.0.69/go.mod h1:RXrx7M2T3V3rMZ2h0X2/SsY93+RhgF/LmFa1E0MI3L8=
|
||||
github.com/projectdiscovery/dsl v0.1.3 h1:Lxlgpx8lwq/CaBjVUmwF6KmkMDnYf9IVKlMsWCF72OE=
|
||||
github.com/projectdiscovery/dsl v0.1.3/go.mod h1:WAuQLD4KQ/94RASotE7cvk7X6kY7JQG9Fcqc3lRQfpI=
|
||||
github.com/projectdiscovery/fastdialer v0.1.5 h1:5rNJ8I+GVzDsUqRSVLUqGTm7LsshfNYmCh20lLn1aaI=
|
||||
github.com/projectdiscovery/fastdialer v0.1.5/go.mod h1:oVoMl9Y6da2timhszy0okyBvhbKDSk+zqFazGV4y6pU=
|
||||
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/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.50 h1:vhpM3mDTnXTktypUuVKWxgRpuBFJE+wzOr56rnOl4Bc=
|
||||
github.com/projectdiscovery/goflags v0.1.50/go.mod h1:3A8u7Q7e59S7f6QlJmMvKMXD/Tp+bfWre64gUpt6a6Q=
|
||||
github.com/projectdiscovery/goflags v0.1.56 h1:tJYiZN7s9Jk9DxfYOUiqOoybaIDlXyX4ZgT4B/06SyU=
|
||||
github.com/projectdiscovery/goflags v0.1.56/go.mod h1:DsGF0NPpM5hGg75N3MTSvWJ4MIT7HFEAOEeWZ074+Fg=
|
||||
github.com/projectdiscovery/gologger v1.1.12 h1:uX/QkQdip4PubJjjG0+uk5DtyAi1ANPJUvpmimXqv4A=
|
||||
github.com/projectdiscovery/gologger v1.1.12/go.mod h1:DI8nywPLERS5mo8QEA9E7gd5HZ3Je14SjJBH3F5/kLw=
|
||||
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.41 h1:8IgTyDce3/2JzcfPVA4H+XpBRFfETULx8td3BMdSYVE=
|
||||
github.com/projectdiscovery/hmap v0.0.41/go.mod h1:bCrai6x5Eijqm2U+jtcH0wZX5ZcaZhcvzoMGTZgLAf0=
|
||||
github.com/projectdiscovery/httpx v1.6.0 h1:6g4UoSQpsOyZgaK+SMLLnZIAU0eYyTxBUwVl+jtm0JQ=
|
||||
github.com/projectdiscovery/httpx v1.6.0/go.mod h1:dzMzOWKfeofaXcXzac3O+YmuY24P0CRnviKGxvol6MM=
|
||||
github.com/projectdiscovery/interactsh v1.1.9 h1:b77SaSGrO+DtivmWwqGGY2dmNlQC3Zgmwlaj9L4Oqvc=
|
||||
github.com/projectdiscovery/interactsh v1.1.9/go.mod h1:0FRQXCildcTLq7Tsa4BVZAsFCXhpWs4C4quKWigXb5I=
|
||||
github.com/projectdiscovery/hmap v0.0.47 h1:NdakfvlFruevnOvehdllofg/hc0CQn2WQaLHaRcsGAk=
|
||||
github.com/projectdiscovery/hmap v0.0.47/go.mod h1:/9V6EnyTY33hAy71Quox6Ggu9ZkKp36tgepBh/NzNSg=
|
||||
github.com/projectdiscovery/httpx v1.6.3 h1:TSu3zEKOHGFfMsGeB0lZEFRl6Tq4nPuIgKDXi9NnYpg=
|
||||
github.com/projectdiscovery/httpx v1.6.3/go.mod h1:9FmsmkT71aTa4WnOyBIPzB8I9c0QAJ9PSVUqs2NkHRQ=
|
||||
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=
|
||||
github.com/projectdiscovery/ldapserver v1.0.2-0.20240219154113-dcc758ebc0cb/go.mod h1:vmgC0DTFCfoCLp0RAfsfYTZZan0QMVs+cmTbH6blfjk=
|
||||
github.com/projectdiscovery/machineid v0.0.0-20240226150047-2e2c51e35983 h1:ZScLodGSezQVwsQDtBSMFp72WDq0nNN+KE/5DHKY5QE=
|
||||
@@ -867,32 +866,32 @@ github.com/projectdiscovery/mapcidr v1.1.34 h1:udr83vQ7oz3kEOwlsU6NC6o08leJzSDQt
|
||||
github.com/projectdiscovery/mapcidr v1.1.34/go.mod h1:1+1R6OkKSAKtWDXE9RvxXtXPoajXTYX0eiEdkqlhQqQ=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5 h1:L/e8z8yw1pfT6bg35NiN7yd1XKtJap5Nk6lMwQ0RNi8=
|
||||
github.com/projectdiscovery/n3iwf v0.0.0-20230523120440-b8cd232ff1f5/go.mod h1:pGW2ncnTxTxHtP9wzcIJAB+3/NMp6IiuQWd2NK7K+oc=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.8 h1:XvfBaBwSDNTesSfNQP9VLk3HX9I7x7gHm028TJ5XwI8=
|
||||
github.com/projectdiscovery/networkpolicy v0.0.8/go.mod h1:xnjNqhemxUPxU+UD5Jgsc3+K8IVmcqT1SJeo6UzMtkI=
|
||||
github.com/projectdiscovery/ratelimit v0.0.39 h1:gMpkkuRSzsxNOxPOiD0aYTLQTOiqM/I0WZSZ7kmL+hc=
|
||||
github.com/projectdiscovery/ratelimit v0.0.39/go.mod h1:vR+q959CbyXCV/pD4bl4aGP2/R91BOKv1SyiHDOqeL0=
|
||||
github.com/projectdiscovery/rawhttp v0.1.47 h1:bjhZBK+7iuvcu0QTRJjdS4wP747+33Sd5gfgc4V9AWU=
|
||||
github.com/projectdiscovery/rawhttp v0.1.47/go.mod h1:2XA7xODWEGoHpEB8qzgqsV8yVL3cg5G63ZaT9ALwU4g=
|
||||
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.45 h1:h28oF+hJ0CHcdBZozT1Go7ppWmzTxSXDKNNh2G1Ot9Q=
|
||||
github.com/projectdiscovery/ratelimit v0.0.45/go.mod h1:1vSJUseDS7SjNwIBi9wNRcgsMKNTLxy/GfdlLFVbgI4=
|
||||
github.com/projectdiscovery/rawhttp v0.1.51 h1:T4sskU2fsX9zNR7nyae0tD/0fliIs73KwBzyoJwBs10=
|
||||
github.com/projectdiscovery/rawhttp v0.1.51/go.mod h1:ejBaw3dCfJSfbtFv0ib6YfFSjQzt7ZdRVpQWJASxIUs=
|
||||
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.58 h1:ut1FSB9+GZ6zQIlKJFLqIz2RZs81EmkbsHTuIrWfYLE=
|
||||
github.com/projectdiscovery/retryabledns v1.0.58/go.mod h1:RobmKoNBgngAVE4H9REQtaLP1pa4TCyypHy1MWHT1mY=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.58 h1:i5BlSJGgNnoTULyqLSe3d39o/XShxK4oEvx0e/gb9N4=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.58/go.mod h1:bbok7sSEplSwZOY91UlLdVilhavYos1RaCJLJx761V0=
|
||||
github.com/projectdiscovery/retryabledns v1.0.65 h1:wOtMuVkrcrGkzaT8Jixb3BtUcJODXLRz+z9EjftmFsQ=
|
||||
github.com/projectdiscovery/retryabledns v1.0.65/go.mod h1:lf3xRdFhGh55fWVlzK1oAaDPrsKliqH3gGvsFNAOkCk=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.66 h1:dY+sGVf/O8CvKcSdTx0rxJoKG+HyOScz5T89TijZohU=
|
||||
github.com/projectdiscovery/retryablehttp-go v1.0.66/go.mod h1:3QJt06j4hEZVpJOr31u49I3mDnqbAzhcjJX8Yvn/xnY=
|
||||
github.com/projectdiscovery/sarif v0.0.1 h1:C2Tyj0SGOKbCLgHrx83vaE6YkzXEVrMXYRGLkKCr/us=
|
||||
github.com/projectdiscovery/sarif v0.0.1/go.mod h1:cEYlDu8amcPf6b9dSakcz2nNnJsoz4aR6peERwV+wuQ=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2 h1:uzmw3IVLJSMW1kEg8eCStG/cGbYYZAja8BH3LqqJXMA=
|
||||
github.com/projectdiscovery/stringsutil v0.0.2/go.mod h1:EJ3w6bC5fBYjVou6ryzodQq37D5c6qbAYQpGmAy+DC0=
|
||||
github.com/projectdiscovery/tlsx v1.1.6 h1:iw2zwKbd2+kRQ8J1G4dLmS0CLyemd/tKz1UzcNsC77A=
|
||||
github.com/projectdiscovery/tlsx v1.1.6/go.mod h1:s7SRRFdrwIZBK/RXXZi4CR/CubqFSvp8h5Bk1srEZIo=
|
||||
github.com/projectdiscovery/uncover v1.0.7 h1:ut+2lTuvmftmveqF5RTjMWAgyLj8ltPQC7siFy9sj0A=
|
||||
github.com/projectdiscovery/uncover v1.0.7/go.mod h1:HFXgm1sRPuoN0D4oATljPIdmbo/EEh1wVuxQqo/dwFE=
|
||||
github.com/projectdiscovery/useragent v0.0.48 h1:ITygElwcY9FlOt0F65kcW/oAALNr1nQOtO3kR9lRzaY=
|
||||
github.com/projectdiscovery/useragent v0.0.48/go.mod h1:ahQMoWlVNFVQxjHOKqOPHJJQ7Ovz1zJZuJbBsAwIcOw=
|
||||
github.com/projectdiscovery/utils v0.0.92 h1:lGCmjUJhzoNX4FQZWpp80058pRlD0/dYxLJOSs07EqY=
|
||||
github.com/projectdiscovery/utils v0.0.92/go.mod h1:d5uvD5qcRiK3qxZbBy9eatCqrCSuj9SObL04w/WgXSg=
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.120 h1:dphOXnaT3rryo9h9fgbxnAVhtQ1uq61yyQZMYyHz960=
|
||||
github.com/projectdiscovery/wappalyzergo v0.0.120/go.mod h1:qW0PP+UBMcdQBBnwk+X6YYFs6huKNvn2BOVs4vQPru0=
|
||||
github.com/projectdiscovery/uncover v1.0.8 h1:nE8bYJuwhqk0BEMRQRhNbjPGMy40A9gkiMk5xq6U4u0=
|
||||
github.com/projectdiscovery/uncover v1.0.8/go.mod h1:1SwsNqjwMkJEzJQ7lQr5AHIdYd+BQlyqhO5IwIYmIAM=
|
||||
github.com/projectdiscovery/useragent v0.0.56 h1:KpUtOgq1vUerPrgRFZAIr1hF0lDxA2RF6YkT2PLwSoQ=
|
||||
github.com/projectdiscovery/useragent v0.0.56/go.mod h1:LAh9vudxXPA5JfNN3+1g/Unqvulq0BDkjxK7IrE+e/s=
|
||||
github.com/projectdiscovery/utils v0.1.4 h1:VSULN/RBEXbkgWuhrDey84ZDnSXtYMSev9p+QHXXDeI=
|
||||
github.com/projectdiscovery/utils v0.1.4/go.mod h1:mXs6OOeG9l/dVchjB2PGvQO3+wuMiE14Y/kmHeKogoM=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.6 h1:4iZDEnytsnAqjQgD5K6lmAzVx++KSQeMCDfomDtQM1M=
|
||||
github.com/projectdiscovery/wappalyzergo v0.1.6/go.mod h1:wBYGKmA5BQp/NWsAy1q/jSH8N1LHWQ/LV26DuR+KzPM=
|
||||
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=
|
||||
@@ -927,13 +926,11 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4=
|
||||
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
|
||||
github.com/quic-go/quic-go v0.42.0 h1:uSfdap0eveIl8KXnipv9K7nlwZ5IqLlYOpJ58u5utpM=
|
||||
github.com/quic-go/quic-go v0.42.0/go.mod h1:132kz4kL3F9vxhW3CtQJLDVwcFe5wdWeJXXijhsO57M=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20181016184325-3113b8401b8a/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/redis/go-redis/v9 v9.1.0 h1:137FnGdk+EQdCbye1FW+qOEcY5S+SpY9T0NiuqvtfMY=
|
||||
github.com/redis/go-redis/v9 v9.1.0/go.mod h1:urWj3He21Dj5k4TK1y59xH8Uj6ATueP8AH1cY3lZl4c=
|
||||
github.com/refraction-networking/utls v1.6.1 h1:n1JG5karzdGWsI6iZmGrOv3SNzR4c+4M8J6KWGsk3lA=
|
||||
github.com/refraction-networking/utls v1.6.1/go.mod h1:+EbcQOvQvXoFV9AEKbuGlljt1doLRKAVY1jJHe9EtDo=
|
||||
github.com/refraction-networking/utls v1.6.6 h1:igFsYBUJPYM8Rno9xUuDoM5GQrVEqY4llzEXOkL43Ig=
|
||||
github.com/refraction-networking/utls v1.6.6/go.mod h1:BC3O4vQzye5hqpmDTWUqi4P5DDhzJfkV1tdqtawQIH0=
|
||||
github.com/remeh/sizedwaitgroup v1.0.0 h1:VNGGFwNo/R5+MJBf6yrsr110p0m4/OX4S3DCy7Kyl5E=
|
||||
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=
|
||||
@@ -949,7 +946,6 @@ github.com/rs/xid v1.5.0 h1:mKX4bl4iPYJtEIxp6CYiUuLQ/8DYMoz0PUdtGgMFRVc=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/saintfish/chardet v0.0.0-20120816061221-3af4cd4741ca/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
|
||||
github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
|
||||
github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E=
|
||||
@@ -1016,17 +1012,17 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/syndtr/goleveldb v1.0.0 h1:fBdIW9lB4Iz0n9khmH8w27SJ3QEJ7+IgjPEwGSZiFdE=
|
||||
github.com/syndtr/goleveldb v1.0.0/go.mod h1:ZVVdQEZoIme9iO1Ch2Jdy24qqXrMMOU6lpPAyBWyWuQ=
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc h1:/5P5I7oDqdLee8W9Moof0xSD8tT1qEVzhObSI9CqHkg=
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240426214851-746d64600adc/go.mod h1:uQdBQGrE1fZ2EyOs0pLcCDd1bBV4rSThieuIIGhXZ50=
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9 h1:GXIyLuIJ5Qk46lI8WJ83qHBZKUI3zhmMmuoY9HICUIQ=
|
||||
github.com/tarunKoyalwar/goleak v0.0.0-20240429141123-0efa90dbdcf9/go.mod h1:uQdBQGrE1fZ2EyOs0pLcCDd1bBV4rSThieuIIGhXZ50=
|
||||
github.com/tidwall/assert v0.1.0 h1:aWcKyRBUAdLoVebxo95N7+YZVTFF/ASTr7BN4sLP6XI=
|
||||
github.com/tidwall/assert v0.1.0/go.mod h1:QLYtGyeqse53vuELQheYl9dngGCJQ+mTtlxcktb+Kj8=
|
||||
github.com/tidwall/btree v1.7.0 h1:L1fkJH/AuEh5zBnnBbmTwQ5Lt+bRJ5A8EWecslvo9iI=
|
||||
github.com/tidwall/btree v1.7.0/go.mod h1:twD9XRA5jj9VUQGELzDO4HPQTNJsoWWfYEL+EUQ2cKY=
|
||||
github.com/tidwall/buntdb v1.3.0 h1:gdhWO+/YwoB2qZMeAU9JcWWsHSYU3OvcieYgFRS0zwA=
|
||||
github.com/tidwall/buntdb v1.3.0/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/buntdb v1.3.1 h1:HKoDF01/aBhl9RjYtbaLnvX9/OuenwvQiC3OP1CcL4o=
|
||||
github.com/tidwall/buntdb v1.3.1/go.mod h1:lZZrZUWzlyDJKlLQ6DKAy53LnG7m5kHyrEHvvcDmBpU=
|
||||
github.com/tidwall/gjson v1.12.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.0 h1:/Jocvlh98kcTfpN2+JzGQWQcqrPQwDrVEMApx/M5ZwM=
|
||||
github.com/tidwall/gjson v1.17.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/gjson v1.17.1 h1:wlYEnwqAHgzmhNUFfw7Xalt2JzQvsMx2Se4PcoFCT/U=
|
||||
github.com/tidwall/gjson v1.17.1/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
|
||||
github.com/tidwall/grect v0.1.4 h1:dA3oIgNgWdSspFzn1kS4S/RDpZFLrIxAZOdJKjYapOg=
|
||||
github.com/tidwall/grect v0.1.4/go.mod h1:9FBsaYRaR0Tcy4UwefBX/UDcDcDy9V5jUcxHzv2jd5Q=
|
||||
github.com/tidwall/lotsa v1.0.2 h1:dNVBH5MErdaQ/xd9s769R31/n2dXavsQ0Yf4TMEHHw8=
|
||||
@@ -1057,8 +1053,8 @@ github.com/ugorji/go/codec v1.2.11 h1:BMaWp1Bb6fHwEtbplGBGJ498wD+LKlNSl25MjdZY4d
|
||||
github.com/ugorji/go/codec v1.2.11/go.mod h1:UNopzCgEMSXjBc6AOMqYvWC1ktqTAfzJZUZgYf6w6lg=
|
||||
github.com/ulikunitz/xz v0.5.8/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.9/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.11 h1:kpFauv27b6ynzBNT/Xy+1k+fK4WswhN/6PN5WhFAGw8=
|
||||
github.com/ulikunitz/xz v0.5.11/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/ulikunitz/xz v0.5.12 h1:37Nm15o69RwBkXM0J6A5OlE67RZTfzUxTj8fB3dfcsc=
|
||||
github.com/ulikunitz/xz v0.5.12/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
github.com/urfave/cli v1.22.1/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
|
||||
github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw=
|
||||
@@ -1069,8 +1065,8 @@ github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+
|
||||
github.com/weppos/publicsuffix-go v0.12.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.13.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.30.0/go.mod h1:kBi8zwYnR0zrbm8RcuN1o9Fzgpnnn+btVN8uWPMyXAY=
|
||||
github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222 h1:h2JizvZl9aIj6za9S5AyrkU+OzIS4CetQthH/ejO+lg=
|
||||
github.com/weppos/publicsuffix-go v0.30.2-0.20230730094716-a20f9abcc222/go.mod h1:s41lQh6dIsDWIC1OWh7ChWJXLH0zkJ9KHZVqA7vHyuQ=
|
||||
github.com/weppos/publicsuffix-go v0.30.2 h1:Np18yzfMR90jNampWFs7iSh2sw/qCZkhL41/ffyihCU=
|
||||
github.com/weppos/publicsuffix-go v0.30.2/go.mod h1:/hGscit36Yt+wammfBBwdMdxBT8btsTt6KvwO9OvMyM=
|
||||
github.com/weppos/publicsuffix-go/publicsuffix/generator v0.0.0-20220927085643-dc0d00c92642/go.mod h1:GHfoeIdZLdZmLjMlzBftbTDntahTttUMWjxZwQJhULE=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
|
||||
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
|
||||
@@ -1133,15 +1129,15 @@ github.com/zmap/zcertificate v0.0.1/go.mod h1:q0dlN54Jm4NVSSuzisusQY0hqDWvu92C+T
|
||||
github.com/zmap/zcrypto v0.0.0-20201128221613-3719af1573cf/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ=
|
||||
github.com/zmap/zcrypto v0.0.0-20201211161100-e54a5822fb7e/go.mod h1:aPM7r+JOkfL+9qSB4KbYjtoEzJqUK50EXkkJabeNJDQ=
|
||||
github.com/zmap/zcrypto v0.0.0-20230310154051-c8b263fd8300/go.mod h1:mOd4yUMgn2fe2nV9KXsa9AyQBFZGzygVPovsZR+Rl5w=
|
||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c h1:U1b4THKcgOpJ+kILupuznNwPiURtwVW3e9alJvji9+s=
|
||||
github.com/zmap/zcrypto v0.0.0-20231219022726-a1f61fb1661c/go.mod h1:GSDpFDD4TASObxvfZfvpZZ3OWHIUHMlhVWlkOe4ewVk=
|
||||
github.com/zmap/zcrypto v0.0.0-20240512203510-0fef58d9a9db h1:IfONOhyZlf4qPt3ENPU+27mBbPjzTQ+swKpj7MJva9I=
|
||||
github.com/zmap/zcrypto v0.0.0-20240512203510-0fef58d9a9db/go.mod h1:mo/07mo6reDaiz6BzveCuYBWb1d+aX8Pf8Nh+Q57y2g=
|
||||
github.com/zmap/zflags v1.4.0-beta.1.0.20200204220219-9d95409821b6/go.mod h1:HXDUD+uue8yeLHr0eXx1lvY6CvMiHbTKw5nGmA9OUoo=
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706 h1:LaMyYFWQA7kh3ovPfAaFDTKlJu3JGng8khruOtsBVnE=
|
||||
github.com/zmap/zgrab2 v0.1.8-0.20230806160807-97ba87c0e706/go.mod h1:re2kMcs84XHb8Xl6RInt0emoKCuphfmfjHYuteviLHQ=
|
||||
github.com/zmap/zlint/v3 v3.0.0/go.mod h1:paGwFySdHIBEMJ61YjoqT4h7Ge+fdYG4sUQhnTb1lJ8=
|
||||
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
|
||||
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
|
||||
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.opencensus.io v0.20.1/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
go.opencensus.io v0.20.2/go.mod h1:6WKK9ahsWS3RSO+PY9ZHZUfv2irvY6gN279GOPZjmmk=
|
||||
@@ -1189,10 +1185,11 @@ golang.org/x/crypto v0.0.0-20220525230936-793ad666bf5e/go.mod h1:IxCIyHEi3zRg3s0
|
||||
golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58=
|
||||
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
|
||||
golang.org/x/crypto v0.11.0/go.mod h1:xgJhtzW8F9jGdVFWZESrid1U1bjeNy4zgy5cRr/CIio=
|
||||
golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
|
||||
golang.org/x/crypto v0.22.0 h1:g1v0xeRhjcugydODzvb3mEM9SQ0HGp9s/nh3COQ/C30=
|
||||
golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M=
|
||||
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/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=
|
||||
@@ -1203,8 +1200,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a h1:Q8/wZp0KX97QFTc2ywcOE0YRjZPVIx+MXInMzdvQqcA=
|
||||
golang.org/x/exp v0.0.0-20240119083558-1b970713d09a/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
|
||||
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
@@ -1227,8 +1224,8 @@ golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
|
||||
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA=
|
||||
golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -1284,9 +1281,11 @@ golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
|
||||
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
|
||||
golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
|
||||
golang.org/x/net v0.12.0/go.mod h1:zEVYFnQC7m/vmpQFELhcD1EWkZlX69l4oqgmer6hfKA=
|
||||
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
|
||||
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
|
||||
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/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=
|
||||
@@ -1296,8 +1295,8 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ
|
||||
golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc=
|
||||
golang.org/x/oauth2 v0.5.0/go.mod h1:9/XBHVqLaWO3/BRHs5jbpYCnOZVjj5V0ndyaAM7KB4I=
|
||||
golang.org/x/oauth2 v0.6.0/go.mod h1:ycmewcwgD4Rpr3eZJLSB4Kyyljb3qDh40vJ8STE5HKw=
|
||||
golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU=
|
||||
golang.org/x/oauth2 v0.11.0/go.mod h1:LdF7O/8bLR/qWK9DrpXmbHLTouvRHK0SgJl0GmDBchk=
|
||||
golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
|
||||
golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -1311,8 +1310,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.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
|
||||
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M=
|
||||
golang.org/x/sync v0.7.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=
|
||||
@@ -1363,7 +1362,6 @@ golang.org/x/sys v0.0.0-20210228012217-479acdf4ea46/go.mod h1:h1NjWce9XRLGQEsW7w
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
@@ -1378,18 +1376,19 @@ golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
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=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.7.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
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.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
|
||||
golang.org/x/sys v0.19.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/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,10 +1398,11 @@ golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
|
||||
golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
|
||||
golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
|
||||
golang.org/x/term v0.10.0/go.mod h1:lpqdcUyK/oCiQxvxVrppt5ggO2KCZ5QblwqPnfZ6d5o=
|
||||
golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
|
||||
golang.org/x/term v0.19.0 h1:+ThwsDv+tYfnJFhF4L8jITxu1tdTWRTZpdsWgEgjL6Q=
|
||||
golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk=
|
||||
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/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=
|
||||
@@ -1416,9 +1416,11 @@ golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
|
||||
golang.org/x/text v0.11.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
|
||||
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
|
||||
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/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=
|
||||
@@ -1476,8 +1478,8 @@ golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
|
||||
golang.org/x/tools v0.17.0/go.mod h1:xsh6VxdV005rRVaS6SSAf9oiAqljS7UZUacMZ8Bnsps=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg=
|
||||
golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -1506,8 +1508,9 @@ google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c=
|
||||
google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.8 h1:IhEN5q69dyKagZPYMSdIjS2HqprW324FRQZJcGqPAsM=
|
||||
google.golang.org/appengine v1.6.8/go.mod h1:1jJ3jBArFh5pcgW8gCtRJnepW8FzD1V44FJffLiz/Ds=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
|
||||
@@ -15,21 +15,18 @@ http:
|
||||
- type: dsl
|
||||
dsl:
|
||||
- 'method == "GET"'
|
||||
- regex("/(.*?/)([0-9]+)(/.*)?",path)
|
||||
condition: and
|
||||
|
||||
payloads:
|
||||
pathsqli:
|
||||
- "'OR1=1"
|
||||
- '%20OR%20True'
|
||||
|
||||
fuzzing:
|
||||
- part: path
|
||||
type: replace-regex
|
||||
type: postfix
|
||||
mode: single
|
||||
replace-regex: '/(.*?/)([0-9]+)(/.*)?'
|
||||
fuzz:
|
||||
- '/${1}${2}{{pathsqli}}${3}'
|
||||
- '{{pathsqli}}'
|
||||
|
||||
matchers:
|
||||
- type: status
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
id: multi-req
|
||||
|
||||
info:
|
||||
name: Multi-Request
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
ssl:
|
||||
- address: "{{Host}}:{{Port}}"
|
||||
min_version: ssl30
|
||||
max_version: ssl30
|
||||
|
||||
extractors:
|
||||
- type: json
|
||||
json:
|
||||
- " .tls_version"
|
||||
|
||||
- address: "{{Host}}:{{Port}}"
|
||||
min_version: tls10
|
||||
max_version: tls10
|
||||
|
||||
extractors:
|
||||
- type: json
|
||||
json:
|
||||
- " .tls_version"
|
||||
|
||||
- address: "{{Host}}:{{Port}}"
|
||||
min_version: tls11
|
||||
max_version: tls11
|
||||
|
||||
extractors:
|
||||
- type: json
|
||||
json:
|
||||
- " .tls_version"
|
||||
+34
-5
@@ -9,6 +9,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"regexp"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
@@ -17,7 +18,9 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
pdcpauth "github.com/projectdiscovery/utils/auth/pdcp"
|
||||
"github.com/projectdiscovery/utils/env"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
updateutils "github.com/projectdiscovery/utils/update"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
@@ -25,11 +28,18 @@ import (
|
||||
const (
|
||||
uploadEndpoint = "/v1/scans/import"
|
||||
appendEndpoint = "/v1/scans/%s/import"
|
||||
flushTimer = time.Duration(1) * time.Minute
|
||||
MaxChunkSize = 1024 * 1024 * 4 // 4 MB
|
||||
flushTimer = time.Minute
|
||||
MaxChunkSize = 4 * unitutils.Mega // 4 MB
|
||||
xidRe = `^[a-z0-9]{20}$`
|
||||
teamIDHeader = "X-Team-Id"
|
||||
)
|
||||
|
||||
var _ output.Writer = &UploadWriter{}
|
||||
var (
|
||||
xidRegex = regexp.MustCompile(xidRe)
|
||||
_ output.Writer = &UploadWriter{}
|
||||
// teamID if given
|
||||
teamID = env.GetEnvOrDefault("PDCP_TEAM_ID", "")
|
||||
)
|
||||
|
||||
// UploadWriter is a writer that uploads its output to pdcp
|
||||
// server to enable web dashboard and more
|
||||
@@ -41,6 +51,7 @@ type UploadWriter struct {
|
||||
cancel context.CancelFunc
|
||||
done chan struct{}
|
||||
scanID string
|
||||
scanName string
|
||||
counter atomic.Int32
|
||||
}
|
||||
|
||||
@@ -86,8 +97,17 @@ func NewUploadWriter(ctx context.Context, creds *pdcpauth.PDCPCredentials) (*Upl
|
||||
}
|
||||
|
||||
// SetScanID sets the scan id for the upload writer
|
||||
func (u *UploadWriter) SetScanID(id string) {
|
||||
func (u *UploadWriter) SetScanID(id string) error {
|
||||
if !xidRegex.MatchString(id) {
|
||||
return fmt.Errorf("invalid scan id provided")
|
||||
}
|
||||
u.scanID = id
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetScanName sets the scan name for the upload writer
|
||||
func (u *UploadWriter) SetScanName(name string) {
|
||||
u.scanName = name
|
||||
}
|
||||
|
||||
func (u *UploadWriter) autoCommit(ctx context.Context, r *io.PipeReader) {
|
||||
@@ -220,8 +240,17 @@ func (u *UploadWriter) getRequest(bin []byte) (*retryablehttp.Request, error) {
|
||||
return nil, errorutil.NewWithErr(err).Msgf("could not create cloud upload request")
|
||||
}
|
||||
// add pdtm meta params
|
||||
req.URL.RawQuery = updateutils.GetpdtmParams(config.Version)
|
||||
req.URL.Params.Merge(updateutils.GetpdtmParams(config.Version))
|
||||
// if it is upload endpoint also include name if it exists
|
||||
if u.scanName != "" && req.URL.Path == uploadEndpoint {
|
||||
req.URL.Params.Add("name", u.scanName)
|
||||
}
|
||||
req.URL.Update()
|
||||
|
||||
req.Header.Set(pdcpauth.ApiKeyHeaderName, u.creds.APIKey)
|
||||
if teamID != "" {
|
||||
req.Header.Set(teamIDHeader, teamID)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/octet-stream")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
return req, nil
|
||||
|
||||
@@ -19,6 +19,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger/levels"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/utils/vardump"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
@@ -114,7 +115,12 @@ func ParseOptions(options *types.Options) {
|
||||
// Load the resolvers if user asked for them
|
||||
loadResolvers(options)
|
||||
|
||||
err := protocolinit.Init(options)
|
||||
err := protocolstate.Init(options)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not initialize protocol state: %s\n", err)
|
||||
}
|
||||
|
||||
err = protocolinit.Init(options)
|
||||
if err != nil {
|
||||
gologger.Fatal().Msgf("Could not initialize protocols: %s\n", err)
|
||||
}
|
||||
@@ -316,9 +322,6 @@ func createReportingOptions(options *types.Options) (*reporting.Options, error)
|
||||
|
||||
// configureOutput configures the output logging levels to be displayed on the screen
|
||||
func configureOutput(options *types.Options) {
|
||||
// disable standard logger (ref: https://github.com/golang/go/issues/19895)
|
||||
defer logutil.DisableDefaultLogger()
|
||||
|
||||
if options.NoColor {
|
||||
gologger.DefaultLogger.SetFormatter(formatter.NewCLI(true))
|
||||
}
|
||||
@@ -337,6 +340,9 @@ func configureOutput(options *types.Options) {
|
||||
if options.Silent {
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelSilent)
|
||||
}
|
||||
|
||||
// disable standard logger (ref: https://github.com/golang/go/issues/19895)
|
||||
logutil.DisableDefaultLogger()
|
||||
}
|
||||
|
||||
// loadResolvers loads resolvers from both user-provided flags and file
|
||||
|
||||
+47
-32
@@ -15,6 +15,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/internal/pdcp"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/frequency"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/provider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/installer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
||||
@@ -74,21 +75,22 @@ var (
|
||||
|
||||
// Runner is a client for running the enumeration process.
|
||||
type Runner struct {
|
||||
output output.Writer
|
||||
interactsh *interactsh.Client
|
||||
options *types.Options
|
||||
projectFile *projectfile.ProjectFile
|
||||
catalog catalog.Catalog
|
||||
progress progress.Progress
|
||||
colorizer aurora.Aurora
|
||||
issuesClient reporting.Client
|
||||
browser *engine.Browser
|
||||
rateLimiter *ratelimit.Limiter
|
||||
hostErrors hosterrorscache.CacheInterface
|
||||
resumeCfg *types.ResumeCfg
|
||||
pprofServer *http.Server
|
||||
pdcpUploadErrMsg string
|
||||
inputProvider provider.InputProvider
|
||||
output output.Writer
|
||||
interactsh *interactsh.Client
|
||||
options *types.Options
|
||||
projectFile *projectfile.ProjectFile
|
||||
catalog catalog.Catalog
|
||||
progress progress.Progress
|
||||
colorizer aurora.Aurora
|
||||
issuesClient reporting.Client
|
||||
browser *engine.Browser
|
||||
rateLimiter *ratelimit.Limiter
|
||||
hostErrors hosterrorscache.CacheInterface
|
||||
resumeCfg *types.ResumeCfg
|
||||
pprofServer *http.Server
|
||||
pdcpUploadErrMsg string
|
||||
inputProvider provider.InputProvider
|
||||
fuzzFrequencyCache *frequency.Tracker
|
||||
//general purpose temporary directory
|
||||
tmpDir string
|
||||
parser parser.Parser
|
||||
@@ -359,6 +361,10 @@ func (r *Runner) runStandardEnumeration(executerOpts protocols.ExecutorOptions,
|
||||
|
||||
// Close releases all the resources and cleans up
|
||||
func (r *Runner) Close() {
|
||||
// dump hosterrors cache
|
||||
if r.hostErrors != nil {
|
||||
r.hostErrors.Close()
|
||||
}
|
||||
if r.output != nil {
|
||||
r.output.Close()
|
||||
}
|
||||
@@ -414,7 +420,11 @@ func (r *Runner) setupPDCPUpload(writer output.Writer) output.Writer {
|
||||
return writer
|
||||
}
|
||||
if r.options.ScanID != "" {
|
||||
uploadWriter.SetScanID(r.options.ScanID)
|
||||
// ignore and use empty scan id if invalid
|
||||
_ = uploadWriter.SetScanID(r.options.ScanID)
|
||||
}
|
||||
if r.options.ScanName != "" {
|
||||
uploadWriter.SetScanName(r.options.ScanName)
|
||||
}
|
||||
return output.NewMultiWriter(writer, uploadWriter)
|
||||
}
|
||||
@@ -440,27 +450,31 @@ func (r *Runner) RunEnumeration() error {
|
||||
r.options.ExcludedTemplates = append(r.options.ExcludedTemplates, ignoreFile.Files...)
|
||||
}
|
||||
|
||||
fuzzFreqCache := frequency.New(frequency.DefaultMaxTrackCount, r.options.FuzzParamFrequency)
|
||||
r.fuzzFrequencyCache = fuzzFreqCache
|
||||
|
||||
// Create the executor options which will be used throughout the execution
|
||||
// stage by the nuclei engine modules.
|
||||
executorOpts := protocols.ExecutorOptions{
|
||||
Output: r.output,
|
||||
Options: r.options,
|
||||
Progress: r.progress,
|
||||
Catalog: r.catalog,
|
||||
IssuesClient: r.issuesClient,
|
||||
RateLimiter: r.rateLimiter,
|
||||
Interactsh: r.interactsh,
|
||||
ProjectFile: r.projectFile,
|
||||
Browser: r.browser,
|
||||
Colorizer: r.colorizer,
|
||||
ResumeCfg: r.resumeCfg,
|
||||
ExcludeMatchers: excludematchers.New(r.options.ExcludeMatchers),
|
||||
InputHelper: input.NewHelper(),
|
||||
TemporaryDirectory: r.tmpDir,
|
||||
Parser: r.parser,
|
||||
Output: r.output,
|
||||
Options: r.options,
|
||||
Progress: r.progress,
|
||||
Catalog: r.catalog,
|
||||
IssuesClient: r.issuesClient,
|
||||
RateLimiter: r.rateLimiter,
|
||||
Interactsh: r.interactsh,
|
||||
ProjectFile: r.projectFile,
|
||||
Browser: r.browser,
|
||||
Colorizer: r.colorizer,
|
||||
ResumeCfg: r.resumeCfg,
|
||||
ExcludeMatchers: excludematchers.New(r.options.ExcludeMatchers),
|
||||
InputHelper: input.NewHelper(),
|
||||
TemporaryDirectory: r.tmpDir,
|
||||
Parser: r.parser,
|
||||
FuzzParamsFrequency: fuzzFreqCache,
|
||||
}
|
||||
|
||||
if env.GetEnvOrDefault("NUCLEI_ARGS", "") == "req_url_pattern=true" {
|
||||
if config.DefaultConfig.IsDebugArgEnabled(config.DebugExportURLPattern) {
|
||||
// Go StdLib style experimental/debug feature switch
|
||||
executorOpts.ExportReqURLPattern = true
|
||||
}
|
||||
@@ -611,6 +625,7 @@ func (r *Runner) RunEnumeration() error {
|
||||
if executorOpts.InputHelper != nil {
|
||||
_ = executorOpts.InputHelper.Close()
|
||||
}
|
||||
r.fuzzFrequencyCache.Close()
|
||||
|
||||
// todo: error propagation without canonical straight error check is required by cloud?
|
||||
// use safe dereferencing to avoid potential panics in case of previous unchecked errors
|
||||
|
||||
+7
-1
@@ -164,11 +164,17 @@ func WithConcurrency(opts Concurrency) NucleiSDKOptions {
|
||||
}
|
||||
|
||||
// WithGlobalRateLimit sets global rate (i.e all hosts combined) limit options
|
||||
// Deprecated: will be removed in favour of WithGlobalRateLimitCtx in next release
|
||||
func WithGlobalRateLimit(maxTokens int, duration time.Duration) NucleiSDKOptions {
|
||||
return WithGlobalRateLimitCtx(context.Background(), maxTokens, duration)
|
||||
}
|
||||
|
||||
// WithGlobalRateLimitCtx allows setting a global rate limit for the entire engine
|
||||
func WithGlobalRateLimitCtx(ctx context.Context, maxTokens int, duration time.Duration) NucleiSDKOptions {
|
||||
return func(e *NucleiEngine) error {
|
||||
e.opts.RateLimit = maxTokens
|
||||
e.opts.RateLimitDuration = duration
|
||||
e.rateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), e.opts.RateLimitDuration)
|
||||
e.rateLimiter = ratelimit.New(ctx, uint(e.opts.RateLimit), e.opts.RateLimitDuration)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
+21
-10
@@ -26,7 +26,7 @@ type unsafeOptions struct {
|
||||
}
|
||||
|
||||
// createEphemeralObjects creates ephemeral nuclei objects/instances/types
|
||||
func createEphemeralObjects(base *NucleiEngine, opts *types.Options) (*unsafeOptions, error) {
|
||||
func createEphemeralObjects(ctx context.Context, base *NucleiEngine, opts *types.Options) (*unsafeOptions, error) {
|
||||
u := &unsafeOptions{}
|
||||
u.executerOpts = protocols.ExecutorOptions{
|
||||
Output: base.customWriter,
|
||||
@@ -49,9 +49,9 @@ func createEphemeralObjects(base *NucleiEngine, opts *types.Options) (*unsafeOpt
|
||||
opts.RateLimitDuration = time.Second
|
||||
}
|
||||
if opts.RateLimit == 0 && opts.RateLimitDuration == 0 {
|
||||
u.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
u.executerOpts.RateLimiter = ratelimit.NewUnlimited(ctx)
|
||||
} else {
|
||||
u.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(opts.RateLimit), opts.RateLimitDuration)
|
||||
u.executerOpts.RateLimiter = ratelimit.New(ctx, uint(opts.RateLimit), opts.RateLimitDuration)
|
||||
}
|
||||
u.engine = core.New(opts)
|
||||
u.engine.SetExecuterOptions(u.executerOpts)
|
||||
@@ -83,7 +83,7 @@ type ThreadSafeNucleiEngine struct {
|
||||
// NewThreadSafeNucleiEngine creates a new nuclei engine with given options
|
||||
// whose methods are thread-safe and can be used concurrently
|
||||
// Note: Non-thread-safe methods start with Global prefix
|
||||
func NewThreadSafeNucleiEngine(opts ...NucleiSDKOptions) (*ThreadSafeNucleiEngine, error) {
|
||||
func NewThreadSafeNucleiEngineCtx(ctx context.Context, opts ...NucleiSDKOptions) (*ThreadSafeNucleiEngine, error) {
|
||||
// default options
|
||||
e := &NucleiEngine{
|
||||
opts: types.DefaultOptions(),
|
||||
@@ -94,12 +94,17 @@ func NewThreadSafeNucleiEngine(opts ...NucleiSDKOptions) (*ThreadSafeNucleiEngin
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := e.init(); err != nil {
|
||||
if err := e.init(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &ThreadSafeNucleiEngine{eng: e}, nil
|
||||
}
|
||||
|
||||
// Deprecated: use NewThreadSafeNucleiEngineCtx instead
|
||||
func NewThreadSafeNucleiEngine(opts ...NucleiSDKOptions) (*ThreadSafeNucleiEngine, error) {
|
||||
return NewThreadSafeNucleiEngineCtx(context.Background(), opts...)
|
||||
}
|
||||
|
||||
// GlobalLoadAllTemplates loads all templates from nuclei-templates repo
|
||||
// This method will load all templates based on filters given at the time of nuclei engine creation in opts
|
||||
func (e *ThreadSafeNucleiEngine) GlobalLoadAllTemplates() error {
|
||||
@@ -111,11 +116,11 @@ func (e *ThreadSafeNucleiEngine) GlobalResultCallback(callback func(event *outpu
|
||||
e.eng.resultCallbacks = []func(*output.ResultEvent){callback}
|
||||
}
|
||||
|
||||
// ExecuteNucleiWithOpts executes templates on targets and calls callback on each result(only if results are found)
|
||||
// ExecuteNucleiWithOptsCtx executes templates on targets and calls callback on each result(only if results are found)
|
||||
// This method can be called concurrently and it will use some global resources but can be runned parallelly
|
||||
// by invoking this method with different options and targets
|
||||
// Note: Not all options are thread-safe. this method will throw error if you try to use non-thread-safe options
|
||||
func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts ...NucleiSDKOptions) error {
|
||||
func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOptsCtx(ctx context.Context, targets []string, opts ...NucleiSDKOptions) error {
|
||||
baseOpts := *e.eng.opts
|
||||
tmpEngine := &NucleiEngine{opts: &baseOpts, mode: threadSafe}
|
||||
for _, option := range opts {
|
||||
@@ -123,9 +128,9 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts ..
|
||||
return err
|
||||
}
|
||||
}
|
||||
defer tmpEngine.Close()
|
||||
|
||||
// create ephemeral nuclei objects/instances/types using base nuclei engine
|
||||
unsafeOpts, err := createEphemeralObjects(e.eng, tmpEngine.opts)
|
||||
unsafeOpts, err := createEphemeralObjects(ctx, e.eng, tmpEngine.opts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -157,12 +162,18 @@ func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts ..
|
||||
engine := core.New(tmpEngine.opts)
|
||||
engine.SetExecuterOptions(unsafeOpts.executerOpts)
|
||||
|
||||
_ = engine.ExecuteScanWithOpts(context.Background(), store.Templates(), inputProvider, false)
|
||||
_ = engine.ExecuteScanWithOpts(ctx, store.Templates(), inputProvider, false)
|
||||
|
||||
engine.WorkPool().Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteNucleiWithOpts is same as ExecuteNucleiWithOptsCtx but with default context
|
||||
// This is a placeholder and will be deprecated in future major release
|
||||
func (e *ThreadSafeNucleiEngine) ExecuteNucleiWithOpts(targets []string, opts ...NucleiSDKOptions) error {
|
||||
return e.ExecuteNucleiWithOptsCtx(context.Background(), targets, opts...)
|
||||
}
|
||||
|
||||
// Close all resources used by nuclei engine
|
||||
func (e *ThreadSafeNucleiEngine) Close() {
|
||||
e.eng.Close()
|
||||
|
||||
+28
-11
@@ -18,7 +18,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"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/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/headless/engine"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates"
|
||||
@@ -183,8 +183,7 @@ func (e *NucleiEngine) SignTemplate(tmplSigner *signer.TemplateSigner, data []by
|
||||
return buff.Bytes(), err
|
||||
}
|
||||
|
||||
// Close all resources used by nuclei engine
|
||||
func (e *NucleiEngine) Close() {
|
||||
func (e *NucleiEngine) closeInternal() {
|
||||
if e.interactshClient != nil {
|
||||
e.interactshClient.Close()
|
||||
}
|
||||
@@ -206,8 +205,6 @@ func (e *NucleiEngine) Close() {
|
||||
if e.rateLimiter != nil {
|
||||
e.rateLimiter.Stop()
|
||||
}
|
||||
// close global shared resources
|
||||
protocolstate.Close()
|
||||
if e.inputProvider != nil {
|
||||
e.inputProvider.Close()
|
||||
}
|
||||
@@ -219,8 +216,15 @@ func (e *NucleiEngine) Close() {
|
||||
}
|
||||
}
|
||||
|
||||
// ExecuteWithCallback executes templates on targets and calls callback on each result(only if results are found)
|
||||
func (e *NucleiEngine) ExecuteWithCallback(callback ...func(event *output.ResultEvent)) error {
|
||||
// Close all resources used by nuclei engine
|
||||
func (e *NucleiEngine) Close() {
|
||||
e.closeInternal()
|
||||
protocolinit.Close()
|
||||
}
|
||||
|
||||
// ExecuteCallbackWithCtx executes templates on targets and calls callback on each result(only if results are found)
|
||||
// enable matcher-status option if you expect this callback to be called for all results regardless if it matched or not
|
||||
func (e *NucleiEngine) ExecuteCallbackWithCtx(ctx context.Context, callback ...func(event *output.ResultEvent)) error {
|
||||
if !e.templatesLoaded {
|
||||
_ = e.LoadAllTemplates()
|
||||
}
|
||||
@@ -239,21 +243,29 @@ func (e *NucleiEngine) ExecuteWithCallback(callback ...func(event *output.Result
|
||||
}
|
||||
e.resultCallbacks = append(e.resultCallbacks, filtered...)
|
||||
|
||||
_ = e.engine.ExecuteScanWithOpts(context.Background(), e.store.Templates(), e.inputProvider, false)
|
||||
_ = e.engine.ExecuteScanWithOpts(ctx, e.store.Templates(), e.inputProvider, false)
|
||||
defer e.engine.WorkPool().Wait()
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExecuteWithCallback is same as ExecuteCallbackWithCtx but with default context
|
||||
// Note this is deprecated and will be removed in future major release
|
||||
func (e *NucleiEngine) ExecuteWithCallback(callback ...func(event *output.ResultEvent)) error {
|
||||
return e.ExecuteCallbackWithCtx(context.Background(), callback...)
|
||||
}
|
||||
|
||||
// Options return nuclei Type Options
|
||||
func (e *NucleiEngine) Options() *types.Options {
|
||||
return e.opts
|
||||
}
|
||||
|
||||
// Engine returns core Executer of nuclei
|
||||
func (e *NucleiEngine) Engine() *core.Engine {
|
||||
return e.engine
|
||||
}
|
||||
|
||||
// NewNucleiEngine creates a new nuclei engine instance
|
||||
func NewNucleiEngine(options ...NucleiSDKOptions) (*NucleiEngine, error) {
|
||||
// NewNucleiEngineCtx creates a new nuclei engine instance with given context
|
||||
func NewNucleiEngineCtx(ctx context.Context, options ...NucleiSDKOptions) (*NucleiEngine, error) {
|
||||
// default options
|
||||
e := &NucleiEngine{
|
||||
opts: types.DefaultOptions(),
|
||||
@@ -264,8 +276,13 @@ func NewNucleiEngine(options ...NucleiSDKOptions) (*NucleiEngine, error) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if err := e.init(); err != nil {
|
||||
if err := e.init(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return e, nil
|
||||
}
|
||||
|
||||
// Deprecated: use NewNucleiEngineCtx instead
|
||||
func NewNucleiEngine(options ...NucleiSDKOptions) (*NucleiEngine, error) {
|
||||
return NewNucleiEngineCtx(context.Background(), options...)
|
||||
}
|
||||
+23
-17
@@ -35,10 +35,10 @@ import (
|
||||
"github.com/projectdiscovery/ratelimit"
|
||||
)
|
||||
|
||||
var sharedInit sync.Once = sync.Once{}
|
||||
var sharedInit *sync.Once
|
||||
|
||||
// applyRequiredDefaults to options
|
||||
func (e *NucleiEngine) applyRequiredDefaults() {
|
||||
func (e *NucleiEngine) applyRequiredDefaults(ctx context.Context) {
|
||||
mockoutput := testutils.NewMockOutputWriter(e.opts.OmitTemplate)
|
||||
mockoutput.WriteCallback = func(event *output.ResultEvent) {
|
||||
if len(e.resultCallbacks) > 0 {
|
||||
@@ -82,7 +82,7 @@ func (e *NucleiEngine) applyRequiredDefaults() {
|
||||
e.interactshOpts = interactsh.DefaultOptions(e.customWriter, e.rc, e.customProgress)
|
||||
}
|
||||
if e.rateLimiter == nil {
|
||||
e.rateLimiter = ratelimit.New(context.Background(), 150, time.Second)
|
||||
e.rateLimiter = ratelimit.New(ctx, 150, time.Second)
|
||||
}
|
||||
if e.opts.ExcludeTags == nil {
|
||||
e.opts.ExcludeTags = []string{}
|
||||
@@ -95,7 +95,7 @@ func (e *NucleiEngine) applyRequiredDefaults() {
|
||||
}
|
||||
|
||||
// init
|
||||
func (e *NucleiEngine) init() error {
|
||||
func (e *NucleiEngine) init(ctx context.Context) error {
|
||||
if e.opts.Verbose {
|
||||
gologger.DefaultLogger.SetMaxLevel(levels.LevelVerbose)
|
||||
} else if e.opts.Debug {
|
||||
@@ -108,6 +108,19 @@ func (e *NucleiEngine) init() error {
|
||||
return err
|
||||
}
|
||||
|
||||
e.parser = templates.NewParser()
|
||||
|
||||
if sharedInit == nil || protocolstate.ShouldInit() {
|
||||
sharedInit = &sync.Once{}
|
||||
}
|
||||
|
||||
sharedInit.Do(func() {
|
||||
_ = protocolinit.Init(e.opts)
|
||||
})
|
||||
|
||||
e.applyRequiredDefaults(ctx)
|
||||
var err error
|
||||
|
||||
if e.opts.ProxyInternal && types.ProxyURL != "" || types.ProxySocksURL != "" {
|
||||
httpclient, err := httpclientpool.Get(e.opts, &httpclientpool.Configuration{})
|
||||
if err != nil {
|
||||
@@ -116,16 +129,6 @@ func (e *NucleiEngine) init() error {
|
||||
e.httpClient = httpclient
|
||||
}
|
||||
|
||||
e.parser = templates.NewParser()
|
||||
|
||||
sharedInit.Do(func() {
|
||||
_ = protocolstate.Init(e.opts)
|
||||
_ = protocolinit.Init(e.opts)
|
||||
})
|
||||
|
||||
e.applyRequiredDefaults()
|
||||
var err error
|
||||
|
||||
// setup progressbar
|
||||
if e.enableStats {
|
||||
progressInstance, progressErr := progress.NewStatsTicker(e.opts.StatsInterval, e.enableStats, e.opts.StatsJSON, false, e.opts.MetricsPort)
|
||||
@@ -206,9 +209,9 @@ func (e *NucleiEngine) init() error {
|
||||
e.opts.RateLimitDuration = time.Second
|
||||
}
|
||||
if e.opts.RateLimit == 0 && e.opts.RateLimitDuration == 0 {
|
||||
e.executerOpts.RateLimiter = ratelimit.NewUnlimited(context.Background())
|
||||
e.executerOpts.RateLimiter = ratelimit.NewUnlimited(ctx)
|
||||
} else {
|
||||
e.executerOpts.RateLimiter = ratelimit.New(context.Background(), uint(e.opts.RateLimit), e.opts.RateLimitDuration)
|
||||
e.executerOpts.RateLimiter = ratelimit.New(ctx, uint(e.opts.RateLimit), e.opts.RateLimitDuration)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,7 +231,10 @@ func (e *NucleiEngine) init() error {
|
||||
// and also upgrade templates to latest version if available
|
||||
installer.NucleiSDKVersionCheck()
|
||||
|
||||
return e.processUpdateCheckResults()
|
||||
if DefaultConfig.CanCheckForUpdates() {
|
||||
return e.processUpdateCheckResults()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type syncOnce struct {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package sdk_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"os/exec"
|
||||
"testing"
|
||||
@@ -28,8 +29,9 @@ func TestSimpleNuclei(t *testing.T) {
|
||||
time.Sleep(2 * time.Second)
|
||||
goleak.VerifyNone(t, knownLeaks...)
|
||||
}()
|
||||
ne, err := nuclei.NewNucleiEngine(
|
||||
nuclei.WithTemplateFilters(nuclei.TemplateFilters{ProtocolTypes: "dns"}),
|
||||
ne, err := nuclei.NewNucleiEngineCtx(
|
||||
context.TODO(),
|
||||
nuclei.WithTemplateFilters(nuclei.TemplateFilters{ProtocolTypes: "dns"}), // filter dns templates
|
||||
nuclei.EnableStatsWithOpts(nuclei.StatsOptions{JSON: true}),
|
||||
)
|
||||
require.Nil(t, err)
|
||||
@@ -62,7 +64,8 @@ func TestSimpleNucleiRemote(t *testing.T) {
|
||||
time.Sleep(2 * time.Second)
|
||||
goleak.VerifyNone(t, knownLeaks...)
|
||||
}()
|
||||
ne, err := nuclei.NewNucleiEngine(
|
||||
ne, err := nuclei.NewNucleiEngineCtx(
|
||||
context.TODO(),
|
||||
nuclei.WithTemplatesOrWorkflows(
|
||||
nuclei.TemplateSources{
|
||||
RemoteTemplates: []string{"https://cloud.projectdiscovery.io/public/nameserver-fingerprint.yaml"},
|
||||
@@ -100,7 +103,7 @@ func TestThreadSafeNuclei(t *testing.T) {
|
||||
goleak.VerifyNone(t, knownLeaks...)
|
||||
}()
|
||||
// create nuclei engine with options
|
||||
ne, err := nuclei.NewThreadSafeNucleiEngine()
|
||||
ne, err := nuclei.NewThreadSafeNucleiEngineCtx(context.TODO())
|
||||
require.Nil(t, err)
|
||||
|
||||
// scan 1 = run dns templates on scanme.sh
|
||||
|
||||
@@ -457,6 +457,22 @@
|
||||
"title": "part of rule",
|
||||
"description": "Part of request rule to fuzz"
|
||||
},
|
||||
"parts": {
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"query",
|
||||
"header",
|
||||
"path",
|
||||
"body",
|
||||
"cookie",
|
||||
"request"
|
||||
]
|
||||
},
|
||||
"type": "array",
|
||||
"title": "parts of rule",
|
||||
"description": "Part of request rule to fuzz"
|
||||
},
|
||||
"mode": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
|
||||
@@ -31,7 +31,7 @@ const (
|
||||
CLIConfigFileName = "config.yaml"
|
||||
ReportingConfigFilename = "reporting-config.yaml"
|
||||
// Version is the current version of nuclei
|
||||
Version = `v3.2.6`
|
||||
Version = `v3.2.9`
|
||||
// Directory Names of custom templates
|
||||
CustomS3TemplatesDirName = "s3"
|
||||
CustomGitHubTemplatesDirName = "github"
|
||||
@@ -68,3 +68,13 @@ func trimDevIfExists(version string) string {
|
||||
}
|
||||
return version
|
||||
}
|
||||
|
||||
// similar to go pattern of enabling debug related features
|
||||
// we add custom/extra switches for debugging purposes
|
||||
const (
|
||||
// DebugArgHostErrorStats is used to print host error stats
|
||||
// when it is closed
|
||||
DebugArgHostErrorStats = "host-error-stats"
|
||||
// DebugExportReqURLPattern is used to export request URL pattern
|
||||
DebugExportURLPattern = "req-url-pattern"
|
||||
)
|
||||
@@ -46,9 +46,10 @@ type Config struct {
|
||||
LatestNucleiIgnoreHash string `json:"nuclei-latest-ignore-hash,omitempty"`
|
||||
|
||||
// internal / unexported fields
|
||||
disableUpdates bool `json:"-"` // disable updates both version check and template updates
|
||||
homeDir string `json:"-"` // User Home Directory
|
||||
configDir string `json:"-"` // Nuclei Global Config Directory
|
||||
disableUpdates bool `json:"-"` // disable updates both version check and template updates
|
||||
homeDir string `json:"-"` // User Home Directory
|
||||
configDir string `json:"-"` // Nuclei Global Config Directory
|
||||
debugArgs []string `json:"-"` // debug args
|
||||
}
|
||||
|
||||
// IsCustomTemplate determines whether a given template is custom-built or part of the official Nuclei templates.
|
||||
@@ -329,6 +330,46 @@ func (c *Config) copyIgnoreFile() {
|
||||
}
|
||||
}
|
||||
|
||||
// IsDebugArgEnabled checks if debug arg is enabled
|
||||
// this could be a feature specific to debugging like PPROF or printing stats
|
||||
// of max host error etc
|
||||
func (c *Config) IsDebugArgEnabled(arg string) bool {
|
||||
for _, v := range c.debugArgs {
|
||||
if v == arg {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// parseDebugArgs from string
|
||||
func (c *Config) parseDebugArgs(data string) {
|
||||
// use space as seperator instead of commas
|
||||
tmp := strings.Fields(data)
|
||||
for _, v := range tmp {
|
||||
key := v
|
||||
value := ""
|
||||
// if it is key value pair then split it
|
||||
if strings.Contains(v, "=") {
|
||||
parts := strings.SplitN(v, "=", 2)
|
||||
if len(parts) != 2 {
|
||||
continue
|
||||
}
|
||||
key, value = strings.TrimSpace(parts[0]), strings.TrimSpace(parts[1])
|
||||
}
|
||||
if value == "false" || value == "0" {
|
||||
// if false or disabled then skip
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case DebugArgHostErrorStats:
|
||||
c.debugArgs = append(c.debugArgs, DebugArgHostErrorStats)
|
||||
case DebugExportURLPattern:
|
||||
c.debugArgs = append(c.debugArgs, DebugExportURLPattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
// first attempt to migrate all files from old config directory to new config directory
|
||||
goflags.AttemptConfigMigration() // regardless how many times this is called it will only migrate once based on condition
|
||||
@@ -377,6 +418,7 @@ func init() {
|
||||
// and even if it is changed we don't follow it since it is not expected behavior
|
||||
// If custom templates are in default locations only then they are loaded while running nuclei
|
||||
DefaultConfig.SetTemplatesDir(DefaultConfig.TemplatesDirectory)
|
||||
DefaultConfig.parseDebugArgs(env.GetEnvOrDefault("NUCLEI_ARGS", ""))
|
||||
}
|
||||
|
||||
// Add Default Config adds default when .templates-config.json file is not present
|
||||
|
||||
+22
-10
@@ -11,17 +11,25 @@ import (
|
||||
// DiskCatalog is a template catalog helper implementation based on disk
|
||||
type DiskCatalog struct {
|
||||
templatesDirectory string
|
||||
templatesFS fs.FS // TODO: Refactor to use this
|
||||
templatesFS fs.FS // Due to issues with how Go has implemented fs.FS, we'll have to also implement normal os operations, as well. See: https://github.com/golang/go/issues/44279
|
||||
}
|
||||
|
||||
// NewCatalog creates a new Catalog structure using provided input items
|
||||
// using disk based items
|
||||
func NewCatalog(directory string) *DiskCatalog {
|
||||
catalog := &DiskCatalog{templatesDirectory: directory}
|
||||
if directory != "" {
|
||||
catalog.templatesFS = os.DirFS(directory)
|
||||
} else {
|
||||
catalog.templatesFS = os.DirFS(config.DefaultConfig.GetTemplateDir())
|
||||
if directory == "" {
|
||||
catalog.templatesDirectory = config.DefaultConfig.GetTemplateDir()
|
||||
}
|
||||
return catalog
|
||||
}
|
||||
|
||||
// NewFSCatalog creates a new Catalog structure using provided input items
|
||||
// using the fs.FS as its filesystem.
|
||||
func NewFSCatalog(fs fs.FS, directory string) *DiskCatalog {
|
||||
catalog := &DiskCatalog{
|
||||
templatesDirectory: directory,
|
||||
templatesFS: fs,
|
||||
}
|
||||
return catalog
|
||||
}
|
||||
@@ -29,11 +37,15 @@ func NewCatalog(directory string) *DiskCatalog {
|
||||
// OpenFile opens a file and returns an io.ReadCloser to the file.
|
||||
// It is used to read template and payload files based on catalog responses.
|
||||
func (d *DiskCatalog) OpenFile(filename string) (io.ReadCloser, error) {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
if file, errx := os.Open(BackwardsCompatiblePaths(d.templatesDirectory, filename)); errx == nil {
|
||||
return file, nil
|
||||
if d.templatesFS == nil {
|
||||
file, err := os.Open(filename)
|
||||
if err != nil {
|
||||
if file, errx := os.Open(BackwardsCompatiblePaths(d.templatesDirectory, filename)); errx == nil {
|
||||
return file, nil
|
||||
}
|
||||
}
|
||||
return file, err
|
||||
}
|
||||
return file, err
|
||||
|
||||
return d.templatesFS.Open(filename)
|
||||
}
|
||||
+111
-38
@@ -79,17 +79,21 @@ func (c *DiskCatalog) GetTemplatePath(target string) ([]string, error) {
|
||||
}
|
||||
|
||||
// try to handle deprecated template paths
|
||||
absPath := BackwardsCompatiblePaths(c.templatesDirectory, target)
|
||||
if absPath != target && strings.TrimPrefix(absPath, c.templatesDirectory+string(filepath.Separator)) != target {
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.DefaultLogger.Print().Msgf("[%v] requested Template path %s is deprecated, please update to %s\n", aurora.Yellow("WRN").String(), target, absPath)
|
||||
absPath := target
|
||||
if c.templatesFS == nil {
|
||||
absPath = BackwardsCompatiblePaths(c.templatesDirectory, target)
|
||||
if absPath != target && strings.TrimPrefix(absPath, c.templatesDirectory+string(filepath.Separator)) != target {
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.DefaultLogger.Print().Msgf("[%v] requested Template path %s is deprecated, please update to %s\n", aurora.Yellow("WRN").String(), target, absPath)
|
||||
}
|
||||
deprecatedPathsCounter++
|
||||
}
|
||||
deprecatedPathsCounter++
|
||||
}
|
||||
|
||||
absPath, err := c.convertPathToAbsolute(absPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not find template file")
|
||||
var err error
|
||||
absPath, err = c.convertPathToAbsolute(absPath)
|
||||
if err != nil {
|
||||
return nil, errors.Wrapf(err, "could not find template file")
|
||||
}
|
||||
}
|
||||
|
||||
// Template input is either a file or a directory
|
||||
@@ -143,24 +147,60 @@ func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]s
|
||||
if c.templatesDirectory == "" {
|
||||
templateDir = "./"
|
||||
}
|
||||
matches, _ := fs.Glob(os.DirFS(filepath.Join(templateDir, "http")), inputGlob)
|
||||
if len(matches) != 0 {
|
||||
|
||||
if c.templatesFS == nil {
|
||||
matches, _ := fs.Glob(os.DirFS(filepath.Join(templateDir, "http")), inputGlob)
|
||||
if len(matches) != 0 {
|
||||
return matches
|
||||
}
|
||||
|
||||
// condition to support network cve related globs
|
||||
matches, _ = fs.Glob(os.DirFS(filepath.Join(templateDir, "network")), inputGlob)
|
||||
return matches
|
||||
} else {
|
||||
sub, err := fs.Sub(c.templatesFS, filepath.Join(templateDir, "http"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches, _ := fs.Glob(sub, inputGlob)
|
||||
if len(matches) != 0 {
|
||||
return matches
|
||||
}
|
||||
|
||||
// condition to support network cve related globs
|
||||
sub, err = fs.Sub(c.templatesFS, filepath.Join(templateDir, "network"))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
matches, _ = fs.Glob(sub, inputGlob)
|
||||
return matches
|
||||
}
|
||||
// condition to support network cve related globs
|
||||
matches, _ = fs.Glob(os.DirFS(filepath.Join(templateDir, "network")), inputGlob)
|
||||
return matches
|
||||
}
|
||||
|
||||
var matched []string
|
||||
matches, err := fs.Glob(c.templatesFS, relPath)
|
||||
if len(matches) != 0 {
|
||||
matched = append(matched, matches...)
|
||||
var matches []string
|
||||
if c.templatesFS == nil {
|
||||
var err error
|
||||
matches, err = filepath.Glob(relPath)
|
||||
if len(matches) != 0 {
|
||||
matched = append(matched, matches...)
|
||||
} else {
|
||||
matched = append(matched, OldPathsResolver(relPath)...)
|
||||
}
|
||||
if err != nil && len(matched) == 0 {
|
||||
return nil, errors.Errorf("wildcard found, but unable to glob: %s\n", err)
|
||||
}
|
||||
} else {
|
||||
matched = append(matched, OldPathsResolver(relPath)...)
|
||||
}
|
||||
if err != nil && len(matched) == 0 {
|
||||
return nil, errors.Errorf("wildcard found, but unable to glob: %s\n", err)
|
||||
var err error
|
||||
matches, err = fs.Glob(c.templatesFS, relPath)
|
||||
if len(matches) != 0 {
|
||||
matched = append(matched, matches...)
|
||||
} else {
|
||||
matched = append(matched, OldPathsResolver(relPath)...)
|
||||
}
|
||||
if err != nil && len(matched) == 0 {
|
||||
return nil, errors.Errorf("wildcard found, but unable to glob: %s\n", err)
|
||||
}
|
||||
}
|
||||
results := make([]string, 0, len(matches))
|
||||
for _, match := range matches {
|
||||
@@ -175,11 +215,23 @@ func (c *DiskCatalog) findGlobPathMatches(absPath string, processed map[string]s
|
||||
// findFileMatches finds if a path is an absolute file. If the path
|
||||
// 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) {
|
||||
info, err := os.Stat(absPath)
|
||||
if c.templatesFS != nil {
|
||||
absPath = strings.TrimPrefix(absPath, "/")
|
||||
}
|
||||
var info fs.File
|
||||
if c.templatesFS == nil {
|
||||
info, err = os.Open(absPath)
|
||||
} else {
|
||||
info, err = c.templatesFS.Open(absPath)
|
||||
}
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if !info.Mode().IsRegular() {
|
||||
stat, err := info.Stat()
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
if !stat.Mode().IsRegular() {
|
||||
return "", false, nil
|
||||
}
|
||||
if _, ok := processed[absPath]; !ok {
|
||||
@@ -192,22 +244,43 @@ func (c *DiskCatalog) findFileMatches(absPath string, processed map[string]struc
|
||||
// findDirectoryMatches finds matches for templates from a directory
|
||||
func (c *DiskCatalog) findDirectoryMatches(absPath string, processed map[string]struct{}) ([]string, error) {
|
||||
var results []string
|
||||
err := filepath.WalkDir(
|
||||
absPath,
|
||||
func(path string, d fs.DirEntry, err error) error {
|
||||
// continue on errors
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
|
||||
if _, ok := processed[path]; !ok {
|
||||
results = append(results, path)
|
||||
processed[path] = struct{}{}
|
||||
var err error
|
||||
if c.templatesFS == nil {
|
||||
err = filepath.WalkDir(
|
||||
absPath,
|
||||
func(path string, d fs.DirEntry, err error) error {
|
||||
// continue on errors
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
|
||||
if _, ok := processed[path]; !ok {
|
||||
results = append(results, path)
|
||||
processed[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
} else {
|
||||
err = fs.WalkDir(
|
||||
c.templatesFS,
|
||||
absPath,
|
||||
func(path string, d fs.DirEntry, err error) error {
|
||||
// continue on errors
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
if !d.IsDir() && config.GetTemplateFormatFromExt(path) != config.Unknown {
|
||||
if _, ok := processed[path]; !ok {
|
||||
results = append(results, path)
|
||||
processed[path] = struct{}{}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/logrusorgru/aurora"
|
||||
"github.com/pkg/errors"
|
||||
@@ -23,6 +24,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
@@ -425,10 +427,10 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
store.logErroredTemplates(errs)
|
||||
templatePathMap := store.pathFilter.Match(includedTemplates)
|
||||
|
||||
loadedTemplates := make([]*templates.Template, 0, len(templatePathMap))
|
||||
loadedTemplates := sliceutil.NewSyncSlice[*templates.Template]()
|
||||
|
||||
loadTemplate := func(tmpl *templates.Template) {
|
||||
loadedTemplates = append(loadedTemplates, tmpl)
|
||||
loadedTemplates.Append(tmpl)
|
||||
// increment signed/unsigned counters
|
||||
if tmpl.Verified {
|
||||
if tmpl.TemplateVerifier == "" {
|
||||
@@ -441,80 +443,89 @@ func (store *Store) LoadTemplatesWithTags(templatesList, tags []string) []*templ
|
||||
}
|
||||
}
|
||||
|
||||
var wgLoadTemplates sync.WaitGroup
|
||||
|
||||
for templatePath := range templatePathMap {
|
||||
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, tags, store.config.Catalog)
|
||||
if loaded || store.pathFilter.MatchIncluded(templatePath) {
|
||||
parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
|
||||
if err != nil {
|
||||
// exclude templates not compatible with offline matching from total runtime warning stats
|
||||
if !errors.Is(err, templates.ErrIncompatibleWithOfflineMatching) {
|
||||
stats.Increment(templates.RuntimeWarningsStats)
|
||||
}
|
||||
gologger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)
|
||||
} else if parsed != nil {
|
||||
if !parsed.Verified && store.config.ExecutorOptions.Options.DisableUnsignedTemplates {
|
||||
// skip unverified templates when prompted to
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
continue
|
||||
}
|
||||
// if template has request signature like aws then only signed and verified templates are allowed
|
||||
if parsed.UsesRequestSignature() && !parsed.Verified {
|
||||
stats.Increment(templates.SkippedRequestSignatureStats)
|
||||
continue
|
||||
}
|
||||
// DAST only templates
|
||||
if store.config.ExecutorOptions.Options.DAST {
|
||||
// check if the template is a DAST template
|
||||
if parsed.IsFuzzing() {
|
||||
wgLoadTemplates.Add(1)
|
||||
go func(templatePath string) {
|
||||
defer wgLoadTemplates.Done()
|
||||
|
||||
loaded, err := store.config.ExecutorOptions.Parser.LoadTemplate(templatePath, store.tagFilter, tags, store.config.Catalog)
|
||||
if loaded || store.pathFilter.MatchIncluded(templatePath) {
|
||||
parsed, err := templates.Parse(templatePath, store.preprocessor, store.config.ExecutorOptions)
|
||||
if err != nil {
|
||||
// exclude templates not compatible with offline matching from total runtime warning stats
|
||||
if !errors.Is(err, templates.ErrIncompatibleWithOfflineMatching) {
|
||||
stats.Increment(templates.RuntimeWarningsStats)
|
||||
}
|
||||
gologger.Warning().Msgf("Could not parse template %s: %s\n", templatePath, err)
|
||||
} else if parsed != nil {
|
||||
if !parsed.Verified && store.config.ExecutorOptions.Options.DisableUnsignedTemplates {
|
||||
// skip unverified templates when prompted to
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
return
|
||||
}
|
||||
// if template has request signature like aws then only signed and verified templates are allowed
|
||||
if parsed.UsesRequestSignature() && !parsed.Verified {
|
||||
stats.Increment(templates.SkippedRequestSignatureStats)
|
||||
return
|
||||
}
|
||||
// DAST only templates
|
||||
if store.config.ExecutorOptions.Options.DAST {
|
||||
// check if the template is a DAST template
|
||||
if parsed.IsFuzzing() {
|
||||
loadTemplate(parsed)
|
||||
}
|
||||
} else if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
// donot include headless template in final list if headless flag is not set
|
||||
stats.Increment(templates.ExcludedHeadlessTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
// donot include 'Code' protocol custom template in final list if code flag is not set
|
||||
stats.Increment(templates.ExcludedCodeTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
||||
// donot include unverified 'Code' protocol custom template in final list
|
||||
stats.Increment(templates.SkippedCodeTmplTamperedStats)
|
||||
// these will be skipped so increment skip counter
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||
stats.Increment(templates.ExludedDastTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] -dast flag is required for DAST template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else {
|
||||
loadTemplate(parsed)
|
||||
}
|
||||
} else if len(parsed.RequestsHeadless) > 0 && !store.config.ExecutorOptions.Options.Headless {
|
||||
// donot include headless template in final list if headless flag is not set
|
||||
stats.Increment(templates.ExcludedHeadlessTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Headless flag is required for headless template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !store.config.ExecutorOptions.Options.EnableCodeTemplates {
|
||||
// donot include 'Code' protocol custom template in final list if code flag is not set
|
||||
stats.Increment(templates.ExcludedCodeTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Code flag is required for code protocol template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if len(parsed.RequestsCode) > 0 && !parsed.Verified && len(parsed.Workflows) == 0 {
|
||||
// donot include unverified 'Code' protocol custom template in final list
|
||||
stats.Increment(templates.SkippedCodeTmplTamperedStats)
|
||||
// these will be skipped so increment skip counter
|
||||
stats.Increment(templates.SkippedUnsignedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] Tampered/Unsigned template at %v.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else if parsed.IsFuzzing() && !store.config.ExecutorOptions.Options.DAST {
|
||||
stats.Increment(templates.ExludedDastTmplStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] -dast flag is required for DAST template '%s'.\n", aurora.Yellow("WRN").String(), templatePath)
|
||||
}
|
||||
} else {
|
||||
loadTemplate(parsed)
|
||||
}
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
||||
stats.Increment(templates.TemplatesExcludedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
|
||||
if err != nil {
|
||||
if strings.Contains(err.Error(), templates.ErrExcluded.Error()) {
|
||||
stats.Increment(templates.TemplatesExcludedStats)
|
||||
if config.DefaultConfig.LogAllEvents {
|
||||
gologger.Print().Msgf("[%v] %v\n", aurora.Yellow("WRN").String(), err.Error())
|
||||
}
|
||||
return
|
||||
}
|
||||
continue
|
||||
gologger.Warning().Msg(err.Error())
|
||||
}
|
||||
gologger.Warning().Msg(err.Error())
|
||||
}
|
||||
}(templatePath)
|
||||
}
|
||||
|
||||
sort.SliceStable(loadedTemplates, func(i, j int) bool {
|
||||
return loadedTemplates[i].Path < loadedTemplates[j].Path
|
||||
wgLoadTemplates.Wait()
|
||||
|
||||
sort.SliceStable(loadedTemplates.Slice, func(i, j int) bool {
|
||||
return loadedTemplates.Slice[i].Path < loadedTemplates.Slice[j].Path
|
||||
})
|
||||
|
||||
return loadedTemplates
|
||||
return loadedTemplates.Slice
|
||||
}
|
||||
|
||||
// IsHTTPBasedProtocolUsed returns true if http/headless protocol is being used for
|
||||
|
||||
@@ -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(scannedValue.ID()) {
|
||||
if e.executerOpts.HostErrorsCache != nil && e.executerOpts.HostErrorsCache.Check(contextargs.NewWithMetaInput(ctx, scannedValue)) {
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -162,7 +162,7 @@ func (e *Engine) executeTemplatesOnTarget(ctx context.Context, alltemplates []*t
|
||||
// wp is workpool that contains different waitgroups for
|
||||
// headless and non-headless templates
|
||||
// global waitgroup should not be used here
|
||||
wp := e.WorkPool()
|
||||
wp := e.GetWorkPool()
|
||||
|
||||
for _, tpl := range alltemplates {
|
||||
select {
|
||||
|
||||
@@ -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.MetaInput.ID(), err)
|
||||
w.Options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
}
|
||||
if len(template.Executers) == 1 {
|
||||
mainErr = err
|
||||
|
||||
+38
-12
@@ -2,10 +2,12 @@ package component
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/dataformat"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
// Path is a component for a request Path
|
||||
@@ -31,13 +33,18 @@ func (q *Path) Name() string {
|
||||
// parsed component
|
||||
func (q *Path) Parse(req *retryablehttp.Request) (bool, error) {
|
||||
q.req = req
|
||||
q.value = NewValue(req.URL.Path)
|
||||
q.value = NewValue("")
|
||||
|
||||
parsed, err := dataformat.Get(dataformat.RawDataFormat).Decode(q.value.String())
|
||||
if err != nil {
|
||||
return false, err
|
||||
splitted := strings.Split(req.URL.Path, "/")
|
||||
values := make(map[string]interface{})
|
||||
for i := range splitted {
|
||||
pathTillNow := strings.Join(splitted[:i+1], "/")
|
||||
if pathTillNow == "" {
|
||||
continue
|
||||
}
|
||||
values[strconv.Itoa(i)] = pathTillNow
|
||||
}
|
||||
q.value.SetParsed(parsed, dataformat.RawDataFormat)
|
||||
q.value.SetParsed(dataformat.KVMap(values), "")
|
||||
return true, nil
|
||||
}
|
||||
|
||||
@@ -56,7 +63,8 @@ func (q *Path) Iterate(callback func(key string, value interface{}) error) (err
|
||||
// SetValue sets a value in the component
|
||||
// for a key
|
||||
func (q *Path) SetValue(key string, value string) error {
|
||||
if !q.value.SetParsedValue(key, value) {
|
||||
escaped := urlutil.ParamEncode(value)
|
||||
if !q.value.SetParsedValue(key, escaped) {
|
||||
return ErrSetValue
|
||||
}
|
||||
return nil
|
||||
@@ -73,13 +81,31 @@ func (q *Path) Delete(key string) error {
|
||||
// Rebuild returns a new request with the
|
||||
// component rebuilt
|
||||
func (q *Path) Rebuild() (*retryablehttp.Request, error) {
|
||||
encoded, err := q.value.Encode()
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not encode query")
|
||||
originalValues := make(map[string]interface{})
|
||||
splitted := strings.Split(q.req.URL.Path, "/")
|
||||
for i := range splitted {
|
||||
pathTillNow := strings.Join(splitted[:i+1], "/")
|
||||
if pathTillNow == "" {
|
||||
continue
|
||||
}
|
||||
originalValues[strconv.Itoa(i)] = pathTillNow
|
||||
}
|
||||
|
||||
originalPath := q.req.URL.Path
|
||||
lengthSplitted := len(q.value.parsed.Map)
|
||||
for i := lengthSplitted; i > 0; i-- {
|
||||
key := strconv.Itoa(i)
|
||||
original := originalValues[key].(string)
|
||||
new := q.value.parsed.Map[key].(string)
|
||||
originalPath = strings.Replace(originalPath, original, new, 1)
|
||||
}
|
||||
|
||||
rebuiltPath := originalPath
|
||||
|
||||
// Clone the request and update the path
|
||||
cloned := q.req.Clone(context.Background())
|
||||
if err := cloned.UpdateRelPath(encoded, true); err != nil {
|
||||
cloned.URL.RawPath = encoded
|
||||
if err := cloned.UpdateRelPath(rebuiltPath, true); err != nil {
|
||||
cloned.URL.RawPath = rebuiltPath
|
||||
}
|
||||
return cloned, nil
|
||||
}
|
||||
|
||||
@@ -28,10 +28,10 @@ func TestURLComponent(t *testing.T) {
|
||||
return nil
|
||||
})
|
||||
|
||||
require.Equal(t, []string{"value"}, keys, "unexpected keys")
|
||||
require.Equal(t, []string{"1"}, keys, "unexpected keys")
|
||||
require.Equal(t, []string{"/testpath"}, values, "unexpected values")
|
||||
|
||||
err = urlComponent.SetValue("value", "/newpath")
|
||||
err = urlComponent.SetValue("1", "/newpath")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -40,7 +40,41 @@ func TestURLComponent(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
require.Equal(t, "/newpath", rebuilt.URL.Path, "unexpected URL path")
|
||||
require.Equal(t, "https://example.com/newpath", rebuilt.URL.String(), "unexpected full URL")
|
||||
}
|
||||
|
||||
func TestURLComponent_NestedPaths(t *testing.T) {
|
||||
path := NewPath()
|
||||
req, err := retryablehttp.NewRequest(http.MethodGet, "https://example.com/user/753/profile", nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
found, err := path.Parse(req)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !found {
|
||||
t.Fatal("expected path to be found")
|
||||
}
|
||||
|
||||
isSet := false
|
||||
|
||||
_ = path.Iterate(func(key string, value interface{}) error {
|
||||
if !isSet && value.(string) == "/user/753" {
|
||||
isSet = true
|
||||
if setErr := path.SetValue(key, "/user/753'"); setErr != nil {
|
||||
t.Fatal(setErr)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
|
||||
newReq, err := path.Rebuild()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if newReq.URL.Path != "/user/753'/profile" {
|
||||
t.Fatal("expected path to be modified")
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,7 @@ func (q *Query) Iterate(callback func(key string, value interface{}) error) (err
|
||||
// SetValue sets a value in the component
|
||||
// for a key
|
||||
func (q *Query) SetValue(key string, value string) error {
|
||||
// Is this safe?
|
||||
if !q.value.SetParsedValue(key, value) {
|
||||
return ErrSetValue
|
||||
}
|
||||
|
||||
+23
-4
@@ -1,6 +1,7 @@
|
||||
package fuzz
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"regexp"
|
||||
@@ -15,6 +16,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
@@ -45,6 +47,8 @@ type ExecuteRuleInput struct {
|
||||
Values map[string]interface{}
|
||||
// BaseRequest is the base http request for fuzzing rule
|
||||
BaseRequest *retryablehttp.Request
|
||||
// DisplayFuzzPoints is a flag to display fuzz points
|
||||
DisplayFuzzPoints bool
|
||||
}
|
||||
|
||||
// GeneratedRequest is a single generated request for rule
|
||||
@@ -76,8 +80,9 @@ func (rule *Rule) Execute(input *ExecuteRuleInput) (err error) {
|
||||
|
||||
var finalComponentList []component.Component
|
||||
// match rule part with component name
|
||||
displayDebugFuzzPoints := make(map[string]map[string]string)
|
||||
for _, componentName := range component.Components {
|
||||
if rule.partType != requestPartType && rule.Part != componentName {
|
||||
if !(rule.Part == componentName || sliceutil.Contains(rule.Parts, componentName) || rule.partType == requestPartType) {
|
||||
continue
|
||||
}
|
||||
component := component.New(componentName)
|
||||
@@ -89,12 +94,25 @@ func (rule *Rule) Execute(input *ExecuteRuleInput) (err error) {
|
||||
if !discovered {
|
||||
continue
|
||||
}
|
||||
|
||||
// check rule applicable on this component
|
||||
if !rule.checkRuleApplicableOnComponent(component) {
|
||||
continue
|
||||
}
|
||||
// Debugging display for fuzz points
|
||||
if input.DisplayFuzzPoints {
|
||||
displayDebugFuzzPoints[componentName] = make(map[string]string)
|
||||
_ = component.Iterate(func(key string, value interface{}) error {
|
||||
displayDebugFuzzPoints[componentName][key] = fmt.Sprintf("%v", value)
|
||||
return nil
|
||||
})
|
||||
}
|
||||
finalComponentList = append(finalComponentList, component)
|
||||
}
|
||||
if len(displayDebugFuzzPoints) > 0 {
|
||||
marshalled, _ := json.MarshalIndent(displayDebugFuzzPoints, "", " ")
|
||||
gologger.Info().Msgf("[%s] Fuzz points for %s [%s]\n%s\n", rule.options.TemplateID, input.Input.MetaInput.Input, input.BaseRequest.Method, string(marshalled))
|
||||
}
|
||||
|
||||
if len(finalComponentList) == 0 {
|
||||
return ErrRuleNotApplicable.Msgf("no component matched on this rule")
|
||||
@@ -225,7 +243,7 @@ func (rule *Rule) executeRuleValues(input *ExecuteRuleInput, ruleComponent compo
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if gotErr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, ""); gotErr != nil {
|
||||
if gotErr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", ""); gotErr != nil {
|
||||
return gotErr
|
||||
}
|
||||
}
|
||||
@@ -261,8 +279,9 @@ func (rule *Rule) Compile(generator *generators.PayloadGenerator, options *proto
|
||||
} else {
|
||||
rule.partType = valueType
|
||||
}
|
||||
} else {
|
||||
rule.partType = queryPartType
|
||||
}
|
||||
if rule.Part == "" && len(rule.Parts) == 0 {
|
||||
return errors.Errorf("no part specified for rule")
|
||||
}
|
||||
|
||||
if rule.Type != "" {
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
package frequency
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/url"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bluele/gcache"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
)
|
||||
|
||||
// Tracker implements a frequency tracker for a given input
|
||||
// which is used to determine uninteresting input parameters
|
||||
// which are not that interesting from fuzzing perspective for a template
|
||||
// and target combination.
|
||||
//
|
||||
// This is used to reduce the number of requests made during fuzzing
|
||||
// for parameters that are less likely to give results for a rule.
|
||||
type Tracker struct {
|
||||
frequencies gcache.Cache
|
||||
paramOccurenceThreshold int
|
||||
|
||||
isDebug bool
|
||||
}
|
||||
|
||||
const (
|
||||
DefaultMaxTrackCount = 10000
|
||||
DefaultParamOccurenceThreshold = 10
|
||||
)
|
||||
|
||||
type cacheItem struct {
|
||||
errors atomic.Int32
|
||||
sync.Once
|
||||
}
|
||||
|
||||
// New creates a new frequency tracker with a given maximum
|
||||
// number of params to track in LRU fashion with a max error threshold
|
||||
func New(maxTrackCount, paramOccurenceThreshold int) *Tracker {
|
||||
gc := gcache.New(maxTrackCount).ARC().Build()
|
||||
|
||||
var isDebug bool
|
||||
if os.Getenv("FREQ_DEBUG") != "" {
|
||||
isDebug = true
|
||||
}
|
||||
return &Tracker{
|
||||
isDebug: isDebug,
|
||||
frequencies: gc,
|
||||
paramOccurenceThreshold: paramOccurenceThreshold,
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Tracker) Close() {
|
||||
t.frequencies.Purge()
|
||||
}
|
||||
|
||||
// MarkParameter marks a parameter as frequently occuring once.
|
||||
//
|
||||
// The logic requires a parameter to be marked as frequently occuring
|
||||
// multiple times before it's considered as frequently occuring.
|
||||
func (t *Tracker) MarkParameter(parameter, target, template string) {
|
||||
normalizedTarget := normalizeTarget(target)
|
||||
key := getFrequencyKey(parameter, normalizedTarget, template)
|
||||
|
||||
if t.isDebug {
|
||||
gologger.Verbose().Msgf("[%s] Marking %s as found uninteresting", template, key)
|
||||
}
|
||||
|
||||
existingCacheItem, err := t.frequencies.GetIFPresent(key)
|
||||
if err != nil || existingCacheItem == nil {
|
||||
newItem := &cacheItem{errors: atomic.Int32{}}
|
||||
newItem.errors.Store(1)
|
||||
_ = t.frequencies.Set(key, newItem)
|
||||
return
|
||||
}
|
||||
existingCacheItemValue := existingCacheItem.(*cacheItem)
|
||||
existingCacheItemValue.errors.Add(1)
|
||||
|
||||
_ = t.frequencies.Set(key, existingCacheItemValue)
|
||||
}
|
||||
|
||||
// IsParameterFrequent checks if a parameter is frequently occuring
|
||||
// in the input with no much results.
|
||||
func (t *Tracker) IsParameterFrequent(parameter, target, template string) bool {
|
||||
normalizedTarget := normalizeTarget(target)
|
||||
key := getFrequencyKey(parameter, normalizedTarget, template)
|
||||
|
||||
if t.isDebug {
|
||||
gologger.Verbose().Msgf("[%s] Checking if %s is frequently found uninteresting", template, key)
|
||||
}
|
||||
|
||||
existingCacheItem, err := t.frequencies.GetIFPresent(key)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
existingCacheItemValue := existingCacheItem.(*cacheItem)
|
||||
|
||||
if existingCacheItemValue.errors.Load() >= int32(t.paramOccurenceThreshold) {
|
||||
existingCacheItemValue.Do(func() {
|
||||
gologger.Verbose().Msgf("[%s] Skipped %s from parameter for %s as found uninteresting %d times", template, parameter, target, existingCacheItemValue.errors.Load())
|
||||
})
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// UnmarkParameter unmarks a parameter as frequently occuring. This carries
|
||||
// more weight and resets the frequency counter for the parameter causing
|
||||
// it to be checked again. This is done when results are found.
|
||||
func (t *Tracker) UnmarkParameter(parameter, target, template string) {
|
||||
normalizedTarget := normalizeTarget(target)
|
||||
key := getFrequencyKey(parameter, normalizedTarget, template)
|
||||
|
||||
if t.isDebug {
|
||||
gologger.Verbose().Msgf("[%s] Unmarking %s as frequently found uninteresting", template, key)
|
||||
}
|
||||
|
||||
_ = t.frequencies.Remove(key)
|
||||
}
|
||||
|
||||
func getFrequencyKey(parameter, target, template string) string {
|
||||
var sb strings.Builder
|
||||
sb.WriteString(target)
|
||||
sb.WriteString(":")
|
||||
sb.WriteString(template)
|
||||
sb.WriteString(":")
|
||||
sb.WriteString(parameter)
|
||||
str := sb.String()
|
||||
return str
|
||||
}
|
||||
|
||||
func normalizeTarget(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)
|
||||
}
|
||||
finalValue = hostname
|
||||
}
|
||||
}
|
||||
return finalValue
|
||||
}
|
||||
+17
-2
@@ -24,12 +24,27 @@ type Rule struct {
|
||||
ruleType ruleType
|
||||
// description: |
|
||||
// Part is the part of request to fuzz.
|
||||
//
|
||||
// query fuzzes the query part of url. More parts will be added later.
|
||||
// values:
|
||||
// - "query"
|
||||
// - "header"
|
||||
// - "path"
|
||||
// - "body"
|
||||
// - "cookie"
|
||||
// - "request"
|
||||
Part string `yaml:"part,omitempty" json:"part,omitempty" jsonschema:"title=part of rule,description=Part of request rule to fuzz,enum=query,enum=header,enum=path,enum=body,enum=cookie,enum=request"`
|
||||
partType partType
|
||||
// description: |
|
||||
// Parts is the list of parts to fuzz. If multiple parts need to be
|
||||
// defined while excluding some, this should be used instead of singular part.
|
||||
// values:
|
||||
// - "query"
|
||||
// - "header"
|
||||
// - "path"
|
||||
// - "body"
|
||||
// - "cookie"
|
||||
// - "request"
|
||||
Parts []string `yaml:"parts,omitempty" json:"parts,omitempty" jsonschema:"title=parts of rule,description=Part of request rule to fuzz,enum=query,enum=header,enum=path,enum=body,enum=cookie,enum=request"`
|
||||
|
||||
// description: |
|
||||
// Mode is the mode of fuzzing to perform.
|
||||
//
|
||||
|
||||
@@ -7,7 +7,9 @@ import (
|
||||
)
|
||||
|
||||
func TestRuleMatchKeyOrValue(t *testing.T) {
|
||||
rule := &Rule{}
|
||||
rule := &Rule{
|
||||
Part: "query",
|
||||
}
|
||||
err := rule.Compile(nil, nil)
|
||||
require.NoError(t, err, "could not compile rule")
|
||||
|
||||
@@ -15,7 +17,7 @@ func TestRuleMatchKeyOrValue(t *testing.T) {
|
||||
require.True(t, result, "could not get correct result")
|
||||
|
||||
t.Run("key", func(t *testing.T) {
|
||||
rule := &Rule{Keys: []string{"url"}}
|
||||
rule := &Rule{Keys: []string{"url"}, Part: "query"}
|
||||
err := rule.Compile(nil, nil)
|
||||
require.NoError(t, err, "could not compile rule")
|
||||
|
||||
@@ -25,7 +27,7 @@ func TestRuleMatchKeyOrValue(t *testing.T) {
|
||||
require.False(t, result, "could not get correct result")
|
||||
})
|
||||
t.Run("value", func(t *testing.T) {
|
||||
rule := &Rule{ValuesRegex: []string{`https?:\/\/?([-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b)*(\/[\/\d\w\.-]*)*(?:[\?])*(.+)*`}}
|
||||
rule := &Rule{ValuesRegex: []string{`https?:\/\/?([-a-zA-Z0-9@:%._\+~#=]{2,256}\.[a-z]{2,6}\b)*(\/[\/\d\w\.-]*)*(?:[\?])*(.+)*`}, Part: "query"}
|
||||
err := rule.Compile(nil, nil)
|
||||
require.NoError(t, err, "could not compile rule")
|
||||
|
||||
|
||||
+23
-5
@@ -2,6 +2,7 @@ package fuzz
|
||||
|
||||
import (
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/component"
|
||||
@@ -9,6 +10,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
)
|
||||
|
||||
// executePartRule executes part rules based on type
|
||||
@@ -18,7 +20,7 @@ func (rule *Rule) executePartRule(input *ExecuteRuleInput, payload ValueOrKeyVal
|
||||
|
||||
// checkRuleApplicableOnComponent checks if a rule is applicable on given component
|
||||
func (rule *Rule) checkRuleApplicableOnComponent(component component.Component) bool {
|
||||
if rule.Part != component.Name() {
|
||||
if rule.Part != component.Name() && !sliceutil.Contains(rule.Parts, component.Name()) && rule.partType != requestPartType {
|
||||
return false
|
||||
}
|
||||
foundAny := false
|
||||
@@ -68,7 +70,7 @@ func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadS
|
||||
return err
|
||||
}
|
||||
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, valueStr); qerr != nil {
|
||||
return qerr
|
||||
}
|
||||
// fmt.Printf("executed with value: %s\n", evaluated)
|
||||
@@ -90,7 +92,7 @@ func (rule *Rule) executePartComponentOnValues(input *ExecuteRuleInput, payloadS
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, ""); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, "", ""); qerr != nil {
|
||||
err = qerr
|
||||
return err
|
||||
}
|
||||
@@ -125,7 +127,7 @@ func (rule *Rule) executePartComponentOnKV(input *ExecuteRuleInput, payload Valu
|
||||
return err
|
||||
}
|
||||
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key); qerr != nil {
|
||||
if qerr := rule.execWithInput(input, req, input.InteractURLs, ruleComponent, key, value); qerr != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -144,7 +146,23 @@ func (rule *Rule) executePartComponentOnKV(input *ExecuteRuleInput, payload Valu
|
||||
}
|
||||
|
||||
// execWithInput executes a rule with input via callback
|
||||
func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.Request, interactURLs []string, component component.Component, parameter string) error {
|
||||
func (rule *Rule) execWithInput(input *ExecuteRuleInput, httpReq *retryablehttp.Request, interactURLs []string, component component.Component, parameter, parameterValue string) error {
|
||||
// If the parameter is a number, replace it with the parameter value
|
||||
// or if the parameter is empty and the parameter value is not empty
|
||||
// replace it with the parameter value
|
||||
if _, err := strconv.Atoi(parameter); err == nil || (parameter == "" && parameterValue != "") {
|
||||
parameter = parameterValue
|
||||
}
|
||||
// If the parameter is frequent, skip it if the option is enabled
|
||||
if rule.options.FuzzParamsFrequency != nil {
|
||||
if rule.options.FuzzParamsFrequency.IsParameterFrequent(
|
||||
parameter,
|
||||
httpReq.URL.String(),
|
||||
rule.options.TemplateID,
|
||||
) {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
request := GeneratedRequest{
|
||||
Request: httpReq,
|
||||
InteractURLs: interactURLs,
|
||||
|
||||
@@ -123,11 +123,15 @@ func (rr *RequestResponse) UnmarshalJSON(data []byte) error {
|
||||
if err := json.Unmarshal(data, &m); err != nil {
|
||||
return err
|
||||
}
|
||||
urlStr, ok := m["url"]
|
||||
urlStrRaw, ok := m["url"]
|
||||
if !ok {
|
||||
return fmt.Errorf("missing url in request response")
|
||||
}
|
||||
parsed, err := urlutil.ParseAbsoluteURL(string(urlStr), false)
|
||||
var urlStr string
|
||||
if err := json.Unmarshal(urlStrRaw, &urlStr); err != nil {
|
||||
return err
|
||||
}
|
||||
parsed, err := urlutil.ParseAbsoluteURL(urlStr, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -65,3 +65,26 @@ func TestParseHttpRequest(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnmarshalJSON(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rawJSONStr string
|
||||
expectedURLStr string
|
||||
}{
|
||||
{"basic url", `{"url": "example.com"}`, "example.com"},
|
||||
{"basic url with scheme", `{"url": "http://example.com"}`, "http://example.com"},
|
||||
}
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
var rr RequestResponse
|
||||
err := rr.UnmarshalJSON([]byte(tc.rawJSONStr))
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if tc.expectedURLStr != "" {
|
||||
require.Equal(t, rr.URL.String(), tc.expectedURLStr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -87,6 +87,7 @@ func doVersionCheck(isSDK bool) error {
|
||||
// and according our config we have idle connections which are shown as leaked by goleak in tests
|
||||
// i.e we close all idle connections after our use and it doesn't affect any other part of the code
|
||||
defer retryableHttpClient.HTTPClient.CloseIdleConnections()
|
||||
|
||||
resp, err := retryableHttpClient.Get(pdtmNucleiVersionEndpoint + "?" + getpdtmParams(isSDK))
|
||||
if err != nil {
|
||||
return err
|
||||
|
||||
@@ -10,9 +10,15 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
contextutil "github.com/projectdiscovery/utils/context"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrJSExecDeadline is the error returned when alloted time for script execution exceeds
|
||||
ErrJSExecDeadline = errkit.New("js engine execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build()
|
||||
)
|
||||
|
||||
// Compiler provides a runtime to execute goja runtime
|
||||
// based javascript scripts efficiently while also
|
||||
// providing them access to custom modules defined in libs/.
|
||||
@@ -107,7 +113,7 @@ func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs,
|
||||
}
|
||||
|
||||
// execute with context and timeout
|
||||
ctx, cancel := context.WithTimeout(opts.Context, time.Duration(opts.Timeout)*time.Second)
|
||||
ctx, cancel := context.WithTimeoutCause(opts.Context, time.Duration(opts.Timeout)*time.Second, ErrJSExecDeadline)
|
||||
defer cancel()
|
||||
// execute the script
|
||||
results, err := contextutil.ExecFuncWithTwoReturns(ctx, func() (val goja.Value, err error) {
|
||||
@@ -119,6 +125,9 @@ func (c *Compiler) ExecuteWithOptions(program *goja.Program, args *ExecuteArgs,
|
||||
return ExecuteProgram(program, args, opts)
|
||||
})
|
||||
if err != nil {
|
||||
if val, ok := err.(*goja.Exception); ok {
|
||||
err = val.Unwrap()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
var res ExecuteResult
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package compiler
|
||||
|
||||
import "github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
)
|
||||
|
||||
// jsprotocolInit
|
||||
|
||||
@@ -9,6 +11,7 @@ var (
|
||||
JsProtocolTimeout = 10
|
||||
PoolingJsVmConcurrency = 100
|
||||
NonPoolingVMConcurrency = 20
|
||||
JsTimeoutMultiplier = 1.5
|
||||
)
|
||||
|
||||
// Init initializes the javascript protocol
|
||||
@@ -21,7 +24,9 @@ func Init(opts *types.Options) error {
|
||||
// 100 is reasonable default
|
||||
opts.JsConcurrency = 100
|
||||
}
|
||||
JsProtocolTimeout = opts.Timeout
|
||||
// we have dialer timeout set to 10s so js needs to be at least
|
||||
// 15s to return the actual error if not it will be a dialer timeout
|
||||
JsProtocolTimeout = int(float64(opts.Timeout) * JsTimeoutMultiplier)
|
||||
PoolingJsVmConcurrency = opts.JsConcurrency
|
||||
PoolingJsVmConcurrency -= NonPoolingVMConcurrency
|
||||
return nil
|
||||
|
||||
@@ -5,7 +5,7 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
_ "github.com/lib/pq"
|
||||
_ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap"
|
||||
|
||||
utils "github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
|
||||
|
||||
|
||||
@@ -9,10 +9,11 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/go-pg/pg"
|
||||
_ "github.com/lib/pq"
|
||||
"github.com/praetorian-inc/fingerprintx/pkg/plugins"
|
||||
postgres "github.com/praetorian-inc/fingerprintx/pkg/plugins/services/postgresql"
|
||||
utils "github.com/projectdiscovery/nuclei/v3/pkg/js/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap"
|
||||
_ "github.com/projectdiscovery/nuclei/v3/pkg/js/utils/pgwrap"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
)
|
||||
|
||||
@@ -100,10 +101,11 @@ func executeQuery(host string, port int, username string, password string, dbNam
|
||||
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||
|
||||
connStr := fmt.Sprintf("postgres://%s:%s@%s/%s?sslmode=disable", username, password, target, dbName)
|
||||
db, err := sql.Open("postgres", connStr)
|
||||
db, err := sql.Open(pgwrap.PGWrapDriver, connStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer db.Close()
|
||||
|
||||
rows, err := db.Query(query)
|
||||
if err != nil {
|
||||
@@ -143,12 +145,19 @@ func connect(host string, port int, username string, password string, dbName str
|
||||
|
||||
target := net.JoinHostPort(host, fmt.Sprintf("%d", port))
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
db := pg.Connect(&pg.Options{
|
||||
Addr: target,
|
||||
User: username,
|
||||
Password: password,
|
||||
Database: dbName,
|
||||
})
|
||||
Addr: target,
|
||||
User: username,
|
||||
Password: password,
|
||||
Database: dbName,
|
||||
Dialer: func(network, addr string) (net.Conn, error) {
|
||||
return protocolstate.Dialer.Dial(context.Background(), network, addr)
|
||||
},
|
||||
IdleCheckFrequency: -1,
|
||||
}).WithContext(ctx).WithTimeout(10 * time.Second)
|
||||
defer db.Close()
|
||||
|
||||
_, err := db.Exec("select 1")
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
package pgwrap
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"net"
|
||||
"time"
|
||||
|
||||
"github.com/lib/pq"
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
)
|
||||
|
||||
const (
|
||||
PGWrapDriver = "pgwrap"
|
||||
)
|
||||
|
||||
type pgDial struct {
|
||||
fd *fastdialer.Dialer
|
||||
}
|
||||
|
||||
func (p *pgDial) Dial(network, address string) (net.Conn, error) {
|
||||
return p.fd.Dial(context.TODO(), network, address)
|
||||
}
|
||||
|
||||
func (p *pgDial) DialTimeout(network, address string, timeout time.Duration) (net.Conn, error) {
|
||||
ctx, cancel := context.WithTimeoutCause(context.Background(), timeout, fastdialer.ErrDialTimeout)
|
||||
defer cancel()
|
||||
return p.fd.Dial(ctx, network, address)
|
||||
}
|
||||
|
||||
func (p *pgDial) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
return p.fd.Dial(ctx, network, address)
|
||||
}
|
||||
|
||||
// Unfortunately lib/pq does not provide easy to customize or
|
||||
// replace dialer so we need to hijack it by wrapping it in our own
|
||||
// driver and register it as postgres driver
|
||||
|
||||
// PgDriver is the Postgres database driver.
|
||||
type PgDriver struct{}
|
||||
|
||||
// Open opens a new connection to the database. name is a connection string.
|
||||
// Most users should only use it through database/sql package from the standard
|
||||
// library.
|
||||
func (d PgDriver) Open(name string) (driver.Conn, error) {
|
||||
return pq.DialOpen(&pgDial{fd: protocolstate.Dialer}, name)
|
||||
}
|
||||
|
||||
func init() {
|
||||
sql.Register(PGWrapDriver, &PgDriver{})
|
||||
}
|
||||
@@ -26,6 +26,14 @@ func (severities *Severities) Set(values string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (severities Severities) MarshalYAML() (interface{}, error) {
|
||||
var stringSeverities = make([]string, 0, len(severities))
|
||||
for _, severity := range severities {
|
||||
stringSeverities = append(stringSeverities, severity.String())
|
||||
}
|
||||
return stringSeverities, nil
|
||||
}
|
||||
|
||||
func (severities *Severities) UnmarshalYAML(unmarshal func(interface{}) error) error {
|
||||
var stringSliceValue stringslice.StringSlice
|
||||
if err := unmarshal(&stringSliceValue); err != nil {
|
||||
|
||||
@@ -74,3 +74,12 @@ func TestMarshalJSON(t *testing.T) {
|
||||
}
|
||||
require.Equal(t, "[\"low\",\"medium\"]", string(data), "could not marshal json")
|
||||
}
|
||||
|
||||
func TestSeveritiesMarshalYaml(t *testing.T) {
|
||||
unmarshalled := Severities{Low, Medium}
|
||||
marshalled, err := yaml.Marshal(unmarshalled)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
require.Equal(t, "- low\n- medium\n", string(marshalled), "could not marshal yaml")
|
||||
}
|
||||
+74
-9
@@ -4,6 +4,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
@@ -27,9 +28,13 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
protocolUtils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
osutils "github.com/projectdiscovery/utils/os"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
// Writer is an interface which writes output to somewhere for nuclei events.
|
||||
@@ -299,10 +304,14 @@ func (w *StandardWriter) Write(event *ResultEvent) error {
|
||||
|
||||
// JSONLogRequest is a trace/error log request written to file
|
||||
type JSONLogRequest struct {
|
||||
Template string `json:"template"`
|
||||
Input string `json:"input"`
|
||||
Error string `json:"error"`
|
||||
Type string `json:"type"`
|
||||
Template string `json:"template"`
|
||||
Type string `json:"type"`
|
||||
Input string `json:"input"`
|
||||
Timestamp *time.Time `json:"timestamp,omitempty"`
|
||||
Address string `json:"address"`
|
||||
Error string `json:"error"`
|
||||
Kind string `json:"kind,omitempty"`
|
||||
Attrs interface{} `json:"attrs,omitempty"`
|
||||
}
|
||||
|
||||
// Request writes a log the requests trace log
|
||||
@@ -315,12 +324,47 @@ func (w *StandardWriter) Request(templatePath, input, requestType string, reques
|
||||
Input: input,
|
||||
Type: requestType,
|
||||
}
|
||||
if unwrappedErr := utils.UnwrapError(requestErr); unwrappedErr != nil {
|
||||
request.Error = unwrappedErr.Error()
|
||||
if w.timestamp {
|
||||
ts := time.Now()
|
||||
request.Timestamp = &ts
|
||||
}
|
||||
parsed, _ := urlutil.ParseAbsoluteURL(input, false)
|
||||
if parsed != nil {
|
||||
request.Address = parsed.Hostname()
|
||||
port := parsed.Port()
|
||||
if port == "" {
|
||||
switch parsed.Scheme {
|
||||
case urlutil.HTTP:
|
||||
port = "80"
|
||||
case urlutil.HTTPS:
|
||||
port = "443"
|
||||
}
|
||||
}
|
||||
request.Address += ":" + port
|
||||
}
|
||||
errX := errkit.FromError(requestErr)
|
||||
if errX == nil {
|
||||
request.Error = "none"
|
||||
} else {
|
||||
request.Error = "none"
|
||||
request.Kind = errkit.ErrKindUnknown.String()
|
||||
var cause error
|
||||
if len(errX.Errors()) > 1 {
|
||||
cause = errX.Errors()[0]
|
||||
}
|
||||
if cause == nil {
|
||||
cause = errX
|
||||
}
|
||||
cause = tryParseCause(cause)
|
||||
request.Error = cause.Error()
|
||||
request.Kind = errkit.GetErrorKind(requestErr, nucleierr.ErrTemplateLogic).String()
|
||||
if len(errX.Attrs()) > 0 {
|
||||
request.Attrs = slog.GroupValue(errX.Attrs()...)
|
||||
}
|
||||
}
|
||||
// check if address slog attr is avaiable in error if set use it
|
||||
if val := errkit.GetAttrValue(requestErr, "address"); val.Any() != nil {
|
||||
request.Address = val.String()
|
||||
}
|
||||
|
||||
data, err := jsoniter.Marshal(request)
|
||||
if err != nil {
|
||||
return
|
||||
@@ -411,7 +455,7 @@ func (w *StandardWriter) WriteFailure(wrappedEvent *InternalWrappedEvent) error
|
||||
return w.Write(data)
|
||||
}
|
||||
|
||||
var maxTemplateFileSizeForEncoding = 1024 * 1024
|
||||
var maxTemplateFileSizeForEncoding = unitutils.Mega
|
||||
|
||||
func (w *StandardWriter) encodeTemplate(templatePath string) string {
|
||||
data, err := os.ReadFile(templatePath)
|
||||
@@ -452,3 +496,24 @@ func (w *StandardWriter) WriteStoreDebugData(host, templateID, eventType string,
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// tryParseCause 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) error {
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
msg := err.Error()
|
||||
if strings.HasPrefix(msg, "ReadStatusLine:") {
|
||||
// last index is actual error (from rawhttp)
|
||||
parts := strings.Split(msg, ":")
|
||||
return errkit.New(strings.TrimSpace(parts[len(parts)-1]))
|
||||
}
|
||||
if strings.Contains(msg, "read ") {
|
||||
// same here
|
||||
parts := strings.Split(msg, ":")
|
||||
return errkit.New(strings.TrimSpace(parts[len(parts)-1]))
|
||||
}
|
||||
return err
|
||||
}
|
||||
@@ -30,7 +30,7 @@ func TestStandardWriterRequest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
w.Request("path", "input", "http", nil)
|
||||
|
||||
require.Equal(t, `{"template":"path","input":"input","error":"none","type":"http"}`, traceWriter.String())
|
||||
require.Equal(t, `{"template":"path","type":"http","input":"input","address":"input:","error":"none"}`, traceWriter.String())
|
||||
require.Empty(t, errorWriter.String())
|
||||
})
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestStandardWriterRequest(t *testing.T) {
|
||||
fmt.Errorf("GET https://example.com/tcpconfig.html/tcpconfig.html giving up after 2 attempts: %w", errors.New("context deadline exceeded (Client.Timeout exceeded while awaiting headers)")),
|
||||
)
|
||||
|
||||
require.Equal(t, `{"template":"misconfiguration/tcpconfig.yaml","input":"https://example.com/tcpconfig.html","error":"context deadline exceeded (Client.Timeout exceeded while awaiting headers)","type":"http"}`, errorWriter.String())
|
||||
require.Equal(t, `{"template":"misconfiguration/tcpconfig.yaml","type":"http","input":"https://example.com/tcpconfig.html","address":"example.com:443","error":"context deadline exceeded (Client.Timeout exceeded while awaiting headers)","kind":"unknown-error"}`, errorWriter.String())
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
contextutil "github.com/projectdiscovery/utils/context"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
)
|
||||
|
||||
@@ -41,7 +42,10 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
// pythonEnvRegexCompiled is the compiled regex for python environment variables
|
||||
pythonEnvRegexCompiled = regexp.MustCompile(pythonEnvRegex)
|
||||
// ErrCodeExecutionDeadline is the error returned when alloted time for script execution exceeds
|
||||
ErrCodeExecutionDeadline = errkit.New("code execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build()
|
||||
)
|
||||
|
||||
// Request is a request for the SSL protocol
|
||||
@@ -214,7 +218,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
}
|
||||
}
|
||||
|
||||
ctx, cancel := context.WithTimeout(context.Background(), time.Duration(timeout)*time.Second)
|
||||
ctx, cancel := context.WithTimeoutCause(input.Context(), time.Duration(timeout)*time.Second, ErrCodeExecutionDeadline)
|
||||
defer cancel()
|
||||
// Note: we use contextutil despite the fact that gozero accepts context as argument
|
||||
gOutput, err := contextutil.ExecFuncWithTwoReturns(ctx, func() (*gozerotypes.Result, error) {
|
||||
|
||||
@@ -32,13 +32,14 @@ import (
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
syncutil "github.com/projectdiscovery/utils/sync"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
wappalyzer "github.com/projectdiscovery/wappalyzergo"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
mappingFilename = "wappalyzer-mapping.yml"
|
||||
maxDefaultBody = 4 * 1024 * 1024 // 4MB
|
||||
maxDefaultBody = 4 * unitutils.Mega
|
||||
)
|
||||
|
||||
// Options contains configuration options for automatic scan service
|
||||
|
||||
@@ -37,6 +37,13 @@ func New(ctx context.Context) *Context {
|
||||
return NewWithInput(ctx, "")
|
||||
}
|
||||
|
||||
// NewWithMetaInput creates a new contextargs instance with meta input
|
||||
func NewWithMetaInput(ctx context.Context, input *MetaInput) *Context {
|
||||
n := New(ctx)
|
||||
n.MetaInput = input
|
||||
return n
|
||||
}
|
||||
|
||||
// Create a new contextargs instance with input string
|
||||
func NewWithInput(ctx context.Context, input string) *Context {
|
||||
jar, err := cookiejar.New(nil)
|
||||
@@ -174,3 +181,20 @@ func (ctx *Context) Clone() *Context {
|
||||
}
|
||||
return newCtx
|
||||
}
|
||||
|
||||
// GetCopyIfHostOutdated returns a new contextargs if the host is outdated
|
||||
func GetCopyIfHostOutdated(ctx *Context, url string) *Context {
|
||||
if ctx.MetaInput.Input == "" {
|
||||
newctx := ctx.Clone()
|
||||
newctx.MetaInput.Input = url
|
||||
return newctx
|
||||
}
|
||||
orig, _ := urlutil.Parse(ctx.MetaInput.Input)
|
||||
newURL, _ := urlutil.Parse(url)
|
||||
if orig != nil && newURL != nil && orig.Host != newURL.Host {
|
||||
newCtx := ctx.Clone()
|
||||
newCtx.MetaInput.Input = newURL.Host
|
||||
return newCtx
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
@@ -4,11 +4,13 @@ import (
|
||||
"bytes"
|
||||
"crypto/md5"
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input/types"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
"github.com/segmentio/ksuid"
|
||||
)
|
||||
|
||||
// MetaInput represents a target with metadata (TODO: replace with https://github.com/projectdiscovery/metainput)
|
||||
@@ -47,6 +49,56 @@ func (metaInput *MetaInput) URL() (*urlutil.URL, error) {
|
||||
return instance, nil
|
||||
}
|
||||
|
||||
// Port returns the port of the target
|
||||
// if port is not present then empty string is returned
|
||||
func (metaInput *MetaInput) Port() string {
|
||||
target, err := urlutil.ParseAbsoluteURL(metaInput.Input, false)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return target.Port()
|
||||
}
|
||||
|
||||
// Address return the remote address of target
|
||||
// Note: it does not resolve the domain to ip
|
||||
// it is meant to be used by hosterrorsCache if invalid metainput
|
||||
// is provided then it uses a random ksuid as hostname to avoid skipping valid targets
|
||||
func (metaInput *MetaInput) Address() string {
|
||||
var hostname, port string
|
||||
target, err := urlutil.ParseAbsoluteURL(metaInput.Target(), false)
|
||||
if err != nil {
|
||||
if metaInput.CustomIP == "" {
|
||||
// since this is used in hosterrorscache we add a random id
|
||||
// which will never be used to avoid skipping valid targets
|
||||
hostname = fmt.Sprintf("invalid-%s", ksuid.New().String())
|
||||
}
|
||||
} else {
|
||||
hostname = target.Hostname()
|
||||
port = target.Port()
|
||||
if port == "" {
|
||||
switch target.Scheme {
|
||||
case urlutil.HTTP:
|
||||
port = "80"
|
||||
case urlutil.HTTPS:
|
||||
port = "443"
|
||||
default:
|
||||
port = "80"
|
||||
}
|
||||
}
|
||||
}
|
||||
if metaInput.CustomIP != "" {
|
||||
hostname = metaInput.CustomIP
|
||||
}
|
||||
if port == "" {
|
||||
if strings.HasPrefix(hostname, "http://") {
|
||||
port = "80"
|
||||
} else if strings.HasPrefix(hostname, "https://") {
|
||||
port = "443"
|
||||
}
|
||||
}
|
||||
return net.JoinHostPort(hostname, port)
|
||||
}
|
||||
|
||||
// ID returns a unique id/hash for metainput
|
||||
func (metaInput *MetaInput) ID() string {
|
||||
if metaInput.CustomIP != "" {
|
||||
|
||||
@@ -25,8 +25,19 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
|
||||
|
||||
// Resolve payload paths if they are files.
|
||||
payloadsFinal := make(map[string]interface{})
|
||||
for name, payload := range payloads {
|
||||
payloadsFinal[name] = payload
|
||||
for payloadName, v := range payloads {
|
||||
switch value := v.(type) {
|
||||
case map[interface{}]interface{}:
|
||||
values, err := parsePayloadsWithAggression(payloadName, value, opts.FuzzAggressionLevel)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "could not parse payloads with aggression")
|
||||
}
|
||||
for k, v := range values {
|
||||
payloadsFinal[k] = v
|
||||
}
|
||||
default:
|
||||
payloadsFinal[payloadName] = v
|
||||
}
|
||||
}
|
||||
|
||||
generator := &PayloadGenerator{catalog: catalog, options: opts}
|
||||
@@ -57,6 +68,60 @@ func New(payloads map[string]interface{}, attackType AttackType, templatePath st
|
||||
return generator, nil
|
||||
}
|
||||
|
||||
type aggressionLevelToPayloads struct {
|
||||
Low []interface{}
|
||||
Medium []interface{}
|
||||
High []interface{}
|
||||
}
|
||||
|
||||
// parsePayloadsWithAggression parses the payloads with the aggression level
|
||||
//
|
||||
// Three agression are supported -
|
||||
// - low
|
||||
// - medium
|
||||
// - high
|
||||
//
|
||||
// low is the default level. If medium is specified, all templates from
|
||||
// low and medium are executed. Similarly with high, including all templates
|
||||
// from low, medium, high.
|
||||
func parsePayloadsWithAggression(name string, v map[interface{}]interface{}, agression string) (map[string]interface{}, error) {
|
||||
payloadsLevels := &aggressionLevelToPayloads{}
|
||||
|
||||
for k, v := range v {
|
||||
if _, ok := v.([]interface{}); !ok {
|
||||
return nil, errors.Errorf("only lists are supported for aggression levels payloads")
|
||||
}
|
||||
var ok bool
|
||||
switch k {
|
||||
case "low":
|
||||
payloadsLevels.Low, ok = v.([]interface{})
|
||||
case "medium":
|
||||
payloadsLevels.Medium, ok = v.([]interface{})
|
||||
case "high":
|
||||
payloadsLevels.High, ok = v.([]interface{})
|
||||
default:
|
||||
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
|
||||
}
|
||||
if !ok {
|
||||
return nil, errors.Errorf("invalid aggression level %s specified for %s", k, name)
|
||||
}
|
||||
}
|
||||
|
||||
payloads := make(map[string]interface{})
|
||||
switch agression {
|
||||
case "low":
|
||||
payloads[name] = payloadsLevels.Low
|
||||
case "medium":
|
||||
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
|
||||
case "high":
|
||||
payloads[name] = append(payloadsLevels.Low, payloadsLevels.Medium...)
|
||||
payloads[name] = append(payloads[name].([]interface{}), payloadsLevels.High...)
|
||||
default:
|
||||
return nil, errors.Errorf("invalid aggression level %s specified for %s", agression, name)
|
||||
}
|
||||
return payloads, nil
|
||||
}
|
||||
|
||||
// Iterator is a single instance of an iterator for a generator structure
|
||||
type Iterator struct {
|
||||
Type AttackType
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package generators
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/disk"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
@@ -90,3 +92,49 @@ func getOptions(allowLocalFileAccess bool) *types.Options {
|
||||
opts.AllowLocalFileAccess = allowLocalFileAccess
|
||||
return opts
|
||||
}
|
||||
|
||||
func TestParsePayloadsWithAggression(t *testing.T) {
|
||||
testPayload := `linux_path:
|
||||
low:
|
||||
- /etc/passwd
|
||||
medium:
|
||||
- ../etc/passwd
|
||||
- ../../etc/passwd
|
||||
high:
|
||||
- ../../../etc/passwd
|
||||
- ../../../../etc/passwd
|
||||
- ../../../../../etc/passwd`
|
||||
|
||||
var payloads map[string]interface{}
|
||||
err := yaml.NewDecoder(strings.NewReader(testPayload)).Decode(&payloads)
|
||||
require.Nil(t, err, "could not unmarshal yaml")
|
||||
|
||||
aggressionsToValues := map[string][]string{
|
||||
"low": {
|
||||
"/etc/passwd",
|
||||
},
|
||||
"medium": {
|
||||
"/etc/passwd",
|
||||
"../etc/passwd",
|
||||
"../../etc/passwd",
|
||||
},
|
||||
"high": {
|
||||
"/etc/passwd",
|
||||
"../etc/passwd",
|
||||
"../../etc/passwd",
|
||||
"../../../etc/passwd",
|
||||
"../../../../etc/passwd",
|
||||
"../../../../../etc/passwd",
|
||||
},
|
||||
}
|
||||
|
||||
for k, v := range payloads {
|
||||
for aggression, values := range aggressionsToValues {
|
||||
parsed, err := parsePayloadsWithAggression(k, v.(map[interface{}]interface{}), aggression)
|
||||
require.Nil(t, err, "could not parse payloads with aggression")
|
||||
|
||||
gotValues := parsed[k].([]interface{})
|
||||
require.Equal(t, len(values), len(gotValues), "could not get correct number of values")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -8,19 +8,27 @@ import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"github.com/bluele/gcache"
|
||||
"github.com/Mzack9999/gcache"
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
// 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(value string) bool // return true if the host should be skipped
|
||||
MarkFailed(value string, err error) // record a failure (and cause) for the host
|
||||
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
|
||||
}
|
||||
|
||||
var (
|
||||
_ CacheInterface = (*Cache)(nil)
|
||||
)
|
||||
|
||||
// Cache is a cache for host based errors. It allows skipping
|
||||
// certain hosts based on an error threshold.
|
||||
//
|
||||
@@ -29,20 +37,22 @@ type CacheInterface interface {
|
||||
type Cache struct {
|
||||
MaxHostError int
|
||||
verbose bool
|
||||
failedTargets gcache.Cache
|
||||
failedTargets gcache.Cache[string, *cacheItem]
|
||||
TrackError []string
|
||||
}
|
||||
|
||||
type cacheItem struct {
|
||||
errors atomic.Int32
|
||||
sync.Once
|
||||
errors atomic.Int32
|
||||
isPermanentErr bool
|
||||
cause error // optional cause
|
||||
}
|
||||
|
||||
const DefaultMaxHostsCount = 10000
|
||||
|
||||
// New returns a new host max errors cache
|
||||
func New(maxHostError, maxHostsCount int, trackError []string) *Cache {
|
||||
gc := gcache.New(maxHostsCount).
|
||||
gc := gcache.New[string, *cacheItem](maxHostsCount).
|
||||
ARC().
|
||||
Build()
|
||||
return &Cache{failedTargets: gc, MaxHostError: maxHostError, TrackError: trackError}
|
||||
@@ -55,6 +65,12 @@ func (c *Cache) SetVerbose(verbose bool) {
|
||||
|
||||
// Close closes the host errors cache
|
||||
func (c *Cache) Close() {
|
||||
if config.DefaultConfig.IsDebugArgEnabled(config.DebugArgHostErrorStats) {
|
||||
items := c.failedTargets.GetALL(false)
|
||||
for k, val := range items {
|
||||
gologger.Info().Label("MaxHostErrorStats").Msgf("Host: %s, Errors: %d", k, val.errors.Load())
|
||||
}
|
||||
}
|
||||
c.failedTargets.Purge()
|
||||
}
|
||||
|
||||
@@ -88,18 +104,22 @@ func (c *Cache) normalizeCacheValue(value string) string {
|
||||
// - URL: https?:// type
|
||||
// - Host:port type
|
||||
// - host type
|
||||
func (c *Cache) Check(value string) bool {
|
||||
finalValue := c.normalizeCacheValue(value)
|
||||
func (c *Cache) Check(ctx *contextargs.Context) bool {
|
||||
finalValue := c.GetKeyFromContext(ctx, nil)
|
||||
|
||||
existingCacheItem, err := c.failedTargets.GetIFPresent(finalValue)
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
existingCacheItemValue := existingCacheItem.(*cacheItem)
|
||||
if existingCacheItem.isPermanentErr {
|
||||
// skipping permanent errors is expected so verbose instead of info
|
||||
gologger.Verbose().Msgf("Skipped %s from target list as found unresponsive permanently: %s", finalValue, existingCacheItem.cause)
|
||||
return true
|
||||
}
|
||||
|
||||
if existingCacheItemValue.errors.Load() >= int32(c.MaxHostError) {
|
||||
existingCacheItemValue.Do(func() {
|
||||
gologger.Info().Msgf("Skipped %s from target list as found unresponsive %d times", finalValue, existingCacheItemValue.errors.Load())
|
||||
if existingCacheItem.errors.Load() >= int32(c.MaxHostError) {
|
||||
existingCacheItem.Do(func() {
|
||||
gologger.Info().Msgf("Skipped %s from target list as found unresponsive %d times", finalValue, existingCacheItem.errors.Load())
|
||||
})
|
||||
return true
|
||||
}
|
||||
@@ -107,36 +127,101 @@ func (c *Cache) Check(value string) bool {
|
||||
}
|
||||
|
||||
// MarkFailed marks a host as failed previously
|
||||
func (c *Cache) MarkFailed(value string, err error) {
|
||||
func (c *Cache) MarkFailed(ctx *contextargs.Context, err error) {
|
||||
if !c.checkError(err) {
|
||||
return
|
||||
}
|
||||
finalValue := c.normalizeCacheValue(value)
|
||||
finalValue := c.GetKeyFromContext(ctx, err)
|
||||
existingCacheItem, err := c.failedTargets.GetIFPresent(finalValue)
|
||||
if err != nil || existingCacheItem == nil {
|
||||
newItem := &cacheItem{errors: atomic.Int32{}}
|
||||
newItem.errors.Store(1)
|
||||
if errkit.IsKind(err, errkit.ErrKindNetworkPermanent) {
|
||||
// skip this address altogether
|
||||
// permanent errors are always permanent hence this is created once
|
||||
// and never updated so no need to synchronize
|
||||
newItem.isPermanentErr = true
|
||||
newItem.cause = err
|
||||
}
|
||||
_ = c.failedTargets.Set(finalValue, newItem)
|
||||
return
|
||||
}
|
||||
existingCacheItemValue := existingCacheItem.(*cacheItem)
|
||||
existingCacheItemValue.errors.Add(1)
|
||||
_ = c.failedTargets.Set(finalValue, existingCacheItemValue)
|
||||
existingCacheItem.errors.Add(1)
|
||||
_ = c.failedTargets.Set(finalValue, existingCacheItem)
|
||||
}
|
||||
|
||||
var reCheckError = regexp.MustCompile(`(no address found for host|Client\.Timeout exceeded while awaiting headers|could not resolve host|connection refused|connection reset by peer|i/o timeout|could not connect to any address found for host|timeout awaiting response headers)`)
|
||||
// GetKeyFromContext returns the key for the cache from the context
|
||||
func (c *Cache) GetKeyFromContext(ctx *contextargs.Context, err error) string {
|
||||
// Note:
|
||||
// ideally any changes made to remote addr in template like {{Hostname}}:81 etc
|
||||
// should be reflected in contextargs but it is not yet reflected in some cases
|
||||
// and needs refactor of ScanContext + ContextArgs to achieve that
|
||||
// i.e why we use real address from error if present
|
||||
address := ctx.MetaInput.Address()
|
||||
// get address override from error
|
||||
if err != nil {
|
||||
tmp := errkit.GetAttrValue(err, "address")
|
||||
if tmp.Any() != nil {
|
||||
address = tmp.String()
|
||||
}
|
||||
}
|
||||
finalValue := c.normalizeCacheValue(address)
|
||||
return finalValue
|
||||
}
|
||||
|
||||
var reCheckError = regexp.MustCompile(`(no address found for host|could not resolve host|connection refused|connection reset by peer|could not connect to any address found for host|timeout awaiting response headers)`)
|
||||
|
||||
// checkError checks if an error represents a type that should be
|
||||
// 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 {
|
||||
if err == nil {
|
||||
return false
|
||||
}
|
||||
errString := err.Error()
|
||||
for _, msg := range c.TrackError {
|
||||
if strings.Contains(errString, msg) {
|
||||
return true
|
||||
|
||||
kind := errkit.GetErrorKind(err, nucleierr.ErrTemplateLogic)
|
||||
switch kind {
|
||||
case nucleierr.ErrTemplateLogic:
|
||||
// these are errors that are not related to the target
|
||||
// and are due to template logic
|
||||
return false
|
||||
case errkit.ErrKindNetworkTemporary:
|
||||
// these should not be counted as host errors
|
||||
return false
|
||||
case errkit.ErrKindNetworkPermanent:
|
||||
// these should be counted as host errors
|
||||
return true
|
||||
case errkit.ErrKindDeadline:
|
||||
// these should not be counted as host errors
|
||||
return false
|
||||
default:
|
||||
// parse error for further processing
|
||||
errX := errkit.FromError(err)
|
||||
tmp := errX.Cause()
|
||||
cause := tmp.Error()
|
||||
if strings.Contains(cause, "ReadStatusLine:") && strings.Contains(cause, "read: connection reset by peer") {
|
||||
// this is a FP and should not be counted as a host error
|
||||
// because server closes connection when it reads corrupted bytes which we send via rawhttp
|
||||
return false
|
||||
}
|
||||
if strings.HasPrefix(cause, "ReadStatusLine:") {
|
||||
// error is present in last part when using rawhttp
|
||||
// this will be fixed once errkit is used everywhere
|
||||
lastIndex := strings.LastIndex(cause, ":")
|
||||
if lastIndex == -1 {
|
||||
lastIndex = 0
|
||||
}
|
||||
if lastIndex >= len(cause)-1 {
|
||||
lastIndex = 0
|
||||
}
|
||||
cause = cause[lastIndex+1:]
|
||||
}
|
||||
for _, msg := range c.TrackError {
|
||||
if strings.Contains(cause, msg) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return reCheckError.MatchString(cause)
|
||||
}
|
||||
return reCheckError.MatchString(errString)
|
||||
}
|
||||
@@ -1,11 +1,13 @@
|
||||
package hosterrorscache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/contextargs"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -13,8 +15,8 @@ func TestCacheCheck(t *testing.T) {
|
||||
cache := New(3, DefaultMaxHostsCount, nil)
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
cache.MarkFailed("test", fmt.Errorf("could not resolve host"))
|
||||
got := cache.Check("test")
|
||||
cache.MarkFailed(newCtxArgs("test"), fmt.Errorf("could not resolve host"))
|
||||
got := cache.Check(newCtxArgs("test"))
|
||||
if i < 2 {
|
||||
// till 3 the host is not flagged to skip
|
||||
require.False(t, got)
|
||||
@@ -24,7 +26,7 @@ func TestCacheCheck(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
value := cache.Check("test")
|
||||
value := cache.Check(newCtxArgs("test"))
|
||||
require.Equal(t, true, value, "could not get checked value")
|
||||
}
|
||||
|
||||
@@ -32,8 +34,8 @@ func TestTrackErrors(t *testing.T) {
|
||||
cache := New(3, DefaultMaxHostsCount, []string{"custom error"})
|
||||
|
||||
for i := 0; i < 100; i++ {
|
||||
cache.MarkFailed("custom", fmt.Errorf("got: nested: custom error"))
|
||||
got := cache.Check("custom")
|
||||
cache.MarkFailed(newCtxArgs("custom"), fmt.Errorf("got: nested: custom error"))
|
||||
got := cache.Check(newCtxArgs("custom"))
|
||||
if i < 2 {
|
||||
// till 3 the host is not flagged to skip
|
||||
require.False(t, got)
|
||||
@@ -42,7 +44,7 @@ func TestTrackErrors(t *testing.T) {
|
||||
require.True(t, got)
|
||||
}
|
||||
}
|
||||
value := cache.Check("custom")
|
||||
value := cache.Check(newCtxArgs("custom"))
|
||||
require.Equal(t, true, value, "could not get checked value")
|
||||
}
|
||||
|
||||
@@ -73,23 +75,23 @@ func TestCacheMarkFailed(t *testing.T) {
|
||||
|
||||
tests := []struct {
|
||||
host string
|
||||
expected int
|
||||
expected int32
|
||||
}{
|
||||
{"http://example.com:80", 1},
|
||||
{"example.com:80", 2},
|
||||
{"example.com", 1},
|
||||
// earlier if port is not provided then port was omitted
|
||||
// but from now it will default to appropriate http scheme based port with 80 as default
|
||||
{"example.com:443", 1},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
normalizedCacheValue := cache.normalizeCacheValue(test.host)
|
||||
cache.MarkFailed(test.host, fmt.Errorf("no address found for host"))
|
||||
normalizedCacheValue := cache.GetKeyFromContext(newCtxArgs(test.host), nil)
|
||||
cache.MarkFailed(newCtxArgs(test.host), fmt.Errorf("no address found for host"))
|
||||
failedTarget, err := cache.failedTargets.Get(normalizedCacheValue)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, failedTarget)
|
||||
|
||||
value, ok := failedTarget.(*cacheItem)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, test.expected, value.errors.Load())
|
||||
require.EqualValues(t, test.expected, failedTarget.errors.Load())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +104,7 @@ func TestCacheMarkFailedConcurrent(t *testing.T) {
|
||||
}{
|
||||
{"http://example.com:80", 200},
|
||||
{"example.com:80", 200},
|
||||
{"example.com", 100},
|
||||
{"example.com:443", 100},
|
||||
}
|
||||
|
||||
// the cache is not atomic during items creation, so we pre-create them with counter to zero
|
||||
@@ -120,22 +122,25 @@ func TestCacheMarkFailedConcurrent(t *testing.T) {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
cache.MarkFailed(currentTest.host, fmt.Errorf("could not resolve host"))
|
||||
cache.MarkFailed(newCtxArgs(currentTest.host), fmt.Errorf("could not resolve host"))
|
||||
}()
|
||||
}
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
for _, test := range tests {
|
||||
require.True(t, cache.Check(test.host))
|
||||
require.True(t, cache.Check(newCtxArgs(test.host)))
|
||||
|
||||
normalizedCacheValue := cache.normalizeCacheValue(test.host)
|
||||
failedTarget, err := cache.failedTargets.Get(normalizedCacheValue)
|
||||
require.Nil(t, err)
|
||||
require.NotNil(t, failedTarget)
|
||||
|
||||
value, ok := failedTarget.(*cacheItem)
|
||||
require.True(t, ok)
|
||||
require.EqualValues(t, test.expected, value.errors.Load())
|
||||
require.EqualValues(t, test.expected, failedTarget.errors.Load())
|
||||
}
|
||||
}
|
||||
|
||||
func newCtxArgs(value string) *contextargs.Context {
|
||||
ctx := contextargs.NewWithInput(context.TODO(), value)
|
||||
return ctx
|
||||
}
|
||||
@@ -21,6 +21,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/responsehighlighter"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/helpers/writer"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
@@ -180,6 +181,14 @@ func (c *Client) processInteractionForRequest(interaction *server.Interaction, d
|
||||
gologger.DefaultLogger.Print().Msgf("[Interactsh]: got result %v and status %v after processing interaction", result, matched)
|
||||
}
|
||||
|
||||
if c.options.FuzzParamsFrequency != nil {
|
||||
if !matched {
|
||||
c.options.FuzzParamsFrequency.MarkParameter(data.Parameter, data.Request.URL.String(), data.Operators.TemplateID)
|
||||
} else {
|
||||
c.options.FuzzParamsFrequency.UnmarkParameter(data.Parameter, data.Request.URL.String(), data.Operators.TemplateID)
|
||||
}
|
||||
}
|
||||
|
||||
// if we don't match, return
|
||||
if !matched || result == nil {
|
||||
return false
|
||||
@@ -320,6 +329,9 @@ type RequestData struct {
|
||||
Operators *operators.Operators
|
||||
MatchFunc operators.MatchFunc
|
||||
ExtractFunc operators.ExtractFunc
|
||||
|
||||
Parameter string
|
||||
Request *retryablehttp.Request
|
||||
}
|
||||
|
||||
// RequestEvent is the event for a network request sent by nuclei.
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/interactsh/pkg/client"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/frequency"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting"
|
||||
@@ -46,8 +47,9 @@ type Options struct {
|
||||
// NoColor disables printing colors for matches
|
||||
NoColor bool
|
||||
|
||||
StopAtFirstMatch bool
|
||||
HTTPClient *retryablehttp.Client
|
||||
FuzzParamsFrequency *frequency.Tracker
|
||||
StopAtFirstMatch bool
|
||||
HTTPClient *retryablehttp.Client
|
||||
}
|
||||
|
||||
// DefaultOptions returns the default options for interactsh client
|
||||
|
||||
@@ -13,7 +13,6 @@ import (
|
||||
|
||||
// Init initializes the client pools for the protocols
|
||||
func Init(options *types.Options) error {
|
||||
|
||||
if err := protocolstate.Init(options); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -39,5 +38,5 @@ func Init(options *types.Options) error {
|
||||
}
|
||||
|
||||
func Close() {
|
||||
protocolstate.Dialer.Close()
|
||||
protocolstate.Close()
|
||||
}
|
||||
@@ -22,6 +22,10 @@ var (
|
||||
Dialer *fastdialer.Dialer
|
||||
)
|
||||
|
||||
func ShouldInit() bool {
|
||||
return Dialer == nil
|
||||
}
|
||||
|
||||
// Init creates the Dialer instance based on user configuration
|
||||
func Init(options *types.Options) error {
|
||||
if Dialer != nil {
|
||||
@@ -180,6 +184,7 @@ func interfaceAddress(interfaceName string) (net.IP, error) {
|
||||
if ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() {
|
||||
if ipnet.IP.To4() != nil {
|
||||
address = ipnet.IP
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -209,6 +214,8 @@ func interfaceAddresses(interfaceName string) ([]net.Addr, error) {
|
||||
func Close() {
|
||||
if Dialer != nil {
|
||||
Dialer.Close()
|
||||
Dialer = nil
|
||||
}
|
||||
Dialer = nil
|
||||
StopActiveMemGuardian()
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import (
|
||||
"github.com/projectdiscovery/uncover"
|
||||
"github.com/projectdiscovery/uncover/sources"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
@@ -84,9 +85,21 @@ func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.T
|
||||
if queriesMap[engine] == nil {
|
||||
queriesMap[engine] = []string{}
|
||||
}
|
||||
queriesMap[engine] = append(queriesMap[engine], fmt.Sprint(v))
|
||||
switch v := v.(type) {
|
||||
case []interface{}:
|
||||
qs := queriesMap[engine]
|
||||
for _, vv := range v {
|
||||
qs = append(qs, fmt.Sprint(vv))
|
||||
}
|
||||
queriesMap[engine] = qs
|
||||
default:
|
||||
queriesMap[engine] = append(queriesMap[engine], fmt.Sprint(v))
|
||||
}
|
||||
}
|
||||
}
|
||||
for engine, queries := range queriesMap {
|
||||
queriesMap[engine] = sliceutil.Dedupe(queries)
|
||||
}
|
||||
keys := mapsutil.GetKeys(queriesMap)
|
||||
gologger.Info().Msgf("Running uncover queries from template against: %s", strings.Join(keys, ","))
|
||||
result := make(chan string, runtime.NumCPU())
|
||||
@@ -97,8 +110,11 @@ func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.T
|
||||
// TODO: add support for map[engine]queries in uncover
|
||||
// Note below implementation is intentionally sequential to avoid burning all the API keys
|
||||
counter := 0
|
||||
|
||||
outerLoop:
|
||||
for eng, queries := range queriesMap {
|
||||
if opts.Limit > 0 && counter >= opts.Limit {
|
||||
break
|
||||
}
|
||||
// create new uncover options for each engine
|
||||
uncoverOpts := &uncover.Options{
|
||||
Agents: []string{eng},
|
||||
@@ -114,18 +130,20 @@ func GetUncoverTargetsFromMetadata(ctx context.Context, templates []*templates.T
|
||||
gologger.Error().Msgf("Could not get targets using %v engine from uncover: %s", eng, err)
|
||||
return
|
||||
}
|
||||
|
||||
innerLoop:
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case res, ok := <-ch:
|
||||
if !ok {
|
||||
return
|
||||
continue outerLoop
|
||||
}
|
||||
result <- res
|
||||
counter++
|
||||
if opts.Limit > 0 && counter >= opts.Limit {
|
||||
return
|
||||
break innerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,27 +1,24 @@
|
||||
package dns
|
||||
|
||||
// CanCluster returns true if the request can be clustered.
|
||||
//
|
||||
// This used by the clustering engine to decide whether two requests
|
||||
// are similar enough to be considered one and can be checked by
|
||||
// just adding the matcher/extractors for the request and the correct IDs.
|
||||
func (request *Request) CanCluster(other *Request) bool {
|
||||
if len(request.Resolvers) > 0 || request.Trace || request.ID != "" {
|
||||
return false
|
||||
}
|
||||
if request.Name != other.Name ||
|
||||
request.class != other.class ||
|
||||
request.Retries != other.Retries ||
|
||||
request.question != other.question {
|
||||
return false
|
||||
}
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
)
|
||||
|
||||
|
||||
// TmplClusterKey generates a unique key for the request
|
||||
// to be used in the clustering process.
|
||||
func (request *Request) TmplClusterKey() uint64 {
|
||||
recursion := ""
|
||||
if request.Recursion != nil {
|
||||
if other.Recursion == nil {
|
||||
return false
|
||||
}
|
||||
if *request.Recursion != *other.Recursion {
|
||||
return false
|
||||
}
|
||||
recursion = fmt.Sprintf("%t", *request.Recursion)
|
||||
}
|
||||
return true
|
||||
inp := fmt.Sprintf("%s-%d-%d-%d-%s", request.Name, request.class, request.Retries, request.question, recursion)
|
||||
return xxhash.Sum64String(inp)
|
||||
}
|
||||
|
||||
// IsClusterable returns true if the request is eligible to be clustered.
|
||||
func (request *Request) IsClusterable() bool {
|
||||
return !(len(request.Resolvers) > 0 || request.Trace || request.ID != "")
|
||||
}
|
||||
@@ -24,6 +24,7 @@ import (
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
httputil "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils/http"
|
||||
contextutil "github.com/projectdiscovery/utils/context"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
folderutil "github.com/projectdiscovery/utils/folder"
|
||||
@@ -35,6 +36,8 @@ import (
|
||||
var (
|
||||
errinvalidArguments = errorutil.New("invalid arguments provided")
|
||||
ErrLFAccessDenied = errorutil.New("Use -allow-local-file-access flag to enable local file access")
|
||||
// ErrActionExecDealine is the error returned when alloted time for action execution exceeds
|
||||
ErrActionExecDealine = errkit.New("headless action execution deadline exceeded").SetKind(errkit.ErrKindDeadline).Build()
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -619,7 +622,7 @@ func (p *Page) WaitEvent(act *Action, out map[string]string) (func() error, erro
|
||||
// Just wait the event to happen
|
||||
waitFunc := func() (err error) {
|
||||
// execute actual wait event
|
||||
ctx, cancel := context.WithTimeout(context.Background(), maxDuration)
|
||||
ctx, cancel := context.WithTimeoutCause(context.Background(), maxDuration, ErrActionExecDealine)
|
||||
defer cancel()
|
||||
err = contextutil.ExecFunc(ctx, p.page.WaitEvent(waitEvent))
|
||||
return
|
||||
|
||||
@@ -1,30 +1,21 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
"golang.org/x/exp/maps"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
)
|
||||
|
||||
// CanCluster returns true if the request can be clustered.
|
||||
//
|
||||
// This used by the clustering engine to decide whether two requests
|
||||
// are similar enough to be considered one and can be checked by
|
||||
// just adding the matcher/extractors for the request and the correct IDs.
|
||||
func (request *Request) CanCluster(other *Request) bool {
|
||||
if len(request.Payloads) > 0 || len(request.Fuzzing) > 0 || len(request.Raw) > 0 || len(request.Body) > 0 || request.Unsafe || request.NeedsRequestCondition() || request.Name != "" {
|
||||
return false
|
||||
}
|
||||
if request.Method != other.Method ||
|
||||
request.MaxRedirects != other.MaxRedirects ||
|
||||
request.DisableCookie != other.DisableCookie ||
|
||||
request.Redirects != other.Redirects {
|
||||
return false
|
||||
}
|
||||
if !sliceutil.Equal(request.Path, other.Path) {
|
||||
return false
|
||||
}
|
||||
if !maps.Equal(request.Headers, other.Headers) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
// TmplClusterKey generates a unique key for the request
|
||||
// to be used in the clustering process.
|
||||
func (request *Request) TmplClusterKey() uint64 {
|
||||
inp := fmt.Sprintf("%s-%d-%t-%t-%s-%d", request.Method.String(), request.MaxRedirects, request.DisableCookie, request.Redirects, strings.Join(request.Path, "-"), utils.MapHash(request.Headers))
|
||||
return xxhash.Sum64String(inp)
|
||||
}
|
||||
|
||||
// IsClusterable returns true if the request is eligible to be clustered.
|
||||
func (request *Request) IsClusterable() bool {
|
||||
return !(len(request.Payloads) > 0 || len(request.Fuzzing) > 0 || len(request.Raw) > 0 || len(request.Body) > 0 || request.Unsafe || request.NeedsRequestCondition() || request.Name != "")
|
||||
}
|
||||
@@ -8,8 +8,11 @@ import (
|
||||
|
||||
func TestCanCluster(t *testing.T) {
|
||||
req := &Request{Unsafe: true}
|
||||
require.False(t, req.CanCluster(&Request{}), "could cluster unsafe request")
|
||||
require.False(t, req.IsClusterable(), "could cluster unsafe request")
|
||||
|
||||
req = &Request{Path: []string{"{{BaseURL}}"}, Method: HTTPMethodTypeHolder{MethodType: HTTPGet}}
|
||||
require.True(t, req.CanCluster(&Request{Path: []string{"{{BaseURL}}"}, Method: HTTPMethodTypeHolder{MethodType: HTTPGet}}), "could not cluster GET request")
|
||||
newReq := &Request{Path: []string{"{{BaseURL}}"}, Method: HTTPMethodTypeHolder{MethodType: HTTPGet}}
|
||||
require.True(t, req.IsClusterable(), "could not cluster GET request")
|
||||
require.True(t, req.IsClusterable(), "could not cluster GET request")
|
||||
require.Equal(t, req.TmplClusterKey(), newReq.TmplClusterKey(), "cluster keys should be equal")
|
||||
}
|
||||
@@ -16,7 +16,6 @@ import (
|
||||
"golang.org/x/net/proxy"
|
||||
"golang.org/x/net/publicsuffix"
|
||||
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer/ja3/impersonate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
@@ -28,16 +27,14 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
// Dialer is a copy of the fastdialer from protocolstate
|
||||
Dialer *fastdialer.Dialer
|
||||
|
||||
rawHttpClient *rawhttp.Client
|
||||
forceMaxRedirects int
|
||||
normalClient *retryablehttp.Client
|
||||
clientPool *mapsutil.SyncLockMap[string, *retryablehttp.Client]
|
||||
// ResponseHeaderTimeout is the timeout for response headers
|
||||
// MaxResponseHeaderTimeout is the timeout for response headers
|
||||
// to be read from the server (this prevents infinite hang started by server if any)
|
||||
ResponseHeaderTimeout = time.Duration(5) * time.Second
|
||||
// Note: this will be overridden temporarily when using @timeout request annotation
|
||||
MaxResponseHeaderTimeout = time.Duration(10) * time.Second
|
||||
// HttpTimeoutMultiplier is the multiplier for the http timeout
|
||||
HttpTimeoutMultiplier = 3
|
||||
)
|
||||
@@ -53,6 +50,9 @@ func Init(options *types.Options) error {
|
||||
if normalClient != nil {
|
||||
return nil
|
||||
}
|
||||
if options.Timeout > 10 {
|
||||
MaxResponseHeaderTimeout = time.Duration(options.Timeout) * time.Second
|
||||
}
|
||||
if options.ShouldFollowHTTPRedirects() {
|
||||
forceMaxRedirects = options.MaxRedirects
|
||||
}
|
||||
@@ -111,6 +111,8 @@ type Configuration struct {
|
||||
RedirectFlow RedirectFlow
|
||||
// Connection defines custom connection configuration
|
||||
Connection *ConnectionConfiguration
|
||||
// ResponseHeaderTimeout is the timeout for response body to be read from the server
|
||||
ResponseHeaderTimeout time.Duration
|
||||
}
|
||||
|
||||
// Hash returns the hash of the configuration to allow client pooling
|
||||
@@ -129,13 +131,15 @@ func (c *Configuration) Hash() string {
|
||||
builder.WriteString(strconv.FormatBool(c.DisableCookie))
|
||||
builder.WriteString("c")
|
||||
builder.WriteString(strconv.FormatBool(c.Connection != nil))
|
||||
builder.WriteString("r")
|
||||
builder.WriteString(strconv.FormatInt(int64(c.ResponseHeaderTimeout.Seconds()), 10))
|
||||
hash := builder.String()
|
||||
return hash
|
||||
}
|
||||
|
||||
// HasStandardOptions checks whether the configuration requires custom settings
|
||||
func (c *Configuration) HasStandardOptions() bool {
|
||||
return c.Threads == 0 && c.MaxRedirects == 0 && c.RedirectFlow == DontFollowRedirect && c.DisableCookie && c.Connection == nil && !c.NoTimeout
|
||||
return c.Threads == 0 && c.MaxRedirects == 0 && c.RedirectFlow == DontFollowRedirect && c.DisableCookie && c.Connection == nil && !c.NoTimeout && c.ResponseHeaderTimeout == 0
|
||||
}
|
||||
|
||||
// GetRawHTTP returns the rawhttp request client
|
||||
@@ -146,8 +150,8 @@ func GetRawHTTP(options *types.Options) *rawhttp.Client {
|
||||
rawHttpOptions.Proxy = types.ProxyURL
|
||||
} else if types.ProxySocksURL != "" {
|
||||
rawHttpOptions.Proxy = types.ProxySocksURL
|
||||
} else if Dialer != nil {
|
||||
rawHttpOptions.FastDialer = Dialer
|
||||
} else if protocolstate.Dialer != nil {
|
||||
rawHttpOptions.FastDialer = protocolstate.Dialer
|
||||
}
|
||||
rawHttpOptions.Timeout = GetHttpTimeout(options)
|
||||
rawHttpClient = rawhttp.NewClient(rawHttpOptions)
|
||||
@@ -167,10 +171,6 @@ func Get(options *types.Options, configuration *Configuration) (*retryablehttp.C
|
||||
func wrappedGet(options *types.Options, configuration *Configuration) (*retryablehttp.Client, error) {
|
||||
var err error
|
||||
|
||||
if Dialer == nil {
|
||||
Dialer = protocolstate.Dialer
|
||||
}
|
||||
|
||||
hash := configuration.Hash()
|
||||
if client, ok := clientPool.Get(hash); ok {
|
||||
return client, nil
|
||||
@@ -182,6 +182,9 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
maxIdleConns := 0
|
||||
maxConnsPerHost := 0
|
||||
maxIdleConnsPerHost := -1
|
||||
// do not split given timeout into chunks for retry
|
||||
// because this won't work on slow hosts
|
||||
retryableHttpOptions.NoAdjustTimeout = true
|
||||
|
||||
if configuration.Threads > 0 || options.ScanStrategy == scanstrategy.HostSpray.String() {
|
||||
// Single host
|
||||
@@ -235,24 +238,30 @@ func wrappedGet(options *types.Options, configuration *Configuration) (*retryabl
|
||||
return nil, errors.Wrap(err, "could not create client certificate")
|
||||
}
|
||||
|
||||
// responseHeaderTimeout is max timeout for response headers to be read
|
||||
responseHeaderTimeout := MaxResponseHeaderTimeout
|
||||
if configuration.ResponseHeaderTimeout != 0 {
|
||||
responseHeaderTimeout = configuration.ResponseHeaderTimeout
|
||||
}
|
||||
|
||||
transport := &http.Transport{
|
||||
ForceAttemptHTTP2: options.ForceAttemptHTTP2,
|
||||
DialContext: Dialer.Dial,
|
||||
DialContext: protocolstate.Dialer.Dial,
|
||||
DialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
if options.TlsImpersonate {
|
||||
return Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsConfig, impersonate.Random, nil)
|
||||
return protocolstate.Dialer.DialTLSWithConfigImpersonate(ctx, network, addr, tlsConfig, impersonate.Random, nil)
|
||||
}
|
||||
if options.HasClientCertificates() || options.ForceAttemptHTTP2 {
|
||||
return Dialer.DialTLSWithConfig(ctx, network, addr, tlsConfig)
|
||||
return protocolstate.Dialer.DialTLSWithConfig(ctx, network, addr, tlsConfig)
|
||||
}
|
||||
return Dialer.DialTLS(ctx, network, addr)
|
||||
return protocolstate.Dialer.DialTLS(ctx, network, addr)
|
||||
},
|
||||
MaxIdleConns: maxIdleConns,
|
||||
MaxIdleConnsPerHost: maxIdleConnsPerHost,
|
||||
MaxConnsPerHost: maxConnsPerHost,
|
||||
TLSClientConfig: tlsConfig,
|
||||
DisableKeepAlives: disableKeepAlives,
|
||||
ResponseHeaderTimeout: ResponseHeaderTimeout,
|
||||
ResponseHeaderTimeout: responseHeaderTimeout,
|
||||
}
|
||||
|
||||
if types.ProxyURL != "" {
|
||||
@@ -351,7 +360,7 @@ func makeCheckRedirectFunc(redirectType RedirectFlow, maxRedirects int) checkRed
|
||||
}
|
||||
}
|
||||
|
||||
func checkMaxRedirects(req *http.Request, via []*http.Request, maxRedirects int) error {
|
||||
func checkMaxRedirects(_ *http.Request, via []*http.Request, maxRedirects int) error {
|
||||
if maxRedirects == 0 {
|
||||
if len(via) > defaultMaxRedirects {
|
||||
return http.ErrUseLastResponse
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package httpclientpool
|
||||
|
||||
import (
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
// WithCustomTimeout is a configuration for custom timeout
|
||||
type WithCustomTimeout struct {
|
||||
Timeout time.Duration
|
||||
}
|
||||
|
||||
// RawHttpRequestOpts is a configuration for raw http request
|
||||
type RawHttpRequestOpts struct {
|
||||
// Method is the http method to use
|
||||
Method string
|
||||
// URL is the url to request
|
||||
URL string
|
||||
// Path is request path to use
|
||||
Path string
|
||||
// Headers is the headers to use
|
||||
Headers map[string][]string
|
||||
// Body is the body to use
|
||||
Body io.Reader
|
||||
// Options is more client related options
|
||||
Options *rawhttp.Options
|
||||
}
|
||||
|
||||
// SendRawRequest sends a raw http request with the provided options and returns http response
|
||||
func SendRawRequest(client *rawhttp.Client, opts *RawHttpRequestOpts) (*http.Response, error) {
|
||||
resp, err := client.DoRawWithOptions(opts.Method, opts.URL, opts.Path, opts.Headers, opts.Body, opts.Options)
|
||||
if err != nil {
|
||||
cause := err.Error()
|
||||
if strings.Contains(cause, "ReadStatusLine: ") && strings.Contains(cause, "read: connection reset by peer") {
|
||||
// this error is caused when rawhttp client sends a corrupted or malformed request packet to server
|
||||
// some servers may attempt gracefully shutdown but most will just abruptly close the connection which results
|
||||
// in a connection reset by peer error and this can be safely assumed as 400 Bad Request in terms of normal http flow
|
||||
req, reqErr := http.NewRequest(opts.Method, opts.URL, opts.Body)
|
||||
if reqErr != nil {
|
||||
// failed to build new request mostly because of invalid url or body
|
||||
// try again or else return urlErr
|
||||
parsed, urlErr := urlutil.ParseAbsoluteURL(opts.URL, true)
|
||||
if urlErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
req, reqErr = http.NewRequest(opts.Method, parsed.Host, opts.Body)
|
||||
if reqErr != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.URL = parsed.URL
|
||||
req.Header = opts.Headers
|
||||
}
|
||||
|
||||
// if req is still nil, return error
|
||||
if req == nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
req.Header = opts.Headers
|
||||
resp = &http.Response{
|
||||
Request: req,
|
||||
StatusCode: http.StatusBadRequest,
|
||||
Status: http.StatusText(http.StatusBadRequest),
|
||||
Body: io.NopCloser(strings.NewReader("")),
|
||||
}
|
||||
return resp, nil
|
||||
}
|
||||
}
|
||||
return resp, err
|
||||
}
|
||||
@@ -123,9 +123,11 @@ func (h *StopAtFirstMatchHandler[T]) MatchCallback(fn func()) {
|
||||
// run runs the internal handler
|
||||
func (h *StopAtFirstMatchHandler[T]) run(ctx context.Context) {
|
||||
defer h.internalWg.Done()
|
||||
|
||||
for {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
return
|
||||
case val, ok := <-h.ResultChan:
|
||||
if !ok {
|
||||
return
|
||||
|
||||
+104
-37
@@ -35,13 +35,16 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/signerpool"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
|
||||
"github.com/projectdiscovery/rawhttp"
|
||||
convUtil "github.com/projectdiscovery/utils/conversion"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
httpUtils "github.com/projectdiscovery/utils/http"
|
||||
"github.com/projectdiscovery/utils/reader"
|
||||
sliceutil "github.com/projectdiscovery/utils/slice"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
urlutil "github.com/projectdiscovery/utils/url"
|
||||
)
|
||||
|
||||
@@ -53,9 +56,11 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
MaxBodyRead = int64(10 * 1024 * 1024) // 10MB
|
||||
MaxBodyRead = 10 * unitutils.Mega
|
||||
// ErrMissingVars is error occured when variables are missing
|
||||
ErrMissingVars = errors.New("stop execution due to unresolved variables")
|
||||
ErrMissingVars = errkit.New("stop execution due to unresolved variables").SetKind(nucleierr.ErrTemplateLogic).Build()
|
||||
// ErrHttpEngineRequestDeadline is error occured when request deadline set by http request engine is exceeded
|
||||
ErrHttpEngineRequestDeadline = errkit.New("http request engine deadline exceeded").SetKind(errkit.ErrKindDeadline).Build()
|
||||
)
|
||||
|
||||
// Type returns the type of the protocol request
|
||||
@@ -114,7 +119,13 @@ func (request *Request) executeRaceRequest(input *contextargs.Context, previous
|
||||
}
|
||||
|
||||
shouldStop := (request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch || request.options.StopAtFirstMatch)
|
||||
spmHandler := httputils.NewNonBlockingSPMHandler[error](ctx, maxErrorsWhenParallel, shouldStop)
|
||||
|
||||
childCtx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
spmHandler := httputils.NewNonBlockingSPMHandler[error](childCtx, maxErrorsWhenParallel, shouldStop)
|
||||
defer spmHandler.Cancel()
|
||||
|
||||
gotMatches := &atomic.Bool{}
|
||||
// wrappedCallback is a callback that wraps the original callback
|
||||
// to implement stop at first match logic
|
||||
@@ -141,15 +152,15 @@ func (request *Request) executeRaceRequest(input *contextargs.Context, previous
|
||||
return
|
||||
}
|
||||
// marks thsi host as unresponsive if applicable
|
||||
request.markUnresponsiveHost(input, err)
|
||||
if request.isUnresponsiveHost(input) {
|
||||
request.markUnresponsiveAddress(input, err)
|
||||
if request.isUnresponsiveAddress(input) {
|
||||
// stop all inflight requests
|
||||
spmHandler.Cancel()
|
||||
}
|
||||
})
|
||||
|
||||
for i := 0; i < request.RaceNumberRequests; i++ {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(input) {
|
||||
// stop sending more requests condition is met
|
||||
break
|
||||
}
|
||||
@@ -157,7 +168,7 @@ func (request *Request) executeRaceRequest(input *contextargs.Context, previous
|
||||
// execute http request
|
||||
go func(httpRequest *generatedRequest) {
|
||||
defer spmHandler.Release()
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(input) {
|
||||
// stop sending more requests condition is met
|
||||
return
|
||||
}
|
||||
@@ -195,7 +206,13 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
|
||||
// Stop-at-first-match logic while executing requests
|
||||
// parallely using threads
|
||||
shouldStop := (request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch || request.options.StopAtFirstMatch)
|
||||
spmHandler := httputils.NewBlockingSPMHandler[error](context.Background(), maxWorkers, maxErrorsWhenParallel, shouldStop)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
spmHandler := httputils.NewBlockingSPMHandler[error](ctx, maxWorkers, maxErrorsWhenParallel, shouldStop)
|
||||
defer spmHandler.Cancel()
|
||||
|
||||
// wrappedCallback is a callback that wraps the original callback
|
||||
// to implement stop at first match logic
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
@@ -220,8 +237,8 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
|
||||
return
|
||||
}
|
||||
// marks thsi host as unresponsive if applicable
|
||||
request.markUnresponsiveHost(input, err)
|
||||
if request.isUnresponsiveHost(input) {
|
||||
request.markUnresponsiveAddress(input, err)
|
||||
if request.isUnresponsiveAddress(input) {
|
||||
// stop all inflight requests
|
||||
spmHandler.Cancel()
|
||||
}
|
||||
@@ -249,7 +266,7 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
|
||||
}
|
||||
|
||||
// break if stop at first match is found or host is unresponsive
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(input) {
|
||||
break
|
||||
}
|
||||
|
||||
@@ -265,17 +282,23 @@ func (request *Request) executeParallelHTTP(input *contextargs.Context, dynamicV
|
||||
if input.MetaInput.Input == "" {
|
||||
input.MetaInput.Input = generatedHttpRequest.URL()
|
||||
}
|
||||
updatedInput := contextargs.GetCopyIfHostOutdated(input, generatedHttpRequest.URL())
|
||||
if request.isUnresponsiveAddress(updatedInput) {
|
||||
// skip on unresponsive host no need to continue
|
||||
spmHandler.Cancel()
|
||||
return nil
|
||||
}
|
||||
spmHandler.Acquire()
|
||||
go func(httpRequest *generatedRequest) {
|
||||
defer spmHandler.Release()
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) || spmHandler.Cancelled() {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(updatedInput) || spmHandler.Cancelled() {
|
||||
return
|
||||
}
|
||||
// putting ratelimiter here prevents any unnecessary waiting if any
|
||||
request.options.RateLimitTake()
|
||||
|
||||
// after ratelimit take, check if we need to stop
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) || spmHandler.Cancelled() {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(updatedInput) || spmHandler.Cancelled() {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -327,7 +350,13 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
|
||||
// Stop-at-first-match logic while executing requests
|
||||
// parallely using threads
|
||||
shouldStop := (request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch || request.options.StopAtFirstMatch)
|
||||
spmHandler := httputils.NewBlockingSPMHandler[error](context.Background(), maxWorkers, maxErrorsWhenParallel, shouldStop)
|
||||
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
|
||||
spmHandler := httputils.NewBlockingSPMHandler[error](ctx, maxWorkers, maxErrorsWhenParallel, shouldStop)
|
||||
defer spmHandler.Cancel()
|
||||
|
||||
// wrappedCallback is a callback that wraps the original callback
|
||||
// to implement stop at first match logic
|
||||
wrappedCallback := func(event *output.InternalWrappedEvent) {
|
||||
@@ -352,8 +381,8 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
|
||||
return
|
||||
}
|
||||
// marks thsi host as unresponsive if applicable
|
||||
request.markUnresponsiveHost(input, err)
|
||||
if request.isUnresponsiveHost(input) {
|
||||
request.markUnresponsiveAddress(input, err)
|
||||
if request.isUnresponsiveAddress(input) {
|
||||
// stop all inflight requests
|
||||
spmHandler.Cancel()
|
||||
}
|
||||
@@ -371,7 +400,7 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
|
||||
default:
|
||||
}
|
||||
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) || spmHandler.Cancelled() {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(input) || spmHandler.Cancelled() {
|
||||
// skip if first match is found
|
||||
break
|
||||
}
|
||||
@@ -385,11 +414,17 @@ func (request *Request) executeTurboHTTP(input *contextargs.Context, dynamicValu
|
||||
if input.MetaInput.Input == "" {
|
||||
input.MetaInput.Input = generatedHttpRequest.URL()
|
||||
}
|
||||
updatedInput := contextargs.GetCopyIfHostOutdated(input, generatedHttpRequest.URL())
|
||||
if request.isUnresponsiveAddress(updatedInput) {
|
||||
// skip on unresponsive host no need to continue
|
||||
spmHandler.Cancel()
|
||||
return nil
|
||||
}
|
||||
generatedHttpRequest.pipelinedClient = pipeClient
|
||||
spmHandler.Acquire()
|
||||
go func(httpRequest *generatedRequest) {
|
||||
defer spmHandler.Release()
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveHost(input) {
|
||||
if spmHandler.FoundFirstMatch() || request.isUnresponsiveAddress(updatedInput) {
|
||||
// skip if first match is found
|
||||
return
|
||||
}
|
||||
@@ -448,7 +483,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
request.options.RateLimitTake()
|
||||
|
||||
ctx := request.newContext(input)
|
||||
ctxWithTimeout, cancel := context.WithTimeout(ctx, httpclientpool.GetHttpTimeout(request.options.Options))
|
||||
ctxWithTimeout, cancel := context.WithTimeoutCause(ctx, httpclientpool.GetHttpTimeout(request.options.Options), ErrHttpEngineRequestDeadline)
|
||||
defer cancel()
|
||||
|
||||
generatedHttpRequest, err := generator.Make(ctxWithTimeout, input, data, payloads, dynamicValue)
|
||||
@@ -459,6 +494,12 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
request.options.Progress.IncrementFailedRequestsBy(int64(generator.Total()))
|
||||
return true, err
|
||||
}
|
||||
// ideally if http template used a custom port or hostname
|
||||
// we would want to update it in input but currently templateCtx logic
|
||||
// is closely tied to contextargs.Context so we are temporarily creating
|
||||
// a copy and using it to check for host errors etc
|
||||
// but this should be replaced once templateCtx is refactored properly
|
||||
updatedInput := contextargs.GetCopyIfHostOutdated(input, generatedHttpRequest.URL())
|
||||
|
||||
if generatedHttpRequest.customCancelFunction != nil {
|
||||
defer generatedHttpRequest.customCancelFunction()
|
||||
@@ -469,7 +510,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
input.MetaInput.Input = generatedHttpRequest.URL()
|
||||
}
|
||||
// Check if hosts keep erroring
|
||||
if request.isUnresponsiveHost(input) {
|
||||
if request.isUnresponsiveAddress(updatedInput) {
|
||||
return true, nil
|
||||
}
|
||||
var gotMatches bool
|
||||
@@ -509,7 +550,7 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
}
|
||||
if execReqErr != nil {
|
||||
// if applicable mark the host as unresponsive
|
||||
request.markUnresponsiveHost(input, execReqErr)
|
||||
request.markUnresponsiveAddress(updatedInput, execReqErr)
|
||||
requestErr = errorutil.NewWithErr(execReqErr).Msgf("got err while executing %v", generatedHttpRequest.URL())
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
} else {
|
||||
@@ -557,12 +598,12 @@ func (request *Request) ExecuteWithResults(input *contextargs.Context, dynamicVa
|
||||
return requestErr
|
||||
}
|
||||
|
||||
const drainReqSize = int64(8 * 1024)
|
||||
const drainReqSize = int64(8 * unitutils.Kilo)
|
||||
|
||||
// executeRequest executes the actual generated request and returns error if occurred
|
||||
func (request *Request) executeRequest(input *contextargs.Context, generatedRequest *generatedRequest, previousEvent output.InternalEvent, hasInteractMatchers bool, processEvent protocols.OutputEventCallback, requestCount int) (err error) {
|
||||
// Check if hosts keep erroring
|
||||
if request.isUnresponsiveHost(input) {
|
||||
if request.isUnresponsiveAddress(input) {
|
||||
return fmt.Errorf("hostErrorsCache : host %s is unresponsive", input.MetaInput.Input)
|
||||
}
|
||||
|
||||
@@ -695,7 +736,16 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
inputUrl = url.String()
|
||||
}
|
||||
formedURL = fmt.Sprintf("%s%s", inputUrl, generatedRequest.rawRequest.Path)
|
||||
resp, err = generatedRequest.original.rawhttpClient.DoRawWithOptions(generatedRequest.rawRequest.Method, inputUrl, generatedRequest.rawRequest.Path, generators.ExpandMapValues(generatedRequest.rawRequest.Headers), io.NopCloser(strings.NewReader(generatedRequest.rawRequest.Data)), &options)
|
||||
|
||||
// send rawhttp request and get response
|
||||
resp, err = httpclientpool.SendRawRequest(generatedRequest.original.rawhttpClient, &httpclientpool.RawHttpRequestOpts{
|
||||
Method: generatedRequest.rawRequest.Method,
|
||||
URL: inputUrl,
|
||||
Path: generatedRequest.rawRequest.Path,
|
||||
Headers: generators.ExpandMapValues(generatedRequest.rawRequest.Headers),
|
||||
Body: io.NopCloser(strings.NewReader(generatedRequest.rawRequest.Data)),
|
||||
Options: &options,
|
||||
})
|
||||
} else {
|
||||
//** For Normal requests **//
|
||||
hostname = generatedRequest.request.URL.Host
|
||||
@@ -713,17 +763,34 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
if errSignature := request.handleSignature(generatedRequest); errSignature != nil {
|
||||
return errSignature
|
||||
}
|
||||
|
||||
httpclient := request.httpClient
|
||||
|
||||
// this will be assigned/updated if this specific request has a custom configuration
|
||||
var modifiedConfig *httpclientpool.Configuration
|
||||
|
||||
// check for cookie related configuration
|
||||
if input.CookieJar != nil {
|
||||
connConfiguration := request.connConfiguration
|
||||
connConfiguration.Connection.SetCookieJar(input.CookieJar)
|
||||
client, err := httpclientpool.Get(request.options.Options, connConfiguration)
|
||||
modifiedConfig = connConfiguration
|
||||
}
|
||||
// check for request updatedTimeout annotation
|
||||
updatedTimeout, ok := generatedRequest.request.Context().Value(httpclientpool.WithCustomTimeout{}).(httpclientpool.WithCustomTimeout)
|
||||
if ok {
|
||||
if modifiedConfig == nil {
|
||||
modifiedConfig = request.connConfiguration
|
||||
}
|
||||
modifiedConfig.ResponseHeaderTimeout = updatedTimeout.Timeout
|
||||
}
|
||||
|
||||
if modifiedConfig != nil {
|
||||
client, err := httpclientpool.Get(request.options.Options, modifiedConfig)
|
||||
if err != nil {
|
||||
return errors.Wrap(err, "could not get http client")
|
||||
}
|
||||
httpclient = client
|
||||
}
|
||||
|
||||
resp, err = httpclient.Do(generatedRequest.request)
|
||||
}
|
||||
}
|
||||
@@ -776,7 +843,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
if input.MetaInput.CustomIP != "" {
|
||||
outputEvent["ip"] = input.MetaInput.CustomIP
|
||||
} else {
|
||||
outputEvent["ip"] = httpclientpool.Dialer.GetDialedIP(hostname)
|
||||
outputEvent["ip"] = protocolstate.Dialer.GetDialedIP(hostname)
|
||||
}
|
||||
|
||||
if len(generatedRequest.interactshURLs) > 0 {
|
||||
@@ -809,15 +876,15 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
// define max body read limit
|
||||
maxBodylimit := MaxBodyRead // 10MB
|
||||
if request.MaxSize > 0 {
|
||||
maxBodylimit = int64(request.MaxSize)
|
||||
maxBodylimit = request.MaxSize
|
||||
}
|
||||
if request.options.Options.ResponseReadSize != 0 {
|
||||
maxBodylimit = int64(request.options.Options.ResponseReadSize)
|
||||
maxBodylimit = request.options.Options.ResponseReadSize
|
||||
}
|
||||
|
||||
// respChain is http response chain that reads response body
|
||||
// efficiently by reusing buffers and does all decoding and optimizations
|
||||
respChain := httpUtils.NewResponseChain(resp, maxBodylimit)
|
||||
respChain := httpUtils.NewResponseChain(resp, int64(maxBodylimit))
|
||||
defer respChain.Close() // reuse buffers
|
||||
|
||||
// we only intend to log/save the final redirected response
|
||||
@@ -872,7 +939,7 @@ func (request *Request) executeRequest(input *contextargs.Context, generatedRequ
|
||||
if input.MetaInput.CustomIP != "" {
|
||||
outputEvent["ip"] = input.MetaInput.CustomIP
|
||||
} else {
|
||||
outputEvent["ip"] = httpclientpool.Dialer.GetDialedIP(hostname)
|
||||
outputEvent["ip"] = protocolstate.Dialer.GetDialedIP(hostname)
|
||||
}
|
||||
if request.options.Interactsh != nil {
|
||||
request.options.Interactsh.MakePlaceholders(generatedRequest.interactshURLs, outputEvent)
|
||||
@@ -1069,20 +1136,20 @@ func (request *Request) newContext(input *contextargs.Context) context.Context {
|
||||
return input.Context()
|
||||
}
|
||||
|
||||
// markUnresponsiveHost checks if the error is a unreponsive host error and marks it
|
||||
func (request *Request) markUnresponsiveHost(input *contextargs.Context, err error) {
|
||||
// markUnresponsiveAddress checks if the error is a unreponsive host error and marks it
|
||||
func (request *Request) markUnresponsiveAddress(input *contextargs.Context, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(input.MetaInput.ID(), err)
|
||||
request.options.HostErrorsCache.MarkFailed(input, err)
|
||||
}
|
||||
}
|
||||
|
||||
// isUnresponsiveHost checks if the error is a unreponsive based on its execution history
|
||||
func (request *Request) isUnresponsiveHost(input *contextargs.Context) bool {
|
||||
// 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.MetaInput.ID())
|
||||
return request.options.HostErrorsCache.Check(input)
|
||||
}
|
||||
return false
|
||||
}
|
||||
@@ -10,7 +10,9 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/fastdialer/fastdialer"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/http/httpclientpool"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types/nucleierr"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
iputil "github.com/projectdiscovery/utils/ip"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
@@ -27,6 +29,15 @@ var (
|
||||
reTimeoutAnnotation = regexp.MustCompile(`(?m)^@timeout:\s*(.+)\s*$`)
|
||||
// @once sets the request to be executed only once for a specific URL
|
||||
reOnceAnnotation = regexp.MustCompile(`(?m)^@once\s*$`)
|
||||
|
||||
// ErrTimeoutAnnotationDeadline is the error returned when a specific amount of time was exceeded for a request
|
||||
// which was alloted using @timeout annotation this usually means that vulnerability was not found
|
||||
// in rare case it could also happen due to network congestion
|
||||
// the assigned class is TemplateLogic since this in almost every case means that server is not vulnerable
|
||||
ErrTimeoutAnnotationDeadline = errkit.New("timeout annotation deadline exceeded").SetKind(nucleierr.ErrTemplateLogic).Build()
|
||||
// ErrRequestTimeoutDeadline is the error returned when a specific amount of time was exceeded for a request
|
||||
// this happens when the request execution exceeds alloted time
|
||||
ErrRequestTimeoutDeadline = errkit.New("request timeout deadline exceeded when notimeout is set").SetKind(errkit.ErrKindDeadline).Build()
|
||||
)
|
||||
|
||||
type flowMark int
|
||||
@@ -120,12 +131,16 @@ func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Req
|
||||
value := strings.TrimSpace(duration[1])
|
||||
if parsed, err := time.ParseDuration(value); err == nil {
|
||||
//nolint:govet // cancelled automatically by withTimeout
|
||||
ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), parsed)
|
||||
// global timeout is overridden by annotation by replacing context
|
||||
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), parsed, ErrTimeoutAnnotationDeadline)
|
||||
// add timeout value to context
|
||||
ctx = context.WithValue(ctx, httpclientpool.WithCustomTimeout{}, httpclientpool.WithCustomTimeout{Timeout: parsed})
|
||||
request = request.Clone(ctx)
|
||||
}
|
||||
} else {
|
||||
//nolint:govet // cancelled automatically by withTimeout
|
||||
ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), httpclientpool.GetHttpTimeout(r.options.Options))
|
||||
// global timeout is overridden by annotation by replacing context
|
||||
ctx, overrides.cancelFunc = context.WithTimeoutCause(context.TODO(), httpclientpool.GetHttpTimeout(r.options.Options), ErrRequestTimeoutDeadline)
|
||||
request = request.Clone(ctx)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@ package http
|
||||
// -> request.executeGeneratedFuzzingRequest [execute final generated fuzzing request and get result]
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
@@ -121,7 +120,8 @@ func (request *Request) executeAllFuzzingRules(input *contextargs.Context, value
|
||||
}
|
||||
|
||||
err := rule.Execute(&fuzz.ExecuteRuleInput{
|
||||
Input: input,
|
||||
Input: input,
|
||||
DisplayFuzzPoints: request.options.Options.DisplayFuzzPoints,
|
||||
Callback: func(gr fuzz.GeneratedRequest) bool {
|
||||
select {
|
||||
case <-input.Context().Done():
|
||||
@@ -133,13 +133,14 @@ func (request *Request) executeAllFuzzingRules(input *contextargs.Context, value
|
||||
return request.executeGeneratedFuzzingRequest(gr, input, callback)
|
||||
},
|
||||
Values: values,
|
||||
BaseRequest: baseRequest.Clone(context.TODO()),
|
||||
BaseRequest: baseRequest.Clone(input.Context()),
|
||||
})
|
||||
if err == nil {
|
||||
applicable = true
|
||||
continue
|
||||
}
|
||||
if fuzz.IsErrRuleNotApplicable(err) {
|
||||
gologger.Verbose().Msgf("[%s] fuzz: rule not applicable : %s\n", request.options.TemplateID, err)
|
||||
continue
|
||||
}
|
||||
if err == types.ErrNoMoreRequests {
|
||||
@@ -158,7 +159,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.MetaInput.Input) {
|
||||
if request.options.HostErrorsCache != nil && request.options.HostErrorsCache.Check(input) {
|
||||
return false
|
||||
}
|
||||
request.options.RateLimitTake()
|
||||
@@ -177,6 +178,7 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
result.FuzzingPosition = gr.Component.Name()
|
||||
}
|
||||
|
||||
setInteractshCallback := false
|
||||
if hasInteractMarkers && hasInteractMatchers && request.options.Interactsh != nil {
|
||||
requestData := &interactsh.RequestData{
|
||||
MakeResultFunc: request.MakeResultEvent,
|
||||
@@ -184,7 +186,10 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
Operators: request.CompiledOperators,
|
||||
MatchFunc: request.Match,
|
||||
ExtractFunc: request.Extract,
|
||||
Parameter: gr.Parameter,
|
||||
Request: gr.Request,
|
||||
}
|
||||
setInteractshCallback = true
|
||||
request.options.Interactsh.RequestEvent(gr.InteractURLs, requestData)
|
||||
gotMatches = request.options.Interactsh.AlreadyMatched(requestData)
|
||||
} else {
|
||||
@@ -194,6 +199,13 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
if event.OperatorsResult != nil {
|
||||
gotMatches = event.OperatorsResult.Matched
|
||||
}
|
||||
if request.options.FuzzParamsFrequency != nil && !setInteractshCallback {
|
||||
if !gotMatches {
|
||||
request.options.FuzzParamsFrequency.MarkParameter(gr.Parameter, gr.Request.URL.String(), request.options.TemplateID)
|
||||
} else {
|
||||
request.options.FuzzParamsFrequency.UnmarkParameter(gr.Parameter, gr.Request.URL.String(), request.options.TemplateID)
|
||||
}
|
||||
}
|
||||
}, 0)
|
||||
// If a variable is unresolved, skip all further requests
|
||||
if errors.Is(requestErr, ErrMissingVars) {
|
||||
@@ -201,7 +213,7 @@ func (request *Request) executeGeneratedFuzzingRequest(gr fuzz.GeneratedRequest,
|
||||
}
|
||||
if requestErr != nil {
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(input.MetaInput.Input, requestErr)
|
||||
request.options.HostErrorsCache.MarkFailed(input, requestErr)
|
||||
}
|
||||
gologger.Verbose().Msgf("[%s] Error occurred in request: %s\n", request.options.TemplateID, requestErr)
|
||||
}
|
||||
|
||||
@@ -32,6 +32,7 @@ import (
|
||||
protocolutils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
iputil "github.com/projectdiscovery/utils/ip"
|
||||
syncutil "github.com/projectdiscovery/utils/sync"
|
||||
@@ -360,7 +361,7 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV
|
||||
}
|
||||
|
||||
if request.generator != nil && request.Threads > 1 {
|
||||
request.executeRequestParallel(context.Background(), hostPort, hostname, input, payloadValues, callback)
|
||||
request.executeRequestParallel(target.Context(), hostPort, hostname, input, payloadValues, callback)
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -387,11 +388,10 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, dynamicV
|
||||
}
|
||||
callback(result)
|
||||
}, requestOptions); err != nil {
|
||||
_ = err
|
||||
// Review: should we log error here?
|
||||
// it is technically not error as it is expected to fail
|
||||
// gologger.Warning().Msgf("Could not execute request: %s\n", err)
|
||||
// do not return even if error occured
|
||||
if errkit.IsNetworkPermanentErr(err) {
|
||||
// gologger.Verbose().Msgf("Could not execute request: %s\n", err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
// If this was a match, and we want to stop at first match, skip all further requests.
|
||||
shouldStopAtFirstMatch := request.options.Options.StopAtFirstMatch || request.StopAtFirstMatch
|
||||
@@ -408,8 +408,8 @@ func (request *Request) executeRequestParallel(ctxParent context.Context, hostPo
|
||||
if threads == 0 {
|
||||
threads = 1
|
||||
}
|
||||
ctx, cancel := context.WithCancel(ctxParent)
|
||||
defer cancel()
|
||||
ctx, cancel := context.WithCancelCause(ctxParent)
|
||||
defer cancel(nil)
|
||||
requestOptions := request.options
|
||||
gotmatches := &atomic.Bool{}
|
||||
|
||||
@@ -453,16 +453,15 @@ func (request *Request) executeRequestParallel(ctxParent context.Context, hostPo
|
||||
}
|
||||
callback(result)
|
||||
}, requestOptions); err != nil {
|
||||
_ = err
|
||||
// Review: should we log error here?
|
||||
// it is technically not error as it is expected to fail
|
||||
// gologger.Warning().Msgf("Could not execute request: %s\n", err)
|
||||
// do not return even if error occured
|
||||
if errkit.IsNetworkPermanentErr(err) {
|
||||
cancel(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
// If this was a match, and we want to stop at first match, skip all further requests.
|
||||
|
||||
if shouldStopAtFirstMatch && gotmatches.Load() {
|
||||
cancel()
|
||||
cancel(nil)
|
||||
return
|
||||
}
|
||||
}()
|
||||
@@ -507,7 +506,6 @@ func (request *Request) executeRequestWithPayloads(hostPort string, input *conte
|
||||
results = compiler.ExecuteResult{"success": false, "error": err.Error()}
|
||||
}
|
||||
request.options.Progress.IncrementRequests()
|
||||
|
||||
requestOptions.Output.Request(requestOptions.TemplateID, hostPort, request.Type().String(), err)
|
||||
gologger.Verbose().Msgf("[%s] Sent Javascript request to %s", request.options.TemplateID, hostPort)
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package network
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
@@ -64,7 +63,7 @@ func (request *Request) getOpenPorts(target *contextargs.Context) ([]string, err
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
}
|
||||
conn, err := protocolstate.Dialer.Dial(context.TODO(), "tcp", addr)
|
||||
conn, err := protocolstate.Dialer.Dial(target.Context(), "tcp", addr)
|
||||
if err != nil {
|
||||
errs = append(errs, err)
|
||||
continue
|
||||
@@ -119,6 +118,10 @@ func (request *Request) ExecuteWithResults(target *contextargs.Context, metadata
|
||||
func (request *Request) executeOnTarget(input *contextargs.Context, visited mapsutil.Map[string, struct{}], metadata, previous output.InternalEvent, callback protocols.OutputEventCallback) error {
|
||||
var address string
|
||||
var err error
|
||||
if request.isUnresponsiveAddress(input) {
|
||||
// skip on unresponsive address no need to continue
|
||||
return nil
|
||||
}
|
||||
|
||||
if request.SelfContained {
|
||||
address = ""
|
||||
@@ -173,6 +176,8 @@ func (request *Request) executeAddress(variables map[string]interface{}, actualA
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return err
|
||||
}
|
||||
updatedTarget := input.Clone()
|
||||
updatedTarget.MetaInput.Input = actualAddress
|
||||
|
||||
// if request threads matches global payload concurrency we follow it
|
||||
shouldFollowGlobal := request.Threads == request.options.Options.PayloadConcurrency
|
||||
@@ -206,11 +211,19 @@ func (request *Request) executeAddress(variables map[string]interface{}, actualA
|
||||
m.Unlock()
|
||||
}
|
||||
}
|
||||
if request.isUnresponsiveAddress(updatedTarget) {
|
||||
// skip on unresponsive address no need to continue
|
||||
return nil
|
||||
}
|
||||
|
||||
value = generators.MergeMaps(value, payloads)
|
||||
swg.Add()
|
||||
go func(vars map[string]interface{}) {
|
||||
defer swg.Done()
|
||||
if request.isUnresponsiveAddress(updatedTarget) {
|
||||
// skip on unresponsive address no need to continue
|
||||
return
|
||||
}
|
||||
if err := request.executeRequestWithPayloads(variables, actualAddress, address, input, shouldUseTLS, vars, previous, callback); err != nil {
|
||||
m.Lock()
|
||||
multiErr = multierr.Append(multiErr, err)
|
||||
@@ -240,13 +253,22 @@ func (request *Request) executeRequestWithPayloads(variables map[string]interfac
|
||||
if host, _, err := net.SplitHostPort(actualAddress); err == nil {
|
||||
hostname = host
|
||||
}
|
||||
updatedTarget := input.Clone()
|
||||
updatedTarget.MetaInput.Input = actualAddress
|
||||
|
||||
if request.isUnresponsiveAddress(updatedTarget) {
|
||||
// skip on unresponsive address no need to continue
|
||||
return nil
|
||||
}
|
||||
|
||||
if shouldUseTLS {
|
||||
conn, err = request.dialer.DialTLS(context.Background(), "tcp", actualAddress)
|
||||
conn, err = request.dialer.DialTLS(input.Context(), "tcp", actualAddress)
|
||||
} else {
|
||||
conn, err = request.dialer.Dial(context.Background(), "tcp", actualAddress)
|
||||
conn, err = request.dialer.Dial(input.Context(), "tcp", actualAddress)
|
||||
}
|
||||
if err != nil {
|
||||
// adds it to unresponsive address list if applicable
|
||||
request.markUnresponsiveAddress(updatedTarget, err)
|
||||
request.options.Output.Request(request.options.TemplatePath, address, request.Type().String(), err)
|
||||
request.options.Progress.IncrementFailedRequestsBy(1)
|
||||
return errors.Wrap(err, "could not connect to server")
|
||||
@@ -475,3 +497,21 @@ func ConnReadNWithTimeout(conn net.Conn, n int64, timeout time.Duration) ([]byte
|
||||
}
|
||||
return b[:count], nil
|
||||
}
|
||||
|
||||
// markUnresponsiveAddress checks if the error is a unreponsive host error and marks it
|
||||
func (request *Request) markUnresponsiveAddress(input *contextargs.Context, err error) {
|
||||
if err == nil {
|
||||
return
|
||||
}
|
||||
if request.options.HostErrorsCache != nil {
|
||||
request.options.HostErrorsCache.MarkFailed(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 false
|
||||
}
|
||||
@@ -17,11 +17,12 @@ import (
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/utils/conversion"
|
||||
syncutil "github.com/projectdiscovery/utils/sync"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
var _ protocols.Request = &Request{}
|
||||
|
||||
const maxSize = 5 * 1024 * 1024
|
||||
const maxSize = 5 * unitutils.Mega
|
||||
|
||||
// Type returns the type of the protocol request
|
||||
func (request *Request) Type() templateTypes.ProtocolType {
|
||||
|
||||
@@ -13,6 +13,7 @@ import (
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/authprovider"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/fuzz/frequency"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/input"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/js/compiler"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/loader/parser"
|
||||
@@ -33,10 +34,11 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
templateTypes "github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
var (
|
||||
MaxTemplateFileSizeForEncoding = 1024 * 1024
|
||||
MaxTemplateFileSizeForEncoding = unitutils.Mega
|
||||
)
|
||||
|
||||
// Executer is an interface implemented any protocol based request executer.
|
||||
@@ -92,6 +94,8 @@ type ExecutorOptions struct {
|
||||
ExcludeMatchers *excludematchers.ExcludeMatchers
|
||||
// InputHelper is a helper for input normalization
|
||||
InputHelper *input.Helper
|
||||
// FuzzParamsFrequency is a cache for parameter frequency
|
||||
FuzzParamsFrequency *frequency.Tracker
|
||||
|
||||
Operators []*operators.Operators // only used by offlinehttp module
|
||||
|
||||
|
||||
@@ -3,8 +3,10 @@ package ssl
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/cespare/xxhash"
|
||||
"github.com/fatih/structs"
|
||||
jsoniter "github.com/json-iterator/go"
|
||||
"github.com/pkg/errors"
|
||||
@@ -99,15 +101,15 @@ type Request struct {
|
||||
options *protocols.ExecutorOptions
|
||||
}
|
||||
|
||||
// CanCluster returns true if the request can be clustered.
|
||||
func (request *Request) CanCluster(other *Request) bool {
|
||||
if len(request.CipherSuites) > 0 || request.MinVersion != "" || request.MaxVersion != "" {
|
||||
return false
|
||||
}
|
||||
if request.Address != other.Address || request.ScanMode != other.ScanMode {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
// TmplClusterKey generates a unique key for the request
|
||||
// to be used in the clustering process.
|
||||
func (request *Request) TmplClusterKey() uint64 {
|
||||
inp := fmt.Sprintf("%s-%s-%t-%t-%s", request.Address, request.ScanMode, request.TLSCiphersEnum, request.TLSVersionsEnum, strings.Join(request.TLSCipherTypes, ","))
|
||||
return xxhash.Sum64String(inp)
|
||||
}
|
||||
|
||||
func (request *Request) IsClusterable() bool {
|
||||
return !(len(request.CipherSuites) > 0 || request.MinVersion != "" || request.MaxVersion != "")
|
||||
}
|
||||
|
||||
// Compile compiles the request generators preparing any requests possible.
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package websocket
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"io"
|
||||
@@ -228,7 +227,7 @@ func (request *Request) executeRequestWithPayloads(target *contextargs.Context,
|
||||
parsedAddress.Path = path.Join(parsedAddress.Path, parsed.Path)
|
||||
addressToDial = parsedAddress.String()
|
||||
|
||||
conn, readBuffer, _, err := websocketDialer.Dial(context.Background(), addressToDial)
|
||||
conn, readBuffer, _, err := websocketDialer.Dial(target.Context(), addressToDial)
|
||||
if err != nil {
|
||||
requestOptions.Output.Request(requestOptions.TemplateID, input, request.Type().String(), err)
|
||||
requestOptions.Progress.IncrementFailedRequestsBy(1)
|
||||
|
||||
@@ -12,6 +12,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/exporters/markdown/util"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
// Summary returns a formatted built one line summary of the event
|
||||
@@ -71,7 +72,7 @@ func CreateReportDescription(event *output.ResultEvent, formatter ResultFormatte
|
||||
if event.Response != "" {
|
||||
var responseString string
|
||||
// If the response is larger than 5 kb, truncate it before writing.
|
||||
maxKbSize := 5 * 1024
|
||||
maxKbSize := 5 * unitutils.Kilo
|
||||
if len(event.Response) > maxKbSize {
|
||||
responseString = event.Response[:maxKbSize]
|
||||
responseString += ".... Truncated ...."
|
||||
|
||||
@@ -273,9 +273,10 @@ func (c *ReportingClient) CreateIssue(event *output.ResultEvent) error {
|
||||
|
||||
for _, tracker := range c.trackers {
|
||||
// process tracker specific allow/deny list
|
||||
if tracker.ShouldFilter(event) {
|
||||
if !tracker.ShouldFilter(event) {
|
||||
continue
|
||||
}
|
||||
|
||||
trackerName := tracker.Name()
|
||||
stats, statsOk := c.stats[trackerName]
|
||||
|
||||
|
||||
@@ -139,15 +139,15 @@ func (i *Integration) CloseIssue(event *output.ResultEvent) error {
|
||||
|
||||
// ShouldFilter determines if an issue should be logged to this tracker
|
||||
func (i *Integration) ShouldFilter(event *output.ResultEvent) bool {
|
||||
if i.options.AllowList != nil && i.options.AllowList.GetMatch(event) {
|
||||
return true
|
||||
if i.options.AllowList != nil && !i.options.AllowList.GetMatch(event) {
|
||||
return false
|
||||
}
|
||||
|
||||
if i.options.DenyList != nil && i.options.DenyList.GetMatch(event) {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *Integration) findIssueByTitle(title string) (*gitea.Issue, error) {
|
||||
|
||||
@@ -175,15 +175,15 @@ func (i *Integration) Name() string {
|
||||
|
||||
// ShouldFilter determines if an issue should be logged to this tracker
|
||||
func (i *Integration) ShouldFilter(event *output.ResultEvent) bool {
|
||||
if i.options.AllowList != nil && i.options.AllowList.GetMatch(event) {
|
||||
return true
|
||||
if i.options.AllowList != nil && !i.options.AllowList.GetMatch(event) {
|
||||
return false
|
||||
}
|
||||
|
||||
if i.options.DenyList != nil && i.options.DenyList.GetMatch(event) {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
|
||||
func (i *Integration) findIssueByTitle(ctx context.Context, title string) (*github.Issue, error) {
|
||||
|
||||
@@ -164,13 +164,13 @@ func (i *Integration) CloseIssue(event *output.ResultEvent) error {
|
||||
|
||||
// ShouldFilter determines if an issue should be logged to this tracker
|
||||
func (i *Integration) ShouldFilter(event *output.ResultEvent) bool {
|
||||
if i.options.AllowList != nil && i.options.AllowList.GetMatch(event) {
|
||||
return true
|
||||
if i.options.AllowList != nil && !i.options.AllowList.GetMatch(event) {
|
||||
return false
|
||||
}
|
||||
|
||||
if i.options.DenyList != nil && i.options.DenyList.GetMatch(event) {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/format"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/filters"
|
||||
"github.com/projectdiscovery/retryablehttp-go"
|
||||
"github.com/projectdiscovery/utils/ptr"
|
||||
)
|
||||
|
||||
type Formatter struct {
|
||||
@@ -147,13 +148,13 @@ func (i *Integration) CreateNewIssue(event *output.ResultEvent) (*filters.Create
|
||||
nestedValue = strings.TrimPrefix(fmtNestedValue, "$")
|
||||
switch nestedValue {
|
||||
case "CVSSMetrics":
|
||||
nestedValue = event.Info.Classification.CVSSMetrics
|
||||
nestedValue = ptr.Safe(event.Info.Classification).CVSSMetrics
|
||||
case "CVEID":
|
||||
nestedValue = event.Info.Classification.CVEID
|
||||
nestedValue = ptr.Safe(event.Info.Classification).CVEID
|
||||
case "CWEID":
|
||||
nestedValue = event.Info.Classification.CWEID
|
||||
nestedValue = ptr.Safe(event.Info.Classification).CWEID
|
||||
case "CVSSScore":
|
||||
nestedValue = event.Info.Classification.CVSSScore
|
||||
nestedValue = ptr.Safe(event.Info.Classification).CVSSScore
|
||||
case "Host":
|
||||
nestedValue = event.Host
|
||||
case "Severity":
|
||||
@@ -314,13 +315,13 @@ func (i *Integration) FindExistingIssue(event *output.ResultEvent) (jira.Issue,
|
||||
|
||||
// ShouldFilter determines if an issue should be logged to this tracker
|
||||
func (i *Integration) ShouldFilter(event *output.ResultEvent) bool {
|
||||
if i.options.AllowList != nil && i.options.AllowList.GetMatch(event) {
|
||||
return true
|
||||
if i.options.AllowList != nil && !i.options.AllowList.GetMatch(event) {
|
||||
return false
|
||||
}
|
||||
|
||||
if i.options.DenyList != nil && i.options.DenyList.GetMatch(event) {
|
||||
return true
|
||||
return false
|
||||
}
|
||||
|
||||
return false
|
||||
return true
|
||||
}
|
||||
@@ -4,6 +4,10 @@ import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/output"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/reporting/trackers/filters"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -36,3 +40,33 @@ func TestTableCreation(t *testing.T) {
|
||||
`
|
||||
require.Equal(t, expected, table)
|
||||
}
|
||||
|
||||
func Test_ShouldFilter_Tracker(t *testing.T) {
|
||||
jiraIntegration := &Integration{
|
||||
options: &Options{AllowList: &filters.Filter{
|
||||
Severities: severity.Severities{severity.Critical},
|
||||
}},
|
||||
}
|
||||
|
||||
require.False(t, jiraIntegration.ShouldFilter(&output.ResultEvent{Info: model.Info{
|
||||
SeverityHolder: severity.Holder{Severity: severity.Info},
|
||||
}}))
|
||||
require.True(t, jiraIntegration.ShouldFilter(&output.ResultEvent{Info: model.Info{
|
||||
SeverityHolder: severity.Holder{Severity: severity.Critical},
|
||||
}}))
|
||||
|
||||
t.Run("deny-list", func(t *testing.T) {
|
||||
jiraIntegration := &Integration{
|
||||
options: &Options{DenyList: &filters.Filter{
|
||||
Severities: severity.Severities{severity.Critical},
|
||||
}},
|
||||
}
|
||||
|
||||
require.True(t, jiraIntegration.ShouldFilter(&output.ResultEvent{Info: model.Info{
|
||||
SeverityHolder: severity.Holder{Severity: severity.Info},
|
||||
}}))
|
||||
require.False(t, jiraIntegration.ShouldFilter(&output.ResultEvent{Info: model.Info{
|
||||
SeverityHolder: severity.Holder{Severity: severity.Critical},
|
||||
}}))
|
||||
})
|
||||
}
|
||||
+46
-70
@@ -14,7 +14,6 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
cryptoutil "github.com/projectdiscovery/utils/crypto"
|
||||
mapsutil "github.com/projectdiscovery/utils/maps"
|
||||
)
|
||||
|
||||
// Cluster clusters a list of templates into a lesser number if possible based
|
||||
@@ -42,91 +41,68 @@ import (
|
||||
// Finally, the engine creates a single executer with a clusteredexecuter for all templates
|
||||
// in a cluster.
|
||||
func Cluster(list []*Template) [][]*Template {
|
||||
http := make(map[uint64][]*Template)
|
||||
dns := make(map[uint64][]*Template)
|
||||
ssl := make(map[uint64][]*Template)
|
||||
|
||||
final := [][]*Template{}
|
||||
skip := mapsutil.NewSyncLockMap[string, struct{}]()
|
||||
|
||||
// Split up templates that might be clusterable
|
||||
for _, template := range list {
|
||||
key := template.Path
|
||||
|
||||
if skip.Has(key) {
|
||||
continue
|
||||
}
|
||||
|
||||
// We only cluster http, dns and ssl requests as of now.
|
||||
// Take care of requests that can't be clustered first.
|
||||
if len(template.RequestsHTTP) == 0 && len(template.RequestsDNS) == 0 && len(template.RequestsSSL) == 0 {
|
||||
_ = skip.Set(key, struct{}{})
|
||||
final = append(final, []*Template{template})
|
||||
continue
|
||||
}
|
||||
|
||||
// it is not possible to cluster flow and multiprotocol due to dependent execution
|
||||
if template.Flow != "" || template.Options.IsMultiProtocol {
|
||||
_ = skip.Set(key, struct{}{})
|
||||
final = append(final, []*Template{template})
|
||||
continue
|
||||
}
|
||||
|
||||
_ = skip.Set(key, struct{}{})
|
||||
|
||||
var templateType types.ProtocolType
|
||||
switch {
|
||||
case len(template.RequestsDNS) == 1:
|
||||
templateType = types.DNSProtocol
|
||||
if template.RequestsDNS[0].IsClusterable() {
|
||||
hash := template.RequestsDNS[0].TmplClusterKey()
|
||||
if dns[hash] == nil {
|
||||
dns[hash] = []*Template{}
|
||||
}
|
||||
dns[hash] = append(dns[hash], template)
|
||||
} else {
|
||||
final = append(final, []*Template{template})
|
||||
}
|
||||
|
||||
case len(template.RequestsHTTP) == 1:
|
||||
templateType = types.HTTPProtocol
|
||||
if template.RequestsHTTP[0].IsClusterable() {
|
||||
hash := template.RequestsHTTP[0].TmplClusterKey()
|
||||
if http[hash] == nil {
|
||||
http[hash] = []*Template{}
|
||||
}
|
||||
http[hash] = append(http[hash], template)
|
||||
} else {
|
||||
final = append(final, []*Template{template})
|
||||
}
|
||||
case len(template.RequestsSSL) == 1:
|
||||
templateType = types.SSLProtocol
|
||||
}
|
||||
|
||||
// Find any/all similar matching request that is identical to
|
||||
// this one and cluster them together for http protocol only.
|
||||
cluster := []*Template{}
|
||||
for _, other := range list {
|
||||
otherKey := other.Path
|
||||
|
||||
if skip.Has(otherKey) {
|
||||
continue
|
||||
}
|
||||
|
||||
// it is not possible to cluster flow and multiprotocol due to dependent execution
|
||||
if other.Flow != "" || other.Options.IsMultiProtocol {
|
||||
_ = skip.Set(otherKey, struct{}{})
|
||||
final = append(final, []*Template{other})
|
||||
continue
|
||||
}
|
||||
|
||||
switch templateType {
|
||||
case types.DNSProtocol:
|
||||
if len(other.RequestsDNS) != 1 {
|
||||
continue
|
||||
} else if template.RequestsDNS[0].CanCluster(other.RequestsDNS[0]) {
|
||||
_ = skip.Set(otherKey, struct{}{})
|
||||
cluster = append(cluster, other)
|
||||
}
|
||||
case types.HTTPProtocol:
|
||||
if len(other.RequestsHTTP) != 1 {
|
||||
continue
|
||||
} else if template.RequestsHTTP[0].CanCluster(other.RequestsHTTP[0]) {
|
||||
_ = skip.Set(otherKey, struct{}{})
|
||||
cluster = append(cluster, other)
|
||||
}
|
||||
case types.SSLProtocol:
|
||||
if len(other.RequestsSSL) != 1 {
|
||||
continue
|
||||
} else if template.RequestsSSL[0].CanCluster(other.RequestsSSL[0]) {
|
||||
_ = skip.Set(otherKey, struct{}{})
|
||||
cluster = append(cluster, other)
|
||||
if template.RequestsSSL[0].IsClusterable() {
|
||||
hash := template.RequestsSSL[0].TmplClusterKey()
|
||||
if ssl[hash] == nil {
|
||||
ssl[hash] = []*Template{}
|
||||
}
|
||||
ssl[hash] = append(ssl[hash], template)
|
||||
} else {
|
||||
final = append(final, []*Template{template})
|
||||
}
|
||||
}
|
||||
if len(cluster) > 0 {
|
||||
cluster = append(cluster, template)
|
||||
final = append(final, cluster)
|
||||
} else {
|
||||
default:
|
||||
final = append(final, []*Template{template})
|
||||
}
|
||||
}
|
||||
|
||||
// add all clusterd templates
|
||||
for _, templates := range http {
|
||||
final = append(final, templates)
|
||||
}
|
||||
for _, templates := range dns {
|
||||
final = append(final, templates)
|
||||
}
|
||||
for _, templates := range ssl {
|
||||
final = append(final, templates)
|
||||
}
|
||||
|
||||
return final
|
||||
}
|
||||
|
||||
@@ -296,7 +272,7 @@ func (e *ClusterExecuter) Execute(ctx *scan.ScanContext) (bool, error) {
|
||||
}
|
||||
})
|
||||
if err != nil && e.options.HostErrorsCache != nil {
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.Input, err)
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
}
|
||||
return results, err
|
||||
}
|
||||
@@ -330,7 +306,7 @@ func (e *ClusterExecuter) ExecuteWithResults(ctx *scan.ScanContext) ([]*output.R
|
||||
}
|
||||
|
||||
if err != nil && e.options.HostErrorsCache != nil {
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input.MetaInput.Input, err)
|
||||
e.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
}
|
||||
return scanCtx.GenerateResult(), err
|
||||
}
|
||||
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/projectdiscovery/gologger"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/catalog/config"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/js/compiler"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/model/types/severity"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/operators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
@@ -341,6 +342,23 @@ func parseTemplate(data []byte, options protocols.ExecutorOptions) (*Template, e
|
||||
return nil, errors.New("no template author field provided")
|
||||
}
|
||||
|
||||
numberOfWorkflows := len(template.Workflows)
|
||||
if numberOfWorkflows > 0 && numberOfWorkflows != template.Requests() {
|
||||
return nil, errors.New("workflows cannot have other protocols")
|
||||
}
|
||||
|
||||
// use default unknown severity
|
||||
if len(template.Workflows) == 0 {
|
||||
if template.Info.SeverityHolder.Severity == severity.Undefined {
|
||||
// set unknown severity with counter and forced warning
|
||||
template.Info.SeverityHolder.Severity = severity.Unknown
|
||||
if options.Options.Validate {
|
||||
// when validating return error
|
||||
return nil, errors.New("no template severity field provided")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Setting up variables regarding template metadata
|
||||
options.TemplateID = template.ID
|
||||
options.TemplateInfo = template.Info
|
||||
|
||||
@@ -197,3 +197,12 @@ func Test_WrongTemplate(t *testing.T) {
|
||||
require.Nil(t, got, "could not parse template")
|
||||
require.ErrorContains(t, err, "no requests defined ")
|
||||
}
|
||||
|
||||
func TestWrongWorkflow(t *testing.T) {
|
||||
setup()
|
||||
|
||||
filePath := "tests/workflow-invalid.yaml"
|
||||
got, err := templates.Parse(filePath, nil, executerOpts)
|
||||
require.Nil(t, got, "could not parse template")
|
||||
require.ErrorContains(t, err, "workflows cannot have other protocols")
|
||||
}
|
||||
@@ -23,6 +23,7 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/websocket"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/whois"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/workflows"
|
||||
errorutil "github.com/projectdiscovery/utils/errors"
|
||||
fileutil "github.com/projectdiscovery/utils/file"
|
||||
@@ -176,8 +177,6 @@ func (template *Template) Type() types.ProtocolType {
|
||||
return types.HeadlessProtocol
|
||||
case len(template.RequestsNetwork) > 0:
|
||||
return types.NetworkProtocol
|
||||
case len(template.Workflow.Workflows) > 0:
|
||||
return types.WorkflowProtocol
|
||||
case len(template.RequestsSSL) > 0:
|
||||
return types.SSLProtocol
|
||||
case len(template.RequestsWebsocket) > 0:
|
||||
@@ -188,6 +187,8 @@ func (template *Template) Type() types.ProtocolType {
|
||||
return types.CodeProtocol
|
||||
case len(template.RequestsJavascript) > 0:
|
||||
return types.JavascriptProtocol
|
||||
case len(template.Workflow.Workflows) > 0:
|
||||
return types.WorkflowProtocol
|
||||
default:
|
||||
return types.InvalidProtocol
|
||||
}
|
||||
@@ -326,6 +327,17 @@ func (template *Template) UnmarshalYAML(unmarshal func(interface{}) error) error
|
||||
}
|
||||
*template = Template(*alias)
|
||||
|
||||
if !ReTemplateID.MatchString(template.ID) {
|
||||
return errorutil.New("template id must match expression %v", ReTemplateID).WithTag("invalid template")
|
||||
}
|
||||
info := template.Info
|
||||
if utils.IsBlank(info.Name) {
|
||||
return errorutil.New("no template name field provided").WithTag("invalid template")
|
||||
}
|
||||
if info.Authors.IsEmpty() {
|
||||
return errorutil.New("no template author field provided").WithTag("invalid template")
|
||||
}
|
||||
|
||||
if len(template.RequestsHTTP) > 0 || len(template.RequestsNetwork) > 0 {
|
||||
_ = deprecatedProtocolNameTemplates.Set(template.ID, true)
|
||||
}
|
||||
|
||||
@@ -747,7 +747,7 @@ func init() {
|
||||
FieldName: "fuzzing",
|
||||
},
|
||||
}
|
||||
FUZZRuleDoc.Fields = make([]encoder.Doc, 8)
|
||||
FUZZRuleDoc.Fields = make([]encoder.Doc, 9)
|
||||
FUZZRuleDoc.Fields[0].Name = "type"
|
||||
FUZZRuleDoc.Fields[0].Type = "string"
|
||||
FUZZRuleDoc.Fields[0].Note = ""
|
||||
@@ -762,51 +762,69 @@ func init() {
|
||||
FUZZRuleDoc.Fields[1].Name = "part"
|
||||
FUZZRuleDoc.Fields[1].Type = "string"
|
||||
FUZZRuleDoc.Fields[1].Note = ""
|
||||
FUZZRuleDoc.Fields[1].Description = "Part is the part of request to fuzz.\n\nquery fuzzes the query part of url. More parts will be added later."
|
||||
FUZZRuleDoc.Fields[1].Description = "Part is the part of request to fuzz."
|
||||
FUZZRuleDoc.Fields[1].Comments[encoder.LineComment] = "Part is the part of request to fuzz."
|
||||
FUZZRuleDoc.Fields[1].Values = []string{
|
||||
"query",
|
||||
"header",
|
||||
"path",
|
||||
"body",
|
||||
"cookie",
|
||||
"request",
|
||||
}
|
||||
FUZZRuleDoc.Fields[2].Name = "mode"
|
||||
FUZZRuleDoc.Fields[2].Type = "string"
|
||||
FUZZRuleDoc.Fields[2].Name = "parts"
|
||||
FUZZRuleDoc.Fields[2].Type = "[]string"
|
||||
FUZZRuleDoc.Fields[2].Note = ""
|
||||
FUZZRuleDoc.Fields[2].Description = "Mode is the mode of fuzzing to perform.\n\nsingle fuzzes one value at a time. multiple fuzzes all values at same time."
|
||||
FUZZRuleDoc.Fields[2].Comments[encoder.LineComment] = "Mode is the mode of fuzzing to perform."
|
||||
FUZZRuleDoc.Fields[2].Description = "Parts is the list of parts to fuzz. If multiple parts need to be\ndefined while excluding some, this should be used instead of singular part."
|
||||
FUZZRuleDoc.Fields[2].Comments[encoder.LineComment] = "Parts is the list of parts to fuzz. If multiple parts need to be"
|
||||
FUZZRuleDoc.Fields[2].Values = []string{
|
||||
"query",
|
||||
"header",
|
||||
"path",
|
||||
"body",
|
||||
"cookie",
|
||||
"request",
|
||||
}
|
||||
FUZZRuleDoc.Fields[3].Name = "mode"
|
||||
FUZZRuleDoc.Fields[3].Type = "string"
|
||||
FUZZRuleDoc.Fields[3].Note = ""
|
||||
FUZZRuleDoc.Fields[3].Description = "Mode is the mode of fuzzing to perform.\n\nsingle fuzzes one value at a time. multiple fuzzes all values at same time."
|
||||
FUZZRuleDoc.Fields[3].Comments[encoder.LineComment] = "Mode is the mode of fuzzing to perform."
|
||||
FUZZRuleDoc.Fields[3].Values = []string{
|
||||
"single",
|
||||
"multiple",
|
||||
}
|
||||
FUZZRuleDoc.Fields[3].Name = "keys"
|
||||
FUZZRuleDoc.Fields[3].Type = "[]string"
|
||||
FUZZRuleDoc.Fields[3].Note = ""
|
||||
FUZZRuleDoc.Fields[3].Description = "Keys is the optional list of key named parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[3].Comments[encoder.LineComment] = "Keys is the optional list of key named parameters to fuzz."
|
||||
|
||||
FUZZRuleDoc.Fields[3].AddExample("Examples of keys", []string{"url", "file", "host"})
|
||||
FUZZRuleDoc.Fields[4].Name = "keys-regex"
|
||||
FUZZRuleDoc.Fields[4].Name = "keys"
|
||||
FUZZRuleDoc.Fields[4].Type = "[]string"
|
||||
FUZZRuleDoc.Fields[4].Note = ""
|
||||
FUZZRuleDoc.Fields[4].Description = "KeysRegex is the optional list of regex key parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[4].Comments[encoder.LineComment] = "KeysRegex is the optional list of regex key parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[4].Description = "Keys is the optional list of key named parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[4].Comments[encoder.LineComment] = "Keys is the optional list of key named parameters to fuzz."
|
||||
|
||||
FUZZRuleDoc.Fields[4].AddExample("Examples of key regex", []string{"url.*"})
|
||||
FUZZRuleDoc.Fields[5].Name = "values"
|
||||
FUZZRuleDoc.Fields[4].AddExample("Examples of keys", []string{"url", "file", "host"})
|
||||
FUZZRuleDoc.Fields[5].Name = "keys-regex"
|
||||
FUZZRuleDoc.Fields[5].Type = "[]string"
|
||||
FUZZRuleDoc.Fields[5].Note = ""
|
||||
FUZZRuleDoc.Fields[5].Description = "Values is the optional list of regex value parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[5].Comments[encoder.LineComment] = "Values is the optional list of regex value parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[5].Description = "KeysRegex is the optional list of regex key parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[5].Comments[encoder.LineComment] = "KeysRegex is the optional list of regex key parameters to fuzz."
|
||||
|
||||
FUZZRuleDoc.Fields[5].AddExample("Examples of value regex", []string{"https?://.*"})
|
||||
FUZZRuleDoc.Fields[6].Name = "fuzz"
|
||||
FUZZRuleDoc.Fields[6].Type = "SliceOrMapSlice"
|
||||
FUZZRuleDoc.Fields[5].AddExample("Examples of key regex", []string{"url.*"})
|
||||
FUZZRuleDoc.Fields[6].Name = "values"
|
||||
FUZZRuleDoc.Fields[6].Type = "[]string"
|
||||
FUZZRuleDoc.Fields[6].Note = ""
|
||||
FUZZRuleDoc.Fields[6].Description = "description: |\n Fuzz is the list of payloads to perform substitutions with.\n examples:\n - name: Examples of fuzz\n value: >\n []string{\"{{ssrf}}\", \"{{interactsh-url}}\", \"example-value\"}\n or\n x-header: 1\n x-header: 2"
|
||||
FUZZRuleDoc.Fields[6].Comments[encoder.LineComment] = " description: |"
|
||||
FUZZRuleDoc.Fields[7].Name = "replace-regex"
|
||||
FUZZRuleDoc.Fields[7].Type = "string"
|
||||
FUZZRuleDoc.Fields[6].Description = "Values is the optional list of regex value parameters to fuzz."
|
||||
FUZZRuleDoc.Fields[6].Comments[encoder.LineComment] = "Values is the optional list of regex value parameters to fuzz."
|
||||
|
||||
FUZZRuleDoc.Fields[6].AddExample("Examples of value regex", []string{"https?://.*"})
|
||||
FUZZRuleDoc.Fields[7].Name = "fuzz"
|
||||
FUZZRuleDoc.Fields[7].Type = "SliceOrMapSlice"
|
||||
FUZZRuleDoc.Fields[7].Note = ""
|
||||
FUZZRuleDoc.Fields[7].Description = "replace-regex is regex for regex-replace rule type\nit is only required for replace-regex rule type"
|
||||
FUZZRuleDoc.Fields[7].Comments[encoder.LineComment] = "replace-regex is regex for regex-replace rule type"
|
||||
FUZZRuleDoc.Fields[7].Description = "description: |\n Fuzz is the list of payloads to perform substitutions with.\n examples:\n - name: Examples of fuzz\n value: >\n []string{\"{{ssrf}}\", \"{{interactsh-url}}\", \"example-value\"}\n or\n x-header: 1\n x-header: 2"
|
||||
FUZZRuleDoc.Fields[7].Comments[encoder.LineComment] = " description: |"
|
||||
FUZZRuleDoc.Fields[8].Name = "replace-regex"
|
||||
FUZZRuleDoc.Fields[8].Type = "string"
|
||||
FUZZRuleDoc.Fields[8].Note = ""
|
||||
FUZZRuleDoc.Fields[8].Description = "replace-regex is regex for regex-replace rule type\nit is only required for replace-regex rule type"
|
||||
FUZZRuleDoc.Fields[8].Comments[encoder.LineComment] = "replace-regex is regex for regex-replace rule type"
|
||||
|
||||
SliceOrMapSliceDoc.Type = "SliceOrMapSlice"
|
||||
SliceOrMapSliceDoc.Comments[encoder.LineComment] = ""
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
id: workflow-example
|
||||
|
||||
info:
|
||||
name: Test Invalid Workflow Template
|
||||
author: pdteam
|
||||
severity: info
|
||||
|
||||
http:
|
||||
- raw:
|
||||
- |
|
||||
POST /re HTTP/1.1
|
||||
Host: {{Hostname}}
|
||||
|
||||
{{code_response}}
|
||||
|
||||
workflows:
|
||||
- template: tests/match-1.yaml
|
||||
- template: tests/match-1.yaml
|
||||
@@ -27,6 +27,7 @@ func GetPlaygroundServer() *echo.Echo {
|
||||
e.GET("/request", requestHandler)
|
||||
e.GET("/email", emailHandler)
|
||||
e.GET("/permissions", permissionsHandler)
|
||||
|
||||
e.GET("/blog/post", numIdorHandler) // for num based idors like ?id=44
|
||||
e.POST("/reset-password", resetPasswordHandler)
|
||||
e.GET("/host-header-lab", hostHeaderLabHandler)
|
||||
@@ -47,13 +48,20 @@ var bodyTemplate = `<html>
|
||||
|
||||
func indexHandler(ctx echo.Context) error {
|
||||
return ctx.HTML(200, fmt.Sprintf(bodyTemplate, `<h1>Fuzzing Playground</h1><hr>
|
||||
<ul>
|
||||
<li><a href="/info?name=test&another=value&random=data">Info Page XSS</a></li>
|
||||
<li><a href="/redirect?redirect_url=/info?name=redirected_from_url">Redirect Page OpenRedirect</a></li>
|
||||
<li><a href="/request?url=https://example.com">Request Page SSRF</a></li>
|
||||
<li><a href="/email?text=important_user">Email Page SSTI</a></li>
|
||||
<li><a href="/permissions?cmd=whoami">Permissions Page CMDI</a></li>
|
||||
</ul>
|
||||
<ul>
|
||||
|
||||
<li><a href="/info?name=test&another=value&random=data">Info Page XSS</a></li>
|
||||
<li><a href="/redirect?redirect_url=/info?name=redirected_from_url">Redirect Page OpenRedirect</a></li>
|
||||
<li><a href="/request?url=https://example.com">Request Page SSRF</a></li>
|
||||
<li><a href="/email?text=important_user">Email Page SSTI</a></li>
|
||||
<li><a href="/permissions?cmd=whoami">Permissions Page CMDI</a></li>
|
||||
|
||||
<li><a href="/host-header-lab">Host Header Lab (X-Forwarded-Host Trusted)</a></li>
|
||||
<li><a href="/user/75/profile">User Profile Page SQLI (path parameter)</a></li>
|
||||
<li><a href="/user">POST on /user SQLI (body parameter)</a></li>
|
||||
<li><a href="/blog/posts">SQLI in cookie lang parameter value (eg. lang=en)</a></li>
|
||||
|
||||
</ul>
|
||||
`))
|
||||
}
|
||||
|
||||
|
||||
@@ -20,13 +20,16 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/progress"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolinit"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"
|
||||
protocolUtils "github.com/projectdiscovery/nuclei/v3/pkg/protocols/utils"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/types"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/utils"
|
||||
unitutils "github.com/projectdiscovery/utils/unit"
|
||||
)
|
||||
|
||||
// Init initializes the protocols and their configurations
|
||||
func Init(options *types.Options) {
|
||||
_ = protocolstate.Init(options)
|
||||
_ = protocolinit.Init(options)
|
||||
}
|
||||
|
||||
@@ -201,7 +204,7 @@ func (m *MockOutputWriter) WriteFailure(wrappedEvent *output.InternalWrappedEven
|
||||
return m.Write(data)
|
||||
}
|
||||
|
||||
var maxTemplateFileSizeForEncoding = 1024 * 1024
|
||||
var maxTemplateFileSizeForEncoding = unitutils.Mega
|
||||
|
||||
func (w *MockOutputWriter) encodeTemplate(templatePath string) string {
|
||||
data, err := os.ReadFile(templatePath)
|
||||
|
||||
@@ -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.MetaInput.ID(), err)
|
||||
g.options.HostErrorsCache.MarkFailed(ctx.Input, err)
|
||||
}
|
||||
gologger.Warning().Msgf("[%s] Could not execute request for %s: %s\n", g.options.TemplateID, ctx.Input.MetaInput.PrettyPrint(), err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,8 @@ import (
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/generators"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/scan"
|
||||
"github.com/projectdiscovery/nuclei/v3/pkg/templates/types"
|
||||
stringsutil "github.com/projectdiscovery/utils/strings"
|
||||
)
|
||||
|
||||
// Mutliprotocol is a template executer engine that executes multiple protocols
|
||||
@@ -110,9 +112,19 @@ func (m *MultiProtocol) ExecuteWithResults(ctx *scan.ScanContext) error {
|
||||
|
||||
values := m.options.GetTemplateCtx(ctx.Input.MetaInput).GetAll()
|
||||
err := req.ExecuteWithResults(ctx.Input, output.InternalEvent(values), nil, multiProtoCallback)
|
||||
// if error skip execution of next protocols
|
||||
// in case of fatal error skip execution of next protocols
|
||||
if err != nil {
|
||||
// always log errors
|
||||
ctx.LogError(err)
|
||||
|
||||
// for some classes of protocols (i.e ssl) errors like tls handshake are a legitimate behavior so we don't stop execution
|
||||
// connection failures are already tracked by the internal host error cache
|
||||
// we use strings comparison as the error is not formalized into instance within the standard library
|
||||
// within a flow instead we consider ssl errors as fatal, since a specific logic was requested
|
||||
if req.Type() == types.SSLProtocol && stringsutil.ContainsAnyI(err.Error(), "protocol version not supported", "could not do tls handshake") {
|
||||
continue
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
package nucleierr
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/projectdiscovery/utils/errkit"
|
||||
)
|
||||
|
||||
var (
|
||||
// ErrTemplateLogic are errors that occured due to missing variable or something similar in template logic
|
||||
// so this is more of a virtual error that is expected due to template logic
|
||||
ErrTemplateLogic = errkit.NewPrimitiveErrKind("TemplateLogic", "Error expected due to template logic", isTemplateLogicKind)
|
||||
)
|
||||
|
||||
// isTemplateLogicKind checks if an error is of template logic kind
|
||||
func isTemplateLogicKind(err *errkit.ErrorX) bool {
|
||||
if err == nil || err.Cause() == nil {
|
||||
return false
|
||||
}
|
||||
v := err.Cause().Error()
|
||||
switch {
|
||||
case strings.Contains(v, "timeout annotation deadline exceeded"):
|
||||
return true
|
||||
case strings.Contains(v, "stop execution due to unresolved variables"):
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
Loaded 100 of 105 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user