199 lines
4.2 KiB
Go
199 lines
4.2 KiB
Go
package webhooks
|
|
|
|
import (
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
|
|
"github.com/OliveTin/OliveTin/internal/config"
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type WebhookMatcher struct {
|
|
config config.WebhookConfig
|
|
req *http.Request
|
|
bodyBytes []byte
|
|
}
|
|
|
|
func NewWebhookMatcher(cfg config.WebhookConfig, r *http.Request, bodyBytes []byte) *WebhookMatcher {
|
|
return &WebhookMatcher{
|
|
config: cfg,
|
|
req: r,
|
|
bodyBytes: bodyBytes,
|
|
}
|
|
}
|
|
|
|
func (m *WebhookMatcher) Matches() bool {
|
|
if !m.matchHeaders() {
|
|
return false
|
|
}
|
|
|
|
if !m.matchQuery() {
|
|
return false
|
|
}
|
|
|
|
if !m.matchPath() {
|
|
return false
|
|
}
|
|
|
|
return true
|
|
}
|
|
|
|
func (m *WebhookMatcher) matchHeaders() bool {
|
|
if len(m.config.MatchHeaders) == 0 {
|
|
return true
|
|
}
|
|
|
|
for key, expectedValue := range m.config.MatchHeaders {
|
|
actualValue := m.req.Header.Get(key)
|
|
if !m.compareValues(actualValue, expectedValue) {
|
|
log.WithFields(log.Fields{
|
|
"header": key,
|
|
"expected": expectedValue,
|
|
"actual": actualValue,
|
|
}).Debugf("Header mismatch")
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (m *WebhookMatcher) matchQuery() bool {
|
|
if len(m.config.MatchQuery) == 0 {
|
|
return true
|
|
}
|
|
|
|
query := m.req.URL.Query()
|
|
for key, expectedValue := range m.config.MatchQuery {
|
|
actualValue := query.Get(key)
|
|
if !m.compareValues(actualValue, expectedValue) {
|
|
log.WithFields(log.Fields{
|
|
"query": key,
|
|
"expected": expectedValue,
|
|
"actual": actualValue,
|
|
}).Debugf("Query parameter mismatch")
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
func (m *WebhookMatcher) matchPath() bool {
|
|
if m.config.MatchPath == "" {
|
|
return true
|
|
}
|
|
|
|
jsonPath, expectedValue := m.parseMatchPath()
|
|
matcher, err := NewJSONMatcher(m.bodyBytes)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"error": err,
|
|
}).Debugf("Failed to create JSON matcher")
|
|
return false
|
|
}
|
|
|
|
return m.matchPathValue(matcher, jsonPath, expectedValue)
|
|
}
|
|
|
|
func (m *WebhookMatcher) parseMatchPath() (string, string) {
|
|
parts := strings.SplitN(m.config.MatchPath, "=", 2)
|
|
jsonPath := parts[0]
|
|
expectedValue := ""
|
|
if len(parts) == 2 {
|
|
expectedValue = parts[1]
|
|
}
|
|
return jsonPath, expectedValue
|
|
}
|
|
|
|
func (m *WebhookMatcher) matchPathValue(matcher *JSONMatcher, jsonPath, expectedValue string) bool {
|
|
if expectedValue == "" {
|
|
_, err := matcher.ExtractValue(jsonPath)
|
|
return err == nil
|
|
}
|
|
|
|
matches, err := matcher.MatchPath(jsonPath, expectedValue)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"jsonPath": jsonPath,
|
|
"error": err,
|
|
}).Debugf("Failed to match JSONPath")
|
|
return false
|
|
}
|
|
return matches
|
|
}
|
|
|
|
func (m *WebhookMatcher) compareValues(actual, expected string) bool {
|
|
if strings.HasPrefix(expected, "regex:") {
|
|
pattern := strings.TrimPrefix(expected, "regex:")
|
|
matched, err := regexp.MatchString(pattern, actual)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"pattern": pattern,
|
|
"error": err,
|
|
}).Warnf("Invalid regex pattern")
|
|
return false
|
|
}
|
|
return matched
|
|
}
|
|
return actual == expected
|
|
}
|
|
|
|
func (m *WebhookMatcher) ExtractJustification() (string, error) {
|
|
if m.config.Justification == "" {
|
|
return "", nil
|
|
}
|
|
|
|
matcher, err := NewJSONMatcher(m.bodyBytes)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return matcher.ExtractValue(m.config.Justification)
|
|
}
|
|
|
|
func (m *WebhookMatcher) ExtractArguments() (map[string]string, error) {
|
|
matcher, err := NewJSONMatcher(m.bodyBytes)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
args := m.extractJSONPathValues(matcher)
|
|
m.addWebhookMetadata(args)
|
|
m.addWebhookHeaders(args)
|
|
|
|
return args, nil
|
|
}
|
|
|
|
func (m *WebhookMatcher) extractJSONPathValues(matcher *JSONMatcher) map[string]string {
|
|
args := make(map[string]string)
|
|
|
|
for argName, jsonPath := range m.config.Extract {
|
|
value, err := matcher.ExtractValue(jsonPath)
|
|
if err != nil {
|
|
log.WithFields(log.Fields{
|
|
"argName": argName,
|
|
"jsonPath": jsonPath,
|
|
"error": err,
|
|
}).Debugf("Failed to extract value")
|
|
continue
|
|
}
|
|
args[argName] = value
|
|
}
|
|
|
|
return args
|
|
}
|
|
|
|
func (m *WebhookMatcher) addWebhookMetadata(args map[string]string) {
|
|
args["webhook_method"] = m.req.Method
|
|
args["webhook_path"] = m.req.URL.Path
|
|
args["webhook_query"] = m.req.URL.RawQuery
|
|
}
|
|
|
|
func (m *WebhookMatcher) addWebhookHeaders(args map[string]string) {
|
|
for key, values := range m.req.Header {
|
|
if len(values) > 0 {
|
|
args["webhook_header_"+strings.ToLower(key)] = values[0]
|
|
}
|
|
}
|
|
}
|