mirror of
https://github.com/projectdiscovery/nuclei.git
synced 2025-12-17 19:45:28 +00:00
* introducing execution id * wip * . * adding separate execution context id * lint * vet * fixing pg dialers * test ignore * fixing loader FD limit * test * fd fix * wip: remove CloseProcesses() from dev merge * wip: fix merge issue * protocolstate: stop memguarding on last dialer delete * avoid data race in dialers.RawHTTPClient * use shared logger and avoid race conditions * use shared logger and avoid race conditions * go mod * patch executionId into compiled template cache * clean up comment in Parse * go mod update * bump echarts * address merge issues * fix use of gologger * switch cmd/nuclei to options.Logger * address merge issues with go.mod * go vet: address copy of lock with new Copy function * fixing tests * disable speed control * fix nil ExecuterOptions * removing deprecated code * fixing result print * default logger * cli default logger * filter warning from results * fix performance test * hardcoding path * disable upload * refactor(runner): uses `Warning` instead of `Print` for `pdcpUploadErrMsg` Signed-off-by: Dwi Siswanto <git@dw1.io> * Revert "disable upload" This reverts commit 114fbe6663361bf41cf8b2645fd2d57083d53682. * Revert "hardcoding path" This reverts commit cf12ca800e0a0e974bd9fd4826a24e51547f7c00. --------- Signed-off-by: Dwi Siswanto <git@dw1.io> Co-authored-by: Mzack9999 <mzack9999@protonmail.com> Co-authored-by: Dwi Siswanto <git@dw1.io> Co-authored-by: Dwi Siswanto <25837540+dwisiswant0@users.noreply.github.com>
356 lines
10 KiB
Go
356 lines
10 KiB
Go
package testutils
|
|
|
|
import (
|
|
"crypto/tls"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/gobwas/ws"
|
|
"github.com/julienschmidt/httprouter"
|
|
"github.com/projectdiscovery/utils/conversion"
|
|
)
|
|
|
|
// ExtraArgs
|
|
var (
|
|
ExtraDebugArgs = []string{}
|
|
ExtraEnvVars = []string{
|
|
"DISABLE_CLOUD_UPLOAD_WRN=true",
|
|
"DISABLE_CLOUD_UPLOAD=true",
|
|
}
|
|
)
|
|
|
|
// RunNucleiTemplateAndGetResults returns a list of results for a template
|
|
func RunNucleiTemplateAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
|
|
return RunNucleiAndGetResults(true, template, url, debug, extra...)
|
|
}
|
|
|
|
// RunNucleiWorkflowAndGetResults returns a list of results for a workflow
|
|
func RunNucleiWorkflowAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
|
|
return RunNucleiAndGetResults(false, template, url, debug, extra...)
|
|
}
|
|
|
|
func RunNucleiAndGetResults(isTemplate bool, template, url string, debug bool, extra ...string) ([]string, error) {
|
|
var templateOrWorkflowFlag string
|
|
if isTemplate {
|
|
templateOrWorkflowFlag = "-t"
|
|
} else {
|
|
templateOrWorkflowFlag = "-w"
|
|
}
|
|
|
|
return RunNucleiBareArgsAndGetResults(debug, nil, append([]string{
|
|
templateOrWorkflowFlag,
|
|
template,
|
|
"-target",
|
|
url,
|
|
}, extra...)...)
|
|
}
|
|
|
|
func RunNucleiBareArgsAndGetResults(debug bool, env []string, extra ...string) ([]string, error) {
|
|
cmd := exec.Command("./nuclei")
|
|
extra = append(extra, ExtraDebugArgs...)
|
|
cmd.Args = append(cmd.Args, extra...)
|
|
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
|
|
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
|
|
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
|
|
cmd.Args = append(cmd.Args, "-allow-local-file-access")
|
|
if env != nil {
|
|
cmd.Env = append(os.Environ(), env...)
|
|
}
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
if debug {
|
|
cmd.Args = append(cmd.Args, "-debug")
|
|
cmd.Stderr = os.Stderr
|
|
fmt.Println(cmd.String())
|
|
} else {
|
|
cmd.Args = append(cmd.Args, "-silent")
|
|
}
|
|
output, err := cmd.Output()
|
|
var data string
|
|
if len(output) > 0 {
|
|
data = strings.TrimSpace(conversion.String(output))
|
|
}
|
|
if debug {
|
|
fmt.Println(data)
|
|
}
|
|
var parts []string
|
|
items := strings.Split(data, "\n")
|
|
for _, i := range items {
|
|
if i != "" {
|
|
parts = append(parts, i)
|
|
}
|
|
}
|
|
|
|
if (data == "" || len(parts) == 0) && err != nil {
|
|
return nil, fmt.Errorf("%v: %v", err.Error(), data)
|
|
}
|
|
|
|
return parts, nil
|
|
}
|
|
|
|
// RunNucleiWithArgsAndGetResults returns result,and runtime errors
|
|
func RunNucleiWithArgsAndGetResults(debug bool, args ...string) ([]string, error) {
|
|
cmd := exec.Command("./nuclei", args...)
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
if debug {
|
|
cmd.Args = append(cmd.Args, "-debug")
|
|
cmd.Stderr = os.Stderr
|
|
fmt.Println(cmd.String())
|
|
} else {
|
|
cmd.Args = append(cmd.Args, "-silent")
|
|
}
|
|
output, err := cmd.Output()
|
|
var data string
|
|
if len(output) > 0 {
|
|
data = strings.TrimSpace(conversion.String(output))
|
|
}
|
|
if debug {
|
|
fmt.Println(data)
|
|
}
|
|
var parts []string
|
|
items := strings.Split(data, "\n")
|
|
for _, i := range items {
|
|
if i != "" {
|
|
parts = append(parts, i)
|
|
}
|
|
}
|
|
|
|
if (data == "" || len(parts) == 0) && err != nil {
|
|
return nil, fmt.Errorf("%v: %v", err.Error(), data)
|
|
}
|
|
return parts, nil
|
|
}
|
|
|
|
// RunNucleiArgsAndGetErrors returns a list of errors in nuclei output (ERR,WRN,FTL)
|
|
func RunNucleiArgsAndGetErrors(debug bool, env []string, extra ...string) ([]string, error) {
|
|
cmd := exec.Command("./nuclei")
|
|
extra = append(extra, ExtraDebugArgs...)
|
|
cmd.Env = append(os.Environ(), env...)
|
|
cmd.Args = append(cmd.Args, extra...)
|
|
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
|
|
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
|
|
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
|
|
cmd.Args = append(cmd.Args, "-allow-local-file-access")
|
|
cmd.Args = append(cmd.Args, "-nc") // disable color
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
dataOutput, err := cmd.CombinedOutput()
|
|
if debug {
|
|
fmt.Println(string(dataOutput))
|
|
}
|
|
var data string
|
|
if len(dataOutput) > 0 {
|
|
data = strings.TrimSpace(conversion.String(dataOutput))
|
|
}
|
|
results := []string{}
|
|
for _, v := range strings.Split(data, "\n") {
|
|
line := strings.TrimSpace(v)
|
|
switch {
|
|
case strings.HasPrefix(line, "[ERR]"):
|
|
results = append(results, line)
|
|
case strings.HasPrefix(line, "[WRN]"):
|
|
results = append(results, line)
|
|
case strings.HasPrefix(line, "[FTL]"):
|
|
results = append(results, line)
|
|
}
|
|
}
|
|
return results, err
|
|
}
|
|
|
|
// RunNucleiArgsWithEnvAndGetResults returns a list of results in nuclei output (ERR,WRN,FTL)
|
|
func RunNucleiArgsWithEnvAndGetResults(debug bool, env []string, extra ...string) ([]string, error) {
|
|
cmd := exec.Command("./nuclei")
|
|
extra = append(extra, ExtraDebugArgs...)
|
|
cmd.Env = append(os.Environ(), env...)
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
cmd.Args = append(cmd.Args, extra...)
|
|
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
|
|
cmd.Args = append(cmd.Args, "-interactions-poll-duration", "5")
|
|
cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
|
|
cmd.Args = append(cmd.Args, "-allow-local-file-access")
|
|
if debug {
|
|
cmd.Args = append(cmd.Args, "-debug")
|
|
cmd.Stderr = os.Stderr
|
|
fmt.Println(cmd.String())
|
|
} else {
|
|
cmd.Args = append(cmd.Args, "-silent")
|
|
}
|
|
dataOutput, err := cmd.Output()
|
|
var data string
|
|
if len(dataOutput) > 0 {
|
|
data = strings.TrimSpace(conversion.String(dataOutput))
|
|
}
|
|
if debug {
|
|
fmt.Println(data)
|
|
}
|
|
var parts []string
|
|
items := strings.Split(data, "\n")
|
|
for _, i := range items {
|
|
if i != "" {
|
|
parts = append(parts, i)
|
|
}
|
|
}
|
|
if (data == "" || len(parts) == 0) && err != nil {
|
|
return nil, fmt.Errorf("%v: %v", err.Error(), data)
|
|
}
|
|
return parts, nil
|
|
}
|
|
|
|
var templateLoaded = regexp.MustCompile(`(?:Templates|Workflows) loaded[^:]*: (\d+)`)
|
|
|
|
// RunNucleiBinaryAndGetLoadedTemplates returns a list of results for a template
|
|
func RunNucleiBinaryAndGetLoadedTemplates(nucleiBinary string, debug bool, args []string) (string, error) {
|
|
cmd := exec.Command(nucleiBinary, args...)
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
cmd.Args = append(cmd.Args, "-duc") // disable auto updates
|
|
if debug {
|
|
cmd.Args = append(cmd.Args, "-debug")
|
|
fmt.Println(cmd.String())
|
|
}
|
|
data, err := cmd.CombinedOutput()
|
|
if debug {
|
|
fmt.Println(string(data))
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
matches := templateLoaded.FindAllStringSubmatch(string(data), -1)
|
|
if len(matches) == 0 {
|
|
return "", errors.New("no matches found")
|
|
}
|
|
return matches[0][1], nil
|
|
}
|
|
func RunNucleiBinaryAndGetCombinedOutput(debug bool, args []string) (string, error) {
|
|
args = append(args, "-interactions-cooldown-period", "10", "-interactions-poll-duration", "1")
|
|
cmd := exec.Command("./nuclei", args...)
|
|
cmd.Env = append(cmd.Env, ExtraEnvVars...)
|
|
if debug {
|
|
cmd.Args = append(cmd.Args, "-debug")
|
|
fmt.Println(cmd.String())
|
|
}
|
|
data, err := cmd.CombinedOutput()
|
|
if debug {
|
|
fmt.Println(string(data))
|
|
}
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return string(data), nil
|
|
}
|
|
|
|
// TestCase is a single integration test case
|
|
type TestCase interface {
|
|
// Execute executes a test case and returns any errors if occurred
|
|
Execute(filePath string) error
|
|
}
|
|
|
|
// TCPServer creates a new tcp server that returns a response
|
|
type TCPServer struct {
|
|
URL string
|
|
listener net.Listener
|
|
}
|
|
|
|
// keys taken from https://pascal.bach.ch/2015/12/17/from-tcp-to-tls-in-go/
|
|
const serverKey = `-----BEGIN EC PARAMETERS-----
|
|
BgUrgQQAIg==
|
|
-----END EC PARAMETERS-----
|
|
-----BEGIN EC PRIVATE KEY-----
|
|
MIGkAgEBBDBJazGwuqgOLsCMr7P56w26JBEHQokiuAy2iCQfCnmOWm7S9FveQ/DP
|
|
qB69zvUPs26gBwYFK4EEACKhZANiAARehvy96ygCAsJ6iQvthzl/Nvq4P3c4MGyx
|
|
UMLMe0L10OCxeCl5ZY2CuFf8UnBgV1u414U4+yjIrS57w1/3utBKC9TVRGj+Vcls
|
|
2NZ4+8Jh6/M/Jf/Mpd8QyIy0WesEUM4=
|
|
-----END EC PRIVATE KEY-----
|
|
`
|
|
|
|
const serverCert = `-----BEGIN CERTIFICATE-----
|
|
MIICJDCCAakCCQDFa0/D9jJw6DAKBggqhkjOPQQDAjB7MQswCQYDVQQGEwJVUzEP
|
|
MA0GA1UECAwGcGRsYW5kMQ8wDQYDVQQHDAZwZGNpdHkxCzAJBgNVBAoMAnBkMQsw
|
|
CQYDVQQLDAJwZDELMAkGA1UEAwwCcGQxIzAhBgkqhkiG9w0BCQEWFGFueXRoaW5n
|
|
QGFueXRoaW5nLnBkMB4XDTIyMDEyNzIyMDUwNFoXDTMyMDEyNTIyMDUwNFowezEL
|
|
MAkGA1UEBhMCVVMxDzANBgNVBAgMBnBkbGFuZDEPMA0GA1UEBwwGcGRjaXR5MQsw
|
|
CQYDVQQKDAJwZDELMAkGA1UECwwCcGQxCzAJBgNVBAMMAnBkMSMwIQYJKoZIhvcN
|
|
AQkBFhRhbnl0aGluZ0Bhbnl0aGluZy5wZDB2MBAGByqGSM49AgEGBSuBBAAiA2IA
|
|
BF6G/L3rKAICwnqJC+2HOX82+rg/dzgwbLFQwsx7QvXQ4LF4KXlljYK4V/xScGBX
|
|
W7jXhTj7KMitLnvDX/e60EoL1NVEaP5VyWzY1nj7wmHr8z8l/8yl3xDIjLRZ6wRQ
|
|
zjAKBggqhkjOPQQDAgNpADBmAjEAgxGPbjRlhz+1Scmr6RU9VbzVJWN8KCsTTpx7
|
|
pqfmKpJ29UYReZN+fm/6fc5vkv1rAjEAkTuTf8ARSn1UiKlCTTDQVtCoRcMVLQQp
|
|
TCxxGzcAlUAAJE6+SJpY7fPRe+n2EvPS
|
|
-----END CERTIFICATE-----
|
|
`
|
|
|
|
// NewTCPServer creates a new TCP server from a handler
|
|
func NewTCPServer(tlsConfig *tls.Config, port int, handler func(conn net.Conn)) *TCPServer {
|
|
server := &TCPServer{}
|
|
|
|
l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
server.URL = l.Addr().String()
|
|
server.listener = l
|
|
|
|
if tlsConfig != nil {
|
|
cer, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
tlsConfig.Certificates = []tls.Certificate{cer}
|
|
}
|
|
|
|
go func() {
|
|
for {
|
|
// Listen for an incoming connection.
|
|
conn, err := l.Accept()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
// Handle connections in a new goroutine.
|
|
if tlsConfig != nil {
|
|
connTls := tls.Server(conn, tlsConfig)
|
|
go handler(connTls)
|
|
} else {
|
|
go handler(conn)
|
|
}
|
|
}
|
|
}()
|
|
return server
|
|
}
|
|
|
|
// Close closes the TCP server
|
|
func (s *TCPServer) Close() {
|
|
_ = s.listener.Close()
|
|
}
|
|
|
|
// NewWebsocketServer creates a new websocket server from a handler
|
|
func NewWebsocketServer(path string, handler func(conn net.Conn), originValidate func(origin string) bool, port ...int) *httptest.Server {
|
|
handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
if value := r.Header.Get("Origin"); value != "" && !originValidate(value) {
|
|
w.WriteHeader(http.StatusBadRequest)
|
|
return
|
|
}
|
|
conn, _, _, err := ws.UpgradeHTTP(r, w)
|
|
if err != nil {
|
|
return
|
|
}
|
|
go func() {
|
|
defer func() {
|
|
_ = conn.Close()
|
|
}()
|
|
|
|
handler(conn)
|
|
}()
|
|
})
|
|
|
|
if path != "" {
|
|
router := httprouter.New()
|
|
router.HandlerFunc("*", "/test", handlerFunc)
|
|
return httptest.NewServer(router)
|
|
}
|
|
return httptest.NewServer(handlerFunc)
|
|
}
|