mirror of
https://github.com/adnanh/webhook.git
synced 2025-05-14 09:34:43 +00:00
Add IP whitelist match rule.
This commit is contained in:
parent
c8a83349d2
commit
173273b466
3 changed files with 115 additions and 52 deletions
73
hook/hook.go
73
hook/hook.go
|
@ -8,6 +8,7 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/textproto"
|
"net/textproto"
|
||||||
"reflect"
|
"reflect"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
@ -100,6 +101,48 @@ func CheckPayloadSignature(payload []byte, secret string, signature string) (str
|
||||||
return expectedMAC, err
|
return expectedMAC, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CheckIPWhitelist makes sure the provided remote address (of the form IP:port) falls within the provided IP range
|
||||||
|
// (in CIDR form or a single IP address).
|
||||||
|
func CheckIPWhitelist(remoteAddr string, ipRange string) (bool, error) {
|
||||||
|
// Extract IP address from remote address.
|
||||||
|
|
||||||
|
ip := remoteAddr
|
||||||
|
|
||||||
|
if strings.LastIndex(remoteAddr, ":") != -1 {
|
||||||
|
ip = remoteAddr[0:strings.LastIndex(remoteAddr, ":")]
|
||||||
|
}
|
||||||
|
|
||||||
|
ip = strings.TrimSpace(ip)
|
||||||
|
|
||||||
|
// IPv6 addresses will likely be surrounded by [], so don't forget to remove those.
|
||||||
|
|
||||||
|
if strings.HasPrefix(ip, "[") && strings.HasSuffix(ip, "]") {
|
||||||
|
ip = ip[1 : len(ip)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
parsedIP := net.ParseIP(strings.TrimSpace(ip))
|
||||||
|
|
||||||
|
if parsedIP == nil {
|
||||||
|
return false, fmt.Errorf("invalid IP address found in remote address '%s'", remoteAddr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Extract IP range in CIDR form. If a single IP address is provided, turn it into CIDR form.
|
||||||
|
|
||||||
|
ipRange = strings.TrimSpace(ipRange)
|
||||||
|
|
||||||
|
if strings.Index(ipRange, "/") == -1 {
|
||||||
|
ipRange = ipRange + "/32"
|
||||||
|
}
|
||||||
|
|
||||||
|
_, cidr, err := net.ParseCIDR(ipRange)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return cidr.Contains(parsedIP), nil
|
||||||
|
}
|
||||||
|
|
||||||
// ReplaceParameter replaces parameter value with the passed value in the passed map
|
// ReplaceParameter replaces parameter value with the passed value in the passed map
|
||||||
// (please note you should pass pointer to the map, because we're modifying it)
|
// (please note you should pass pointer to the map, because we're modifying it)
|
||||||
// based on the passed string
|
// based on the passed string
|
||||||
|
@ -479,16 +522,16 @@ type Rules struct {
|
||||||
|
|
||||||
// Evaluate finds the first rule property that is not nil and returns the value
|
// Evaluate finds the first rule property that is not nil and returns the value
|
||||||
// it evaluates to
|
// it evaluates to
|
||||||
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||||
switch {
|
switch {
|
||||||
case r.And != nil:
|
case r.And != nil:
|
||||||
return r.And.Evaluate(headers, query, payload, body)
|
return r.And.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
case r.Or != nil:
|
case r.Or != nil:
|
||||||
return r.Or.Evaluate(headers, query, payload, body)
|
return r.Or.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
case r.Not != nil:
|
case r.Not != nil:
|
||||||
return r.Not.Evaluate(headers, query, payload, body)
|
return r.Not.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
case r.Match != nil:
|
case r.Match != nil:
|
||||||
return r.Match.Evaluate(headers, query, payload, body)
|
return r.Match.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
}
|
}
|
||||||
|
|
||||||
return false, nil
|
return false, nil
|
||||||
|
@ -498,11 +541,11 @@ func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, body *[
|
||||||
type AndRule []Rules
|
type AndRule []Rules
|
||||||
|
|
||||||
// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
|
// Evaluate AndRule will return true if and only if all of ChildRules evaluate to true
|
||||||
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||||
res := true
|
res := true
|
||||||
|
|
||||||
for _, v := range r {
|
for _, v := range r {
|
||||||
rv, err := v.Evaluate(headers, query, payload, body)
|
rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -520,11 +563,11 @@ func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, body
|
||||||
type OrRule []Rules
|
type OrRule []Rules
|
||||||
|
|
||||||
// Evaluate OrRule will return true if any of ChildRules evaluate to true
|
// Evaluate OrRule will return true if any of ChildRules evaluate to true
|
||||||
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||||
res := false
|
res := false
|
||||||
|
|
||||||
for _, v := range r {
|
for _, v := range r {
|
||||||
rv, err := v.Evaluate(headers, query, payload, body)
|
rv, err := v.Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
@ -542,8 +585,8 @@ func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, body *
|
||||||
type NotRule Rules
|
type NotRule Rules
|
||||||
|
|
||||||
// Evaluate NotRule will return true if and only if ChildRule evaluates to false
|
// Evaluate NotRule will return true if and only if ChildRule evaluates to false
|
||||||
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||||
rv, err := Rules(r).Evaluate(headers, query, payload, body)
|
rv, err := Rules(r).Evaluate(headers, query, payload, body, remoteAddr)
|
||||||
return !rv, err
|
return !rv, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -554,6 +597,7 @@ type MatchRule struct {
|
||||||
Secret string `json:"secret,omitempty"`
|
Secret string `json:"secret,omitempty"`
|
||||||
Value string `json:"value,omitempty"`
|
Value string `json:"value,omitempty"`
|
||||||
Parameter Argument `json:"parameter,omitempty"`
|
Parameter Argument `json:"parameter,omitempty"`
|
||||||
|
IPRange string `json:"ip-range,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constants for the MatchRule type
|
// Constants for the MatchRule type
|
||||||
|
@ -561,10 +605,15 @@ const (
|
||||||
MatchValue string = "value"
|
MatchValue string = "value"
|
||||||
MatchRegex string = "regex"
|
MatchRegex string = "regex"
|
||||||
MatchHashSHA1 string = "payload-hash-sha1"
|
MatchHashSHA1 string = "payload-hash-sha1"
|
||||||
|
IPWhitelist string = "ip-whitelist"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Evaluate MatchRule will return based on the type
|
// Evaluate MatchRule will return based on the type
|
||||||
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte) (bool, error) {
|
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||||
|
if r.Type == IPWhitelist {
|
||||||
|
return CheckIPWhitelist(remoteAddr, r.IPRange)
|
||||||
|
}
|
||||||
|
|
||||||
if arg, ok := r.Parameter.Get(headers, query, payload); ok {
|
if arg, ok := r.Parameter.Get(headers, query, payload); ok {
|
||||||
switch r.Type {
|
switch r.Type {
|
||||||
case MatchValue:
|
case MatchValue:
|
||||||
|
|
|
@ -241,29 +241,43 @@ func TestHooksMatch(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
var matchRuleTests = []struct {
|
var matchRuleTests = []struct {
|
||||||
typ, regex, secret, value string
|
typ, regex, secret, value, ipRange string
|
||||||
param Argument
|
param Argument
|
||||||
headers, query, payload *map[string]interface{}
|
headers, query, payload *map[string]interface{}
|
||||||
body []byte
|
body []byte
|
||||||
ok bool
|
remoteAddr string
|
||||||
err bool
|
ok bool
|
||||||
|
err bool
|
||||||
}{
|
}{
|
||||||
{"value", "", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
{"value", "", "", "z", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
|
||||||
{"regex", "^z", "", "z", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
{"regex", "^z", "", "z", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", true, false},
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), true, false},
|
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||||
// failures
|
// failures
|
||||||
{"value", "", "", "X", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
{"value", "", "", "X", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
|
||||||
{"regex", "^X", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
{"regex", "^X", "", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, false},
|
||||||
{"value", "", "2", "X", Argument{"header", "a", ""}, &map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, false, false}, // reference invalid header
|
{"value", "", "2", "X", "", Argument{"header", "a", ""}, &map[string]interface{}{"Y": "z"}, nil, nil, []byte{}, "", false, false}, // reference invalid header
|
||||||
// errors
|
// errors
|
||||||
{"regex", "*", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, true}, // invalid regex
|
{"regex", "*", "", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, "", false, true}, // invalid regex
|
||||||
{"payload-hash-sha1", "", "secret", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": ""}, nil, nil, []byte{}, false, true}, // invalid hmac
|
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", ""}, &map[string]interface{}{"A": ""}, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||||
|
// IP whitelisting, valid cases
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.1", Argument{}, nil, nil, nil, []byte{}, "192.168.0.1:9000", true, false}, // valid IPv4, no range
|
||||||
|
{"ip-whitelist", "", "", "", "::1/24", Argument{}, nil, nil, nil, []byte{}, "[::1]:9000", true, false}, // valid IPv6, with range
|
||||||
|
{"ip-whitelist", "", "", "", "::1", Argument{}, nil, nil, nil, []byte{}, "[::1]:9000", true, false}, // valid IPv6, no range
|
||||||
|
// IP whitelisting, invalid cases
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.1/a", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", false, true}, // invalid IPv4, with range
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.a", Argument{}, nil, nil, nil, []byte{}, "192.168.0.2:9000", false, true}, // invalid IPv4, no range
|
||||||
|
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, nil, nil, []byte{}, "192.168.0.a:9000", false, true}, // invalid IPv4 address
|
||||||
|
{"ip-whitelist", "", "", "", "::1/a", Argument{}, nil, nil, nil, []byte{}, "[::1]:9000", false, true}, // invalid IPv6, with range
|
||||||
|
{"ip-whitelist", "", "", "", "::z", Argument{}, nil, nil, nil, []byte{}, "[::1]:9000", false, true}, // invalid IPv6, no range
|
||||||
|
{"ip-whitelist", "", "", "", "::1/24", Argument{}, nil, nil, nil, []byte{}, "[::z]:9000", false, true}, // invalid IPv6 address
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestMatchRule(t *testing.T) {
|
func TestMatchRule(t *testing.T) {
|
||||||
for i, tt := range matchRuleTests {
|
for i, tt := range matchRuleTests {
|
||||||
r := MatchRule{tt.typ, tt.regex, tt.secret, tt.value, tt.param}
|
r := MatchRule{tt.typ, tt.regex, tt.secret, tt.value, tt.param, tt.ipRange}
|
||||||
ok, err := r.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
ok, err := r.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, tt.remoteAddr)
|
||||||
if ok != tt.ok || (err != nil) != tt.err {
|
if ok != tt.ok || (err != nil) != tt.err {
|
||||||
t.Errorf("%d failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", i, r, tt.ok, tt.err, ok, (err != nil))
|
t.Errorf("%d failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", i, r, tt.ok, tt.err, ok, (err != nil))
|
||||||
}
|
}
|
||||||
|
@ -281,8 +295,8 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=y): a=z && b=y",
|
"(a=z, b=y): a=z && b=y",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"A": "z", "B": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -290,8 +304,8 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=Y): a=z && b=y",
|
"(a=z, b=Y): a=z && b=y",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -300,22 +314,22 @@ var andRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=y, c=x, d=w=, e=X, f=X): a=z && (b=y && c=x) && (d=w || e=v) && !f=u",
|
"(a=z, b=y, c=x, d=w=, e=X, f=X): a=z && (b=y && c=x) && (d=w || e=v) && !f=u",
|
||||||
AndRule{
|
AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{
|
{
|
||||||
And: &AndRule{
|
And: &AndRule{
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", ""}}},
|
{Match: &MatchRule{"value", "", "", "x", Argument{"header", "c", ""}, ""}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Or: &OrRule{
|
Or: &OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", ""}}},
|
{Match: &MatchRule{"value", "", "", "w", Argument{"header", "d", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", ""}}},
|
{Match: &MatchRule{"value", "", "", "v", Argument{"header", "e", ""}, ""}},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Not: &NotRule{
|
Not: &NotRule{
|
||||||
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", ""}},
|
Match: &MatchRule{"value", "", "", "u", Argument{"header", "f", ""}, ""},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -326,7 +340,7 @@ var andRuleTests = []struct {
|
||||||
// failures
|
// failures
|
||||||
{
|
{
|
||||||
"invalid rule",
|
"invalid rule",
|
||||||
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}},
|
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}, ""}}},
|
||||||
&map[string]interface{}{"Y": "z"}, nil, nil, nil,
|
&map[string]interface{}{"Y": "z"}, nil, nil, nil,
|
||||||
false, false,
|
false, false,
|
||||||
},
|
},
|
||||||
|
@ -334,7 +348,7 @@ var andRuleTests = []struct {
|
||||||
|
|
||||||
func TestAndRule(t *testing.T) {
|
func TestAndRule(t *testing.T) {
|
||||||
for _, tt := range andRuleTests {
|
for _, tt := range andRuleTests {
|
||||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
|
||||||
if ok != tt.ok || (err != nil) != tt.err {
|
if ok != tt.ok || (err != nil) != tt.err {
|
||||||
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.desc, tt.ok, tt.err, ok, err)
|
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.desc, tt.ok, tt.err, ok, err)
|
||||||
}
|
}
|
||||||
|
@ -352,8 +366,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=z, b=X): a=z || b=y",
|
"(a=z, b=X): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"A": "z", "B": "X"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "z", "B": "X"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -361,8 +375,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=X, b=y): a=z || b=y",
|
"(a=X, b=y): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"A": "X", "B": "y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "X", "B": "y"}, nil, nil, []byte{},
|
||||||
true, false,
|
true, false,
|
||||||
|
@ -370,8 +384,8 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"(a=Z, b=Y): a=z || b=y",
|
"(a=Z, b=Y): a=z || b=y",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}}},
|
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, []byte{},
|
&map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -380,7 +394,7 @@ var orRuleTests = []struct {
|
||||||
{
|
{
|
||||||
"invalid rule",
|
"invalid rule",
|
||||||
OrRule{
|
OrRule{
|
||||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}},
|
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}},
|
||||||
},
|
},
|
||||||
&map[string]interface{}{"Y": "Z"}, nil, nil, []byte{},
|
&map[string]interface{}{"Y": "Z"}, nil, nil, []byte{},
|
||||||
false, false,
|
false, false,
|
||||||
|
@ -389,7 +403,7 @@ var orRuleTests = []struct {
|
||||||
|
|
||||||
func TestOrRule(t *testing.T) {
|
func TestOrRule(t *testing.T) {
|
||||||
for _, tt := range orRuleTests {
|
for _, tt := range orRuleTests {
|
||||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
|
||||||
if ok != tt.ok || (err != nil) != tt.err {
|
if ok != tt.ok || (err != nil) != tt.err {
|
||||||
t.Errorf("%#v:\nexpected ok: %#v, err: %v\ngot ok: %#v err: %v", tt.desc, tt.ok, tt.err, ok, err)
|
t.Errorf("%#v:\nexpected ok: %#v, err: %v\ngot ok: %#v err: %v", tt.desc, tt.ok, tt.err, ok, err)
|
||||||
}
|
}
|
||||||
|
@ -404,13 +418,13 @@ var notRuleTests = []struct {
|
||||||
ok bool
|
ok bool
|
||||||
err bool
|
err bool
|
||||||
}{
|
}{
|
||||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", ""}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, true, false},
|
||||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", ""}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, []byte{}, false, false},
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestNotRule(t *testing.T) {
|
func TestNotRule(t *testing.T) {
|
||||||
for _, tt := range notRuleTests {
|
for _, tt := range notRuleTests {
|
||||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body)
|
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, &tt.body, "")
|
||||||
if ok != tt.ok || (err != nil) != tt.err {
|
if ok != tt.ok || (err != nil) != tt.err {
|
||||||
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.rule, tt.ok, tt.err, ok, err)
|
t.Errorf("failed to match %#v:\nexpected ok: %#v, err: %v\ngot ok: %#v, err: %v", tt.rule, tt.ok, tt.err, ok, err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -243,7 +243,7 @@ func hookHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
if matchedHook.TriggerRule == nil {
|
if matchedHook.TriggerRule == nil {
|
||||||
ok = true
|
ok = true
|
||||||
} else {
|
} else {
|
||||||
ok, err = matchedHook.TriggerRule.Evaluate(&headers, &query, &payload, &body)
|
ok, err = matchedHook.TriggerRule.Evaluate(&headers, &query, &payload, &body, r.RemoteAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
msg := fmt.Sprintf("error evaluating hook: %s", err)
|
msg := fmt.Sprintf("error evaluating hook: %s", err)
|
||||||
log.Printf(msg)
|
log.Printf(msg)
|
||||||
|
|
Loading…
Add table
Reference in a new issue