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 }