Path: blob/dev/pkg/protocols/headless/engine/rules.go
2072 views
package engine12import (3"fmt"4"net/http"5"net/http/httputil"6"strings"78"github.com/go-rod/rod"9"github.com/go-rod/rod/lib/proto"10"github.com/projectdiscovery/nuclei/v3/pkg/protocols/common/protocolstate"11)1213// routingRuleHandler handles proxy rule for actions related to request/response modification14func (p *Page) routingRuleHandler(httpClient *http.Client) func(ctx *rod.Hijack) {15return func(ctx *rod.Hijack) {16// usually browsers don't use chunked transfer encoding, so we set the content-length nevertheless17ctx.Request.Req().ContentLength = int64(len(ctx.Request.Body()))18for _, rule := range p.rules {19if rule.Part != "request" {20continue21}2223switch rule.Action {24case ActionSetMethod:25rule.Do(func() {26ctx.Request.Req().Method = rule.Args["method"]27})28case ActionAddHeader:29ctx.Request.Req().Header.Add(rule.Args["key"], rule.Args["value"])30case ActionSetHeader:31ctx.Request.Req().Header.Set(rule.Args["key"], rule.Args["value"])32case ActionDeleteHeader:33ctx.Request.Req().Header.Del(rule.Args["key"])34case ActionSetBody:35body := rule.Args["body"]36ctx.Request.Req().ContentLength = int64(len(body))37ctx.Request.SetBody(body)38}39}4041// each http request is performed via the native go http client42// we first inject the shared cookies43if !p.options.DisableCookie {44if cookies := p.ctx.CookieJar.Cookies(ctx.Request.URL()); len(cookies) > 0 {45httpClient.Jar.SetCookies(ctx.Request.URL(), cookies)46}47}4849// perform the request50_ = ctx.LoadResponse(httpClient, true)5152if !p.options.DisableCookie {53// retrieve the updated cookies from the native http client and inject them into the shared cookie jar54// keeps existing one if not present55if cookies := httpClient.Jar.Cookies(ctx.Request.URL()); len(cookies) > 0 {56p.ctx.CookieJar.SetCookies(ctx.Request.URL(), cookies)57}58}5960for _, rule := range p.rules {61if rule.Part != "response" {62continue63}6465switch rule.Action {66case ActionAddHeader:67ctx.Response.Headers().Add(rule.Args["key"], rule.Args["value"])68case ActionSetHeader:69ctx.Response.Headers().Set(rule.Args["key"], rule.Args["value"])70case ActionDeleteHeader:71ctx.Response.Headers().Del(rule.Args["key"])72case ActionSetBody:73body := rule.Args["body"]74ctx.Response.Headers().Set("Content-Length", fmt.Sprintf("%d", len(body)))75ctx.Response.SetBody(rule.Args["body"])76}77}7879// store history80req := ctx.Request.Req()81var rawReq string82if raw, err := httputil.DumpRequestOut(req, true); err == nil {83rawReq = string(raw)84}8586// attempts to rebuild the response87var rawResp strings.Builder88respPayloads := ctx.Response.Payload()89if respPayloads != nil {90rawResp.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\n", respPayloads.ResponseCode, respPayloads.ResponsePhrase))91for _, header := range respPayloads.ResponseHeaders {92rawResp.WriteString(header.Name + ": " + header.Value + "\n")93}94rawResp.WriteString("\n")95rawResp.WriteString(ctx.Response.Body())96}9798// dump request99historyData := HistoryData{100RawRequest: rawReq,101RawResponse: rawResp.String(),102}103p.addToHistory(historyData)104}105}106107// routingRuleHandlerNative handles native proxy rule108func (p *Page) routingRuleHandlerNative(e *proto.FetchRequestPaused) error {109// ValidateNFailRequest validates if Local file access is enabled110// and local network access is enables if not it will fail the request111// that don't match the rules112if err := protocolstate.ValidateNFailRequest(p.options.Options, p.page, e); err != nil {113return err114}115body, _ := FetchGetResponseBody(p.page, e)116headers := make(map[string][]string)117for _, h := range e.ResponseHeaders {118headers[h.Name] = []string{h.Value}119}120121var statusCode int122if e.ResponseStatusCode != nil {123statusCode = *e.ResponseStatusCode124}125126// attempts to rebuild request127var rawReq strings.Builder128rawReq.WriteString(fmt.Sprintf("%s %s %s\n", e.Request.Method, e.Request.URL, "HTTP/1.1"))129for _, header := range e.Request.Headers {130rawReq.WriteString(fmt.Sprintf("%s\n", header.String()))131}132if e.Request.HasPostData {133rawReq.WriteString(fmt.Sprintf("\n%s\n", e.Request.PostData))134}135136// attempts to rebuild the response137var rawResp strings.Builder138rawResp.WriteString(fmt.Sprintf("HTTP/1.1 %d %s\n", statusCode, e.ResponseStatusText))139for _, header := range e.ResponseHeaders {140rawResp.WriteString(header.Name + ": " + header.Value + "\n")141}142rawResp.WriteString("\n")143rawResp.Write(body)144145// dump request146historyData := HistoryData{147RawRequest: rawReq.String(),148RawResponse: rawResp.String(),149}150p.addToHistory(historyData)151152return FetchContinueRequest(p.page, e)153}154155156