github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/testutils/integration.go (about)

     1  package testutils
     2  
     3  import (
     4  	"crypto/tls"
     5  	"errors"
     6  	"fmt"
     7  	"net"
     8  	"net/http"
     9  	"net/http/httptest"
    10  	"os"
    11  	"os/exec"
    12  	"regexp"
    13  	"strings"
    14  
    15  	"github.com/gobwas/ws"
    16  	"github.com/julienschmidt/httprouter"
    17  )
    18  
    19  // ExtraArgs
    20  var ExtraDebugArgs = []string{}
    21  
    22  // RunNucleiTemplateAndGetResults returns a list of results for a template
    23  func RunNucleiTemplateAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
    24  	return RunNucleiAndGetResults(true, template, url, debug, extra...)
    25  }
    26  
    27  // RunNucleiWorkflowAndGetResults returns a list of results for a workflow
    28  func RunNucleiWorkflowAndGetResults(template, url string, debug bool, extra ...string) ([]string, error) {
    29  	return RunNucleiAndGetResults(false, template, url, debug, extra...)
    30  }
    31  
    32  func RunNucleiAndGetResults(isTemplate bool, template, url string, debug bool, extra ...string) ([]string, error) {
    33  	var templateOrWorkflowFlag string
    34  	if isTemplate {
    35  		templateOrWorkflowFlag = "-t"
    36  	} else {
    37  		templateOrWorkflowFlag = "-w"
    38  	}
    39  
    40  	return RunNucleiBareArgsAndGetResults(debug, append([]string{
    41  		templateOrWorkflowFlag,
    42  		template,
    43  		"-target",
    44  		url,
    45  	}, extra...)...)
    46  }
    47  
    48  func RunNucleiBareArgsAndGetResults(debug bool, extra ...string) ([]string, error) {
    49  	cmd := exec.Command("./nuclei")
    50  	extra = append(extra, ExtraDebugArgs...)
    51  	cmd.Args = append(cmd.Args, extra...)
    52  	cmd.Args = append(cmd.Args, "-duc") // disable auto updates
    53  	cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
    54  	cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
    55  	cmd.Args = append(cmd.Args, "-allow-local-file-access")
    56  	if debug {
    57  		cmd.Args = append(cmd.Args, "-debug")
    58  		cmd.Stderr = os.Stderr
    59  		fmt.Println(cmd.String())
    60  	} else {
    61  		cmd.Args = append(cmd.Args, "-silent")
    62  	}
    63  	data, err := cmd.Output()
    64  	if debug {
    65  		fmt.Println(string(data))
    66  	}
    67  	if len(data) < 1 && err != nil {
    68  		return nil, fmt.Errorf("%v: %v", err.Error(), string(data))
    69  	}
    70  	var parts []string
    71  	items := strings.Split(string(data), "\n")
    72  	for _, i := range items {
    73  		if i != "" {
    74  			parts = append(parts, i)
    75  		}
    76  	}
    77  	return parts, nil
    78  }
    79  
    80  // RunNucleiArgsAndGetResults returns result,and runtime errors
    81  func RunNucleiWithArgsAndGetResults(debug bool, args ...string) ([]string, error) {
    82  	cmd := exec.Command("./nuclei", args...)
    83  	if debug {
    84  		cmd.Args = append(cmd.Args, "-debug")
    85  		cmd.Stderr = os.Stderr
    86  		fmt.Println(cmd.String())
    87  	} else {
    88  		cmd.Args = append(cmd.Args, "-silent")
    89  	}
    90  	data, err := cmd.Output()
    91  	if debug {
    92  		fmt.Println(string(data))
    93  	}
    94  	if len(data) < 1 && err != nil {
    95  		return nil, fmt.Errorf("%v: %v", err.Error(), string(data))
    96  	}
    97  	var parts []string
    98  	items := strings.Split(string(data), "\n")
    99  	for _, i := range items {
   100  		if i != "" {
   101  			parts = append(parts, i)
   102  		}
   103  	}
   104  	return parts, nil
   105  }
   106  
   107  // RunNucleiArgsAndGetErrors returns a list of errors in nuclei output (ERR,WRN,FTL)
   108  func RunNucleiArgsAndGetErrors(debug bool, env []string, extra ...string) ([]string, error) {
   109  	cmd := exec.Command("./nuclei")
   110  	extra = append(extra, ExtraDebugArgs...)
   111  	cmd.Env = append(os.Environ(), env...)
   112  	cmd.Args = append(cmd.Args, extra...)
   113  	cmd.Args = append(cmd.Args, "-duc") // disable auto updates
   114  	cmd.Args = append(cmd.Args, "-interactions-poll-duration", "1")
   115  	cmd.Args = append(cmd.Args, "-interactions-cooldown-period", "10")
   116  	cmd.Args = append(cmd.Args, "-allow-local-file-access")
   117  	cmd.Args = append(cmd.Args, "-nc") // disable color
   118  	data, err := cmd.CombinedOutput()
   119  	if debug {
   120  		fmt.Println(string(data))
   121  	}
   122  	results := []string{}
   123  	for _, v := range strings.Split(string(data), "\n") {
   124  		line := strings.TrimSpace(v)
   125  		switch {
   126  		case strings.HasPrefix(line, "[ERR]"):
   127  			results = append(results, line)
   128  		case strings.HasPrefix(line, "[WRN]"):
   129  			results = append(results, line)
   130  		case strings.HasPrefix(line, "[FTL]"):
   131  			results = append(results, line)
   132  		}
   133  	}
   134  	return results, err
   135  }
   136  
   137  var templateLoaded = regexp.MustCompile(`(?:Templates|Workflows) loaded[^:]*: (\d+)`)
   138  
   139  // RunNucleiBinaryAndGetLoadedTemplates returns a list of results for a template
   140  func RunNucleiBinaryAndGetLoadedTemplates(nucleiBinary string, debug bool, args []string) (string, error) {
   141  	cmd := exec.Command(nucleiBinary, args...)
   142  	cmd.Args = append(cmd.Args, "-duc") // disable auto updates
   143  	if debug {
   144  		cmd.Args = append(cmd.Args, "-debug")
   145  		fmt.Println(cmd.String())
   146  	}
   147  	data, err := cmd.CombinedOutput()
   148  	if debug {
   149  		fmt.Println(string(data))
   150  	}
   151  	if err != nil {
   152  		return "", err
   153  	}
   154  	matches := templateLoaded.FindAllStringSubmatch(string(data), -1)
   155  	if len(matches) == 0 {
   156  		return "", errors.New("no matches found")
   157  	}
   158  	return matches[0][1], nil
   159  }
   160  func RunNucleiBinaryAndGetCombinedOutput(debug bool, args []string) (string, error) {
   161  	args = append(args, "-interactions-cooldown-period", "10", "-interactions-poll-duration", "1")
   162  	cmd := exec.Command("./nuclei", args...)
   163  	if debug {
   164  		cmd.Args = append(cmd.Args, "-debug")
   165  		fmt.Println(cmd.String())
   166  	}
   167  	data, err := cmd.CombinedOutput()
   168  	if debug {
   169  		fmt.Println(string(data))
   170  	}
   171  	if err != nil {
   172  		return "", err
   173  	}
   174  	return string(data), nil
   175  }
   176  
   177  // TestCase is a single integration test case
   178  type TestCase interface {
   179  	// Execute executes a test case and returns any errors if occurred
   180  	Execute(filePath string) error
   181  }
   182  
   183  // TCPServer creates a new tcp server that returns a response
   184  type TCPServer struct {
   185  	URL      string
   186  	listener net.Listener
   187  }
   188  
   189  // keys taken from https://pascal.bach.ch/2015/12/17/from-tcp-to-tls-in-go/
   190  const serverKey = `-----BEGIN EC PARAMETERS-----
   191  BgUrgQQAIg==
   192  -----END EC PARAMETERS-----
   193  -----BEGIN EC PRIVATE KEY-----
   194  MIGkAgEBBDBJazGwuqgOLsCMr7P56w26JBEHQokiuAy2iCQfCnmOWm7S9FveQ/DP
   195  qB69zvUPs26gBwYFK4EEACKhZANiAARehvy96ygCAsJ6iQvthzl/Nvq4P3c4MGyx
   196  UMLMe0L10OCxeCl5ZY2CuFf8UnBgV1u414U4+yjIrS57w1/3utBKC9TVRGj+Vcls
   197  2NZ4+8Jh6/M/Jf/Mpd8QyIy0WesEUM4=
   198  -----END EC PRIVATE KEY-----
   199  `
   200  
   201  const serverCert = `-----BEGIN CERTIFICATE-----
   202  MIICJDCCAakCCQDFa0/D9jJw6DAKBggqhkjOPQQDAjB7MQswCQYDVQQGEwJVUzEP
   203  MA0GA1UECAwGcGRsYW5kMQ8wDQYDVQQHDAZwZGNpdHkxCzAJBgNVBAoMAnBkMQsw
   204  CQYDVQQLDAJwZDELMAkGA1UEAwwCcGQxIzAhBgkqhkiG9w0BCQEWFGFueXRoaW5n
   205  QGFueXRoaW5nLnBkMB4XDTIyMDEyNzIyMDUwNFoXDTMyMDEyNTIyMDUwNFowezEL
   206  MAkGA1UEBhMCVVMxDzANBgNVBAgMBnBkbGFuZDEPMA0GA1UEBwwGcGRjaXR5MQsw
   207  CQYDVQQKDAJwZDELMAkGA1UECwwCcGQxCzAJBgNVBAMMAnBkMSMwIQYJKoZIhvcN
   208  AQkBFhRhbnl0aGluZ0Bhbnl0aGluZy5wZDB2MBAGByqGSM49AgEGBSuBBAAiA2IA
   209  BF6G/L3rKAICwnqJC+2HOX82+rg/dzgwbLFQwsx7QvXQ4LF4KXlljYK4V/xScGBX
   210  W7jXhTj7KMitLnvDX/e60EoL1NVEaP5VyWzY1nj7wmHr8z8l/8yl3xDIjLRZ6wRQ
   211  zjAKBggqhkjOPQQDAgNpADBmAjEAgxGPbjRlhz+1Scmr6RU9VbzVJWN8KCsTTpx7
   212  pqfmKpJ29UYReZN+fm/6fc5vkv1rAjEAkTuTf8ARSn1UiKlCTTDQVtCoRcMVLQQp
   213  TCxxGzcAlUAAJE6+SJpY7fPRe+n2EvPS
   214  -----END CERTIFICATE-----
   215  `
   216  
   217  // NewTCPServer creates a new TCP server from a handler
   218  func NewTCPServer(tlsConfig *tls.Config, port int, handler func(conn net.Conn)) *TCPServer {
   219  	server := &TCPServer{}
   220  
   221  	l, err := net.Listen("tcp", fmt.Sprintf("127.0.0.1:%d", port))
   222  	if err != nil {
   223  		panic(err)
   224  	}
   225  	server.URL = l.Addr().String()
   226  	server.listener = l
   227  
   228  	if tlsConfig != nil {
   229  		cer, err := tls.X509KeyPair([]byte(serverCert), []byte(serverKey))
   230  		if err != nil {
   231  			panic(err)
   232  		}
   233  		tlsConfig.Certificates = []tls.Certificate{cer}
   234  	}
   235  
   236  	go func() {
   237  		for {
   238  			// Listen for an incoming connection.
   239  			conn, err := l.Accept()
   240  			if err != nil {
   241  				continue
   242  			}
   243  			// Handle connections in a new goroutine.
   244  			if tlsConfig != nil {
   245  				connTls := tls.Server(conn, tlsConfig)
   246  				go handler(connTls)
   247  			} else {
   248  				go handler(conn)
   249  			}
   250  		}
   251  	}()
   252  	return server
   253  }
   254  
   255  // Close closes the TCP server
   256  func (s *TCPServer) Close() {
   257  	s.listener.Close()
   258  }
   259  
   260  // NewWebsocketServer creates a new websocket server from a handler
   261  func NewWebsocketServer(path string, handler func(conn net.Conn), originValidate func(origin string) bool, port ...int) *httptest.Server {
   262  	handlerFunc := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
   263  		if value := r.Header.Get("Origin"); value != "" && !originValidate(value) {
   264  			w.WriteHeader(http.StatusBadRequest)
   265  			return
   266  		}
   267  		conn, _, _, err := ws.UpgradeHTTP(r, w)
   268  		if err != nil {
   269  			return
   270  		}
   271  		go func() {
   272  			defer conn.Close()
   273  
   274  			handler(conn)
   275  		}()
   276  	})
   277  
   278  	if path != "" {
   279  		router := httprouter.New()
   280  		router.HandlerFunc("*", "/test", handlerFunc)
   281  		return httptest.NewServer(router)
   282  	}
   283  	return httptest.NewServer(handlerFunc)
   284  }