github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/http/request_annotations.go (about) 1 package http 2 3 import ( 4 "context" 5 "net" 6 "regexp" 7 "strings" 8 "time" 9 10 "github.com/projectdiscovery/fastdialer/fastdialer" 11 "github.com/projectdiscovery/retryablehttp-go" 12 iputil "github.com/projectdiscovery/utils/ip" 13 stringsutil "github.com/projectdiscovery/utils/strings" 14 ) 15 16 var ( 17 // @Host:target overrides the input target with the annotated one (similar to self-contained requests) 18 reHostAnnotation = regexp.MustCompile(`(?m)^@Host:\s*(.+)\s*$`) 19 // @tls-sni:target overrides the input target with the annotated one 20 // special values: 21 // request.host: takes the value from the host header 22 // target: overrides with the specific value 23 reSniAnnotation = regexp.MustCompile(`(?m)^@tls-sni:\s*(.+)\s*$`) 24 // @timeout:duration overrides the input timeout with a custom duration 25 reTimeoutAnnotation = regexp.MustCompile(`(?m)^@timeout:\s*(.+)\s*$`) 26 // @once sets the request to be executed only once for a specific URL 27 reOnceAnnotation = regexp.MustCompile(`(?m)^@once\s*$`) 28 ) 29 30 type flowMark int 31 32 const ( 33 Once flowMark = iota 34 ) 35 36 // parseFlowAnnotations and override requests flow 37 func parseFlowAnnotations(rawRequest string) (flowMark, bool) { 38 var fm flowMark 39 // parse request for known override annotations 40 var hasFlowOverride bool 41 // @once 42 if reOnceAnnotation.MatchString(rawRequest) { 43 fm = Once 44 hasFlowOverride = true 45 } 46 47 return fm, hasFlowOverride 48 } 49 50 type annotationOverrides struct { 51 request *retryablehttp.Request 52 cancelFunc context.CancelFunc 53 interactshURLs []string 54 } 55 56 // parseAnnotations and override requests settings 57 func (r *Request) parseAnnotations(rawRequest string, request *retryablehttp.Request) (overrides annotationOverrides, modified bool) { 58 // parse request for known override annotations 59 60 // @Host:target 61 if hosts := reHostAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 { 62 value := strings.TrimSpace(hosts[1]) 63 // handle scheme 64 switch { 65 case stringsutil.HasPrefixI(value, "http://"): 66 request.URL.Scheme = "http" 67 case stringsutil.HasPrefixI(value, "https://"): 68 request.URL.Scheme = "https" 69 } 70 71 value = stringsutil.TrimPrefixAny(value, "http://", "https://") 72 73 if isHostPort(value) { 74 request.URL.Host = value 75 } else { 76 hostPort := value 77 port := request.URL.Port() 78 if port != "" { 79 hostPort = net.JoinHostPort(hostPort, port) 80 } 81 request.URL.Host = hostPort 82 } 83 modified = true 84 } 85 86 // @tls-sni:target 87 if hosts := reSniAnnotation.FindStringSubmatch(rawRequest); len(hosts) > 0 { 88 value := strings.TrimSpace(hosts[1]) 89 value = stringsutil.TrimPrefixAny(value, "http://", "https://") 90 if idxForwardSlash := strings.Index(value, "/"); idxForwardSlash >= 0 { 91 value = value[:idxForwardSlash] 92 } 93 94 switch value { 95 case "request.host": 96 value = request.Host 97 case "interactsh-url": 98 if interactshURL, err := r.options.Interactsh.NewURLWithData("interactsh-url"); err == nil { 99 value = interactshURL 100 } 101 overrides.interactshURLs = append(overrides.interactshURLs, value) 102 } 103 ctx := context.WithValue(request.Context(), fastdialer.SniName, value) 104 request = request.Clone(ctx) 105 modified = true 106 } 107 108 // @timeout:duration 109 if r.connConfiguration.NoTimeout { 110 modified = true 111 var ctx context.Context 112 113 if duration := reTimeoutAnnotation.FindStringSubmatch(rawRequest); len(duration) > 0 { 114 value := strings.TrimSpace(duration[1]) 115 if parsed, err := time.ParseDuration(value); err == nil { 116 //nolint:govet // cancelled automatically by withTimeout 117 ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), parsed) 118 request = request.Clone(ctx) 119 } 120 } else { 121 //nolint:govet // cancelled automatically by withTimeout 122 ctx, overrides.cancelFunc = context.WithTimeout(context.Background(), time.Duration(r.options.Options.Timeout)*time.Second) 123 request = request.Clone(ctx) 124 } 125 } 126 127 overrides.request = request 128 129 return 130 } 131 132 func isHostPort(value string) bool { 133 _, port, err := net.SplitHostPort(value) 134 if err != nil { 135 return false 136 } 137 if !iputil.IsPort(port) { 138 return false 139 } 140 return true 141 }