4
0

matcher.go 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185
  1. package webhooks
  2. import (
  3. "net/http"
  4. "regexp"
  5. "strings"
  6. "github.com/OliveTin/OliveTin/internal/config"
  7. log "github.com/sirupsen/logrus"
  8. )
  9. type WebhookMatcher struct {
  10. config config.WebhookConfig
  11. req *http.Request
  12. bodyBytes []byte
  13. }
  14. func NewWebhookMatcher(cfg config.WebhookConfig, r *http.Request, bodyBytes []byte) *WebhookMatcher {
  15. return &WebhookMatcher{
  16. config: cfg,
  17. req: r,
  18. bodyBytes: bodyBytes,
  19. }
  20. }
  21. func (m *WebhookMatcher) Matches() bool {
  22. if !m.matchHeaders() {
  23. return false
  24. }
  25. if !m.matchQuery() {
  26. return false
  27. }
  28. if !m.matchPath() {
  29. return false
  30. }
  31. return true
  32. }
  33. func (m *WebhookMatcher) matchHeaders() bool {
  34. if len(m.config.MatchHeaders) == 0 {
  35. return true
  36. }
  37. for key, expectedValue := range m.config.MatchHeaders {
  38. actualValue := m.req.Header.Get(key)
  39. if !m.compareValues(actualValue, expectedValue) {
  40. log.WithFields(log.Fields{
  41. "header": key,
  42. "expected": expectedValue,
  43. "actual": actualValue,
  44. }).Debugf("Header mismatch")
  45. return false
  46. }
  47. }
  48. return true
  49. }
  50. func (m *WebhookMatcher) matchQuery() bool {
  51. if len(m.config.MatchQuery) == 0 {
  52. return true
  53. }
  54. query := m.req.URL.Query()
  55. for key, expectedValue := range m.config.MatchQuery {
  56. actualValue := query.Get(key)
  57. if !m.compareValues(actualValue, expectedValue) {
  58. log.WithFields(log.Fields{
  59. "query": key,
  60. "expected": expectedValue,
  61. "actual": actualValue,
  62. }).Debugf("Query parameter mismatch")
  63. return false
  64. }
  65. }
  66. return true
  67. }
  68. func (m *WebhookMatcher) matchPath() bool {
  69. if m.config.MatchPath == "" {
  70. return true
  71. }
  72. jsonPath, expectedValue := m.parseMatchPath()
  73. matcher, err := NewJSONMatcher(m.bodyBytes)
  74. if err != nil {
  75. log.WithFields(log.Fields{
  76. "error": err,
  77. }).Debugf("Failed to create JSON matcher")
  78. return false
  79. }
  80. return m.matchPathValue(matcher, jsonPath, expectedValue)
  81. }
  82. func (m *WebhookMatcher) parseMatchPath() (string, string) {
  83. parts := strings.SplitN(m.config.MatchPath, "=", 2)
  84. jsonPath := parts[0]
  85. expectedValue := ""
  86. if len(parts) == 2 {
  87. expectedValue = parts[1]
  88. }
  89. return jsonPath, expectedValue
  90. }
  91. func (m *WebhookMatcher) matchPathValue(matcher *JSONMatcher, jsonPath, expectedValue string) bool {
  92. if expectedValue == "" {
  93. _, err := matcher.ExtractValue(jsonPath)
  94. return err == nil
  95. }
  96. matches, err := matcher.MatchPath(jsonPath, expectedValue)
  97. if err != nil {
  98. log.WithFields(log.Fields{
  99. "jsonPath": jsonPath,
  100. "error": err,
  101. }).Debugf("Failed to match JSONPath")
  102. return false
  103. }
  104. return matches
  105. }
  106. func (m *WebhookMatcher) compareValues(actual, expected string) bool {
  107. if strings.HasPrefix(expected, "regex:") {
  108. pattern := strings.TrimPrefix(expected, "regex:")
  109. matched, err := regexp.MatchString(pattern, actual)
  110. if err != nil {
  111. log.WithFields(log.Fields{
  112. "pattern": pattern,
  113. "error": err,
  114. }).Warnf("Invalid regex pattern")
  115. return false
  116. }
  117. return matched
  118. }
  119. return actual == expected
  120. }
  121. func (m *WebhookMatcher) ExtractArguments() (map[string]string, error) {
  122. matcher, err := NewJSONMatcher(m.bodyBytes)
  123. if err != nil {
  124. return nil, err
  125. }
  126. args := m.extractJSONPathValues(matcher)
  127. m.addWebhookMetadata(args)
  128. m.addWebhookHeaders(args)
  129. return args, nil
  130. }
  131. func (m *WebhookMatcher) extractJSONPathValues(matcher *JSONMatcher) map[string]string {
  132. args := make(map[string]string)
  133. for argName, jsonPath := range m.config.Extract {
  134. value, err := matcher.ExtractValue(jsonPath)
  135. if err != nil {
  136. log.WithFields(log.Fields{
  137. "argName": argName,
  138. "jsonPath": jsonPath,
  139. "error": err,
  140. }).Debugf("Failed to extract value")
  141. continue
  142. }
  143. args[argName] = value
  144. }
  145. return args
  146. }
  147. func (m *WebhookMatcher) addWebhookMetadata(args map[string]string) {
  148. args["webhook_method"] = m.req.Method
  149. args["webhook_path"] = m.req.URL.Path
  150. args["webhook_query"] = m.req.URL.RawQuery
  151. }
  152. func (m *WebhookMatcher) addWebhookHeaders(args map[string]string) {
  153. for key, values := range m.req.Header {
  154. if len(values) > 0 {
  155. args["webhook_header_"+strings.ToLower(key)] = values[0]
  156. }
  157. }
  158. }