crowdsec/pkg/waf/request.go
Sebastien Blot b0e7da06b9
up
2023-10-27 11:10:40 +02:00

143 lines
3.9 KiB
Go

package waf
import (
"fmt"
"io"
"net/http"
"net/url"
"github.com/crowdsecurity/coraza/v3/experimental"
"github.com/google/uuid"
)
const (
URIHeaderName = "X-Crowdsec-Waf-Uri"
VerbHeaderName = "X-Crowdsec-Waf-Verb"
HostHeaderName = "X-Crowdsec-Waf-Host"
IPHeaderName = "X-Crowdsec-Waf-Ip"
)
// type ResponseRequest struct {
// UUID string
// Tx corazatypes.Transaction
// Interruption *corazatypes.Interruption
// Err error
// SendEvents bool
// }
// func NewResponseRequest(Tx experimental.FullTransaction, in *corazatypes.Interruption, UUID string, err error) ResponseRequest {
// return ResponseRequest{
// UUID: UUID,
// Tx: Tx,
// Interruption: in,
// Err: err,
// SendEvents: true,
// }
// }
// func (r *ResponseRequest) SetRemediation(remediation string) error {
// if r.Interruption == nil {
// return nil
// }
// r.Interruption.Action = remediation
// return nil
// }
// func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
// if r.Interruption == nil {
// return nil
// }
// if r.Interruption.RuleID == ID {
// r.Interruption.Action = remediation
// }
// return nil
// }
// func (r *ResponseRequest) CancelEvent() error {
// // true by default
// r.SendEvents = false
// return nil
// }
type ParsedRequest struct {
RemoteAddr string
Host string
ClientIP string
URI string
Args url.Values
ClientHost string
Headers http.Header
URL *url.URL
Method string
Proto string
Body []byte
TransferEncoding []string
UUID string
Tx experimental.FullTransaction
ResponseChannel chan WaapTempResponse
IsInBand bool
IsOutBand bool
}
// Generate a ParsedRequest from a http.Request. ParsedRequest can be consumed by the Waap Engine
func NewParsedRequestFromRequest(r *http.Request) (ParsedRequest, error) {
var err error
body := make([]byte, 0)
if r.Body != nil {
body, err = io.ReadAll(r.Body)
if err != nil {
return ParsedRequest{}, fmt.Errorf("unable to read body: %s", err)
}
}
// the real source of the request is set in 'x-client-ip'
clientIP := r.Header.Get(IPHeaderName)
if clientIP == "" {
return ParsedRequest{}, fmt.Errorf("Missing '%s' header", IPHeaderName)
}
// the real target Host of the request is set in 'x-client-host'
clientHost := r.Header.Get(HostHeaderName)
if clientHost == "" {
return ParsedRequest{}, fmt.Errorf("Missing '%s' header", HostHeaderName)
}
// the real URI of the request is set in 'x-client-uri'
clientURI := r.Header.Get(URIHeaderName)
if clientURI == "" {
return ParsedRequest{}, fmt.Errorf("Missing '%s' header", URIHeaderName)
}
// the real VERB of the request is set in 'x-client-uri'
clientMethod := r.Header.Get(VerbHeaderName)
if clientMethod == "" {
return ParsedRequest{}, fmt.Errorf("Missing '%s' header", VerbHeaderName)
}
// delete those headers before coraza process the request
delete(r.Header, IPHeaderName)
delete(r.Header, HostHeaderName)
delete(r.Header, URIHeaderName)
delete(r.Header, VerbHeaderName)
parsedURL, err := url.Parse(clientURI)
if err != nil {
return ParsedRequest{}, fmt.Errorf("unable to parse url '%s': %s", clientURI, err)
}
return ParsedRequest{
RemoteAddr: r.RemoteAddr,
UUID: uuid.New().String(),
ClientHost: clientHost,
ClientIP: clientIP,
URI: parsedURL.Path,
Method: clientMethod,
Host: r.Host,
Headers: r.Header,
URL: r.URL,
Proto: r.Proto,
Body: body,
Args: parsedURL.Query(), //TODO: Check if there's not potential bypass as it excludes malformed args
TransferEncoding: r.TransferEncoding,
ResponseChannel: make(chan WaapTempResponse),
}, nil
}