request.go 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. package waf
  2. import (
  3. "fmt"
  4. "io"
  5. "net/http"
  6. "net/url"
  7. "github.com/crowdsecurity/coraza/v3/experimental"
  8. "github.com/google/uuid"
  9. )
  10. const (
  11. URIHeaderName = "X-Crowdsec-Waf-Uri"
  12. VerbHeaderName = "X-Crowdsec-Waf-Verb"
  13. HostHeaderName = "X-Crowdsec-Waf-Host"
  14. IPHeaderName = "X-Crowdsec-Waf-Ip"
  15. )
  16. // type ResponseRequest struct {
  17. // UUID string
  18. // Tx corazatypes.Transaction
  19. // Interruption *corazatypes.Interruption
  20. // Err error
  21. // SendEvents bool
  22. // }
  23. // func NewResponseRequest(Tx experimental.FullTransaction, in *corazatypes.Interruption, UUID string, err error) ResponseRequest {
  24. // return ResponseRequest{
  25. // UUID: UUID,
  26. // Tx: Tx,
  27. // Interruption: in,
  28. // Err: err,
  29. // SendEvents: true,
  30. // }
  31. // }
  32. // func (r *ResponseRequest) SetRemediation(remediation string) error {
  33. // if r.Interruption == nil {
  34. // return nil
  35. // }
  36. // r.Interruption.Action = remediation
  37. // return nil
  38. // }
  39. // func (r *ResponseRequest) SetRemediationByID(ID int, remediation string) error {
  40. // if r.Interruption == nil {
  41. // return nil
  42. // }
  43. // if r.Interruption.RuleID == ID {
  44. // r.Interruption.Action = remediation
  45. // }
  46. // return nil
  47. // }
  48. // func (r *ResponseRequest) CancelEvent() error {
  49. // // true by default
  50. // r.SendEvents = false
  51. // return nil
  52. // }
  53. type ParsedRequest struct {
  54. RemoteAddr string
  55. Host string
  56. ClientIP string
  57. URI string
  58. Args url.Values
  59. ClientHost string
  60. Headers http.Header
  61. URL *url.URL
  62. Method string
  63. Proto string
  64. Body []byte
  65. TransferEncoding []string
  66. UUID string
  67. Tx experimental.FullTransaction
  68. ResponseChannel chan WaapTempResponse
  69. IsInBand bool
  70. IsOutBand bool
  71. }
  72. // Generate a ParsedRequest from a http.Request. ParsedRequest can be consumed by the Waap Engine
  73. func NewParsedRequestFromRequest(r *http.Request) (ParsedRequest, error) {
  74. var err error
  75. body := make([]byte, 0)
  76. if r.Body != nil {
  77. body, err = io.ReadAll(r.Body)
  78. if err != nil {
  79. return ParsedRequest{}, fmt.Errorf("unable to read body: %s", err)
  80. }
  81. }
  82. // the real source of the request is set in 'x-client-ip'
  83. clientIP := r.Header.Get(IPHeaderName)
  84. if clientIP == "" {
  85. return ParsedRequest{}, fmt.Errorf("Missing '%s' header", IPHeaderName)
  86. }
  87. // the real target Host of the request is set in 'x-client-host'
  88. clientHost := r.Header.Get(HostHeaderName)
  89. if clientHost == "" {
  90. return ParsedRequest{}, fmt.Errorf("Missing '%s' header", HostHeaderName)
  91. }
  92. // the real URI of the request is set in 'x-client-uri'
  93. clientURI := r.Header.Get(URIHeaderName)
  94. if clientURI == "" {
  95. return ParsedRequest{}, fmt.Errorf("Missing '%s' header", URIHeaderName)
  96. }
  97. // the real VERB of the request is set in 'x-client-uri'
  98. clientMethod := r.Header.Get(VerbHeaderName)
  99. if clientMethod == "" {
  100. return ParsedRequest{}, fmt.Errorf("Missing '%s' header", VerbHeaderName)
  101. }
  102. // delete those headers before coraza process the request
  103. delete(r.Header, IPHeaderName)
  104. delete(r.Header, HostHeaderName)
  105. delete(r.Header, URIHeaderName)
  106. delete(r.Header, VerbHeaderName)
  107. parsedURL, err := url.Parse(clientURI)
  108. if err != nil {
  109. return ParsedRequest{}, fmt.Errorf("unable to parse url '%s': %s", clientURI, err)
  110. }
  111. return ParsedRequest{
  112. RemoteAddr: r.RemoteAddr,
  113. UUID: uuid.New().String(),
  114. ClientHost: clientHost,
  115. ClientIP: clientIP,
  116. URI: parsedURL.Path,
  117. Method: clientMethod,
  118. Host: r.Host,
  119. Headers: r.Header,
  120. URL: r.URL,
  121. Proto: r.Proto,
  122. Body: body,
  123. Args: parsedURL.Query(), //TODO: Check if there's not potential bypass as it excludes malformed args
  124. TransferEncoding: r.TransferEncoding,
  125. ResponseChannel: make(chan WaapTempResponse),
  126. }, nil
  127. }