github.com/projectdiscovery/nuclei/v2@v2.9.15/pkg/protocols/headless/engine/rules.go (about) 1 package engine 2 3 import ( 4 "fmt" 5 "net/http/httputil" 6 "strings" 7 8 "github.com/go-rod/rod" 9 "github.com/go-rod/rod/lib/proto" 10 "github.com/projectdiscovery/nuclei/v2/pkg/protocols/common/protocolstate" 11 ) 12 13 // routingRuleHandler handles proxy rule for actions related to request/response modification 14 func (p *Page) routingRuleHandler(ctx *rod.Hijack) { 15 // usually browsers don't use chunked transfer encoding, so we set the content-length nevertheless 16 ctx.Request.Req().ContentLength = int64(len(ctx.Request.Body())) 17 for _, rule := range p.rules { 18 if rule.Part != "request" { 19 continue 20 } 21 22 switch rule.Action { 23 case ActionSetMethod: 24 rule.Do(func() { 25 ctx.Request.Req().Method = rule.Args["method"] 26 }) 27 case ActionAddHeader: 28 ctx.Request.Req().Header.Add(rule.Args["key"], rule.Args["value"]) 29 case ActionSetHeader: 30 ctx.Request.Req().Header.Set(rule.Args["key"], rule.Args["value"]) 31 case ActionDeleteHeader: 32 ctx.Request.Req().Header.Del(rule.Args["key"]) 33 case ActionSetBody: 34 body := rule.Args["body"] 35 ctx.Request.Req().ContentLength = int64(len(body)) 36 ctx.Request.SetBody(body) 37 } 38 } 39 40 if p.options.CookieReuse { 41 // each http request is performed via the native go http client 42 // we first inject the shared cookies 43 if cookies := p.input.CookieJar.Cookies(ctx.Request.URL()); len(cookies) > 0 { 44 p.instance.browser.httpclient.Jar.SetCookies(ctx.Request.URL(), cookies) 45 } 46 } 47 48 // perform the request 49 _ = ctx.LoadResponse(p.instance.browser.httpclient, true) 50 51 if p.options.CookieReuse { 52 // retrieve the updated cookies from the native http client and inject them into the shared cookie jar 53 // keeps existing one if not present 54 if cookies := p.instance.browser.httpclient.Jar.Cookies(ctx.Request.URL()); len(cookies) > 0 { 55 p.input.CookieJar.SetCookies(ctx.Request.URL(), cookies) 56 } 57 } 58 59 for _, rule := range p.rules { 60 if rule.Part != "response" { 61 continue 62 } 63 64 switch rule.Action { 65 case ActionAddHeader: 66 ctx.Response.Headers().Add(rule.Args["key"], rule.Args["value"]) 67 case ActionSetHeader: 68 ctx.Response.Headers().Set(rule.Args["key"], rule.Args["value"]) 69 case ActionDeleteHeader: 70 ctx.Response.Headers().Del(rule.Args["key"]) 71 case ActionSetBody: 72 body := rule.Args["body"] 73 ctx.Response.Headers().Set("Content-Length", fmt.Sprintf("%d", len(body))) 74 ctx.Response.SetBody(rule.Args["body"]) 75 } 76 } 77 78 // store history 79 req := ctx.Request.Req() 80 var rawReq string 81 if raw, err := httputil.DumpRequestOut(req, true); err == nil { 82 rawReq = string(raw) 83 } 84 85 // attempts to rebuild the response 86 var rawResp strings.Builder 87 respPayloads := ctx.Response.Payload() 88 if respPayloads != nil { 89 rawResp.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\n", respPayloads.ResponseCode, respPayloads.ResponsePhrase)) 90 for _, header := range respPayloads.ResponseHeaders { 91 rawResp.WriteString(header.Name + ": " + header.Value + "\n") 92 } 93 rawResp.WriteString("\n") 94 rawResp.WriteString(ctx.Response.Body()) 95 } 96 97 // dump request 98 historyData := HistoryData{ 99 RawRequest: rawReq, 100 RawResponse: rawResp.String(), 101 } 102 p.addToHistory(historyData) 103 } 104 105 // routingRuleHandlerNative handles native proxy rule 106 func (p *Page) routingRuleHandlerNative(e *proto.FetchRequestPaused) error { 107 // ValidateNFailRequest validates if Local file access is enabled 108 // and local network access is enables if not it will fail the request 109 // that don't match the rules 110 if err := protocolstate.ValidateNFailRequest(p.page, e); err != nil { 111 return err 112 } 113 body, _ := FetchGetResponseBody(p.page, e) 114 headers := make(map[string][]string) 115 for _, h := range e.ResponseHeaders { 116 headers[h.Name] = []string{h.Value} 117 } 118 119 var statusCode int 120 if e.ResponseStatusCode != nil { 121 statusCode = *e.ResponseStatusCode 122 } 123 124 // attempts to rebuild request 125 var rawReq strings.Builder 126 rawReq.WriteString(fmt.Sprintf("%s %s %s\n", e.Request.Method, e.Request.URL, "HTTP/1.1")) 127 for _, header := range e.Request.Headers { 128 rawReq.WriteString(fmt.Sprintf("%s\n", header.String())) 129 } 130 if e.Request.HasPostData { 131 rawReq.WriteString(fmt.Sprintf("\n%s\n", e.Request.PostData)) 132 } 133 134 // attempts to rebuild the response 135 var rawResp strings.Builder 136 rawResp.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\n", statusCode, e.ResponseStatusText)) 137 for _, header := range e.ResponseHeaders { 138 rawResp.WriteString(header.Name + ": " + header.Value + "\n") 139 } 140 rawResp.WriteString("\n") 141 rawResp.Write(body) 142 143 // dump request 144 historyData := HistoryData{ 145 RawRequest: rawReq.String(), 146 RawResponse: rawResp.String(), 147 } 148 p.addToHistory(historyData) 149 150 return FetchContinueRequest(p.page, e) 151 }