mirror of
https://github.com/adnanh/webhook.git
synced 2025-07-06 03:08:32 +00:00
Merge branch 'development' into feature/context-provider-command
This commit is contained in:
commit
eece0137ef
25 changed files with 766 additions and 477 deletions
|
@ -50,6 +50,33 @@ const (
|
|||
EnvNamespace string = "HOOK_"
|
||||
)
|
||||
|
||||
// Request represents a webhook request.
|
||||
type Request struct {
|
||||
// The request ID set by the RequestID middleware.
|
||||
ID string
|
||||
|
||||
// The Content-Type of the request.
|
||||
ContentType string
|
||||
|
||||
// The raw request body.
|
||||
Body []byte
|
||||
|
||||
// Headers is a map of the parsed headers.
|
||||
Headers map[string]interface{}
|
||||
|
||||
// Query is a map of the parsed URL query values.
|
||||
Query map[string]interface{}
|
||||
|
||||
// Payload is a map of the parsed payload.
|
||||
Payload map[string]interface{}
|
||||
|
||||
// Context is a map of the parsed pre-hook command result
|
||||
Context map[string]interface{}
|
||||
|
||||
// The underlying HTTP request.
|
||||
RawRequest *http.Request
|
||||
}
|
||||
|
||||
// ParameterNodeError describes an error walking a parameter node.
|
||||
type ParameterNodeError struct {
|
||||
key string
|
||||
|
@ -76,6 +103,8 @@ func IsParameterNodeError(err error) bool {
|
|||
type SignatureError struct {
|
||||
Signature string
|
||||
Signatures []string
|
||||
|
||||
emptyPayload bool
|
||||
}
|
||||
|
||||
func (e *SignatureError) Error() string {
|
||||
|
@ -83,11 +112,16 @@ func (e *SignatureError) Error() string {
|
|||
return "<nil>"
|
||||
}
|
||||
|
||||
if e.Signatures != nil {
|
||||
return fmt.Sprintf("invalid payload signatures %s", e.Signatures)
|
||||
var empty string
|
||||
if e.emptyPayload {
|
||||
empty = " on empty payload"
|
||||
}
|
||||
|
||||
return fmt.Sprintf("invalid payload signature %s", e.Signature)
|
||||
if e.Signatures != nil {
|
||||
return fmt.Sprintf("invalid payload signatures %s%s", e.Signatures, empty)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("invalid payload signature %s%s", e.Signature, empty)
|
||||
}
|
||||
|
||||
// ArgumentError describes an invalid argument passed to Hook.
|
||||
|
@ -163,21 +197,24 @@ func ValidateMAC(payload []byte, mac hash.Hash, signatures []string) (string, er
|
|||
return "", err
|
||||
}
|
||||
|
||||
expectedMAC := hex.EncodeToString(mac.Sum(nil))
|
||||
actualMAC := hex.EncodeToString(mac.Sum(nil))
|
||||
|
||||
for _, signature := range signatures {
|
||||
if hmac.Equal([]byte(signature), []byte(expectedMAC)) {
|
||||
return expectedMAC, err
|
||||
if hmac.Equal([]byte(signature), []byte(actualMAC)) {
|
||||
return actualMAC, err
|
||||
}
|
||||
}
|
||||
|
||||
return expectedMAC, &SignatureError{
|
||||
Signatures: signatures,
|
||||
e := &SignatureError{Signatures: signatures}
|
||||
if len(payload) == 0 {
|
||||
e.emptyPayload = true
|
||||
}
|
||||
|
||||
return actualMAC, e
|
||||
}
|
||||
|
||||
// CheckPayloadSignature calculates and verifies SHA1 signature of the given payload
|
||||
func CheckPayloadSignature(payload []byte, secret string, signature string) (string, error) {
|
||||
func CheckPayloadSignature(payload []byte, secret, signature string) (string, error) {
|
||||
if secret == "" {
|
||||
return "", errors.New("signature validation secret can not be empty")
|
||||
}
|
||||
|
@ -190,7 +227,7 @@ func CheckPayloadSignature(payload []byte, secret string, signature string) (str
|
|||
}
|
||||
|
||||
// CheckPayloadSignature256 calculates and verifies SHA256 signature of the given payload
|
||||
func CheckPayloadSignature256(payload []byte, secret string, signature string) (string, error) {
|
||||
func CheckPayloadSignature256(payload []byte, secret, signature string) (string, error) {
|
||||
if secret == "" {
|
||||
return "", errors.New("signature validation secret can not be empty")
|
||||
}
|
||||
|
@ -203,7 +240,7 @@ func CheckPayloadSignature256(payload []byte, secret string, signature string) (
|
|||
}
|
||||
|
||||
// CheckPayloadSignature512 calculates and verifies SHA512 signature of the given payload
|
||||
func CheckPayloadSignature512(payload []byte, secret string, signature string) (string, error) {
|
||||
func CheckPayloadSignature512(payload []byte, secret, signature string) (string, error) {
|
||||
if secret == "" {
|
||||
return "", errors.New("signature validation secret can not be empty")
|
||||
}
|
||||
|
@ -215,22 +252,26 @@ func CheckPayloadSignature512(payload []byte, secret string, signature string) (
|
|||
return ValidateMAC(payload, hmac.New(sha512.New, []byte(secret)), signatures)
|
||||
}
|
||||
|
||||
func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey string, checkDate bool) (bool, error) {
|
||||
// Check for the signature and date headers
|
||||
if _, ok := headers["X-Signature"]; !ok {
|
||||
func CheckScalrSignature(r *Request, signingKey string, checkDate bool) (bool, error) {
|
||||
if r.Headers == nil {
|
||||
return false, nil
|
||||
}
|
||||
if _, ok := headers["Date"]; !ok {
|
||||
|
||||
// Check for the signature and date headers
|
||||
if _, ok := r.Headers["X-Signature"]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
if _, ok := r.Headers["Date"]; !ok {
|
||||
return false, nil
|
||||
}
|
||||
if signingKey == "" {
|
||||
return false, errors.New("signature validation signing key can not be empty")
|
||||
}
|
||||
|
||||
providedSignature := headers["X-Signature"].(string)
|
||||
dateHeader := headers["Date"].(string)
|
||||
providedSignature := r.Headers["X-Signature"].(string)
|
||||
dateHeader := r.Headers["Date"].(string)
|
||||
mac := hmac.New(sha1.New, []byte(signingKey))
|
||||
mac.Write(body)
|
||||
mac.Write(r.Body)
|
||||
mac.Write([]byte(dateHeader))
|
||||
expectedSignature := hex.EncodeToString(mac.Sum(nil))
|
||||
|
||||
|
@ -257,7 +298,7 @@ func CheckScalrSignature(headers map[string]interface{}, body []byte, signingKey
|
|||
|
||||
// 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) {
|
||||
func CheckIPWhitelist(remoteAddr, ipRange string) (bool, error) {
|
||||
// Extract IP address from remote address.
|
||||
|
||||
// IPv6 addresses will likely be surrounded by [].
|
||||
|
@ -296,7 +337,7 @@ func CheckIPWhitelist(remoteAddr string, ipRange string) (bool, error) {
|
|||
// 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)
|
||||
// based on the passed string
|
||||
func ReplaceParameter(s string, params interface{}, value interface{}) bool {
|
||||
func ReplaceParameter(s string, params, value interface{}) bool {
|
||||
if params == nil {
|
||||
return false
|
||||
}
|
||||
|
@ -385,14 +426,27 @@ func GetParameter(s string, params interface{}) (interface{}, error) {
|
|||
return nil, &ParameterNodeError{s}
|
||||
}
|
||||
|
||||
// ExtractParameterAsString extracts value from interface{} as string based on the passed string
|
||||
// ExtractParameterAsString extracts value from interface{} as string based on
|
||||
// the passed string. Complex data types are rendered as JSON instead of the Go
|
||||
// Stringer format.
|
||||
func ExtractParameterAsString(s string, params interface{}) (string, error) {
|
||||
pValue, err := GetParameter(s, params)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%v", pValue), nil
|
||||
switch v := reflect.ValueOf(pValue); v.Kind() {
|
||||
case reflect.Array, reflect.Map, reflect.Slice:
|
||||
r, err := json.Marshal(pValue)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), nil
|
||||
|
||||
default:
|
||||
return fmt.Sprintf("%v", pValue), nil
|
||||
}
|
||||
}
|
||||
|
||||
// Argument type specifies the parameter key name and the source it should
|
||||
|
@ -406,43 +460,43 @@ type Argument struct {
|
|||
|
||||
// Get Argument method returns the value for the Argument's key name
|
||||
// based on the Argument's source
|
||||
func (ha *Argument) Get(headers, query, payload *map[string]interface{}, context *map[string]interface{}) (string, error) {
|
||||
func (ha *Argument) Get(r *Request) (string, error) {
|
||||
var source *map[string]interface{}
|
||||
key := ha.Name
|
||||
|
||||
switch ha.Source {
|
||||
case SourceHeader:
|
||||
source = headers
|
||||
source = &r.Headers
|
||||
key = textproto.CanonicalMIMEHeaderKey(ha.Name)
|
||||
case SourceQuery, SourceQueryAlias:
|
||||
source = query
|
||||
source = &r.Query
|
||||
case SourcePayload:
|
||||
source = payload
|
||||
source = &r.Payload
|
||||
case SourceContext:
|
||||
source = context
|
||||
source = &r.Context
|
||||
case SourceString:
|
||||
return ha.Name, nil
|
||||
case SourceEntirePayload:
|
||||
r, err := json.Marshal(payload)
|
||||
res, err := json.Marshal(&r.Payload)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), nil
|
||||
return string(res), nil
|
||||
case SourceEntireHeaders:
|
||||
r, err := json.Marshal(headers)
|
||||
res, err := json.Marshal(&r.Headers)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), nil
|
||||
return string(res), nil
|
||||
case SourceEntireQuery:
|
||||
r, err := json.Marshal(query)
|
||||
res, err := json.Marshal(&r.Query)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return string(r), nil
|
||||
return string(res), nil
|
||||
}
|
||||
|
||||
if source != nil {
|
||||
|
@ -540,11 +594,11 @@ type Hook struct {
|
|||
|
||||
// ParseJSONParameters decodes specified arguments to JSON objects and replaces the
|
||||
// string with the newly created object
|
||||
func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface{}, context *map[string]interface{}) []error {
|
||||
func (h *Hook) ParseJSONParameters(r *Request) []error {
|
||||
errors := make([]error, 0)
|
||||
|
||||
for i := range h.JSONStringParameters {
|
||||
arg, err := h.JSONStringParameters[i].Get(headers, query, payload, context)
|
||||
arg, err := h.JSONStringParameters[i].Get(r)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.JSONStringParameters[i]})
|
||||
} else {
|
||||
|
@ -563,13 +617,13 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||
|
||||
switch h.JSONStringParameters[i].Source {
|
||||
case SourceHeader:
|
||||
source = headers
|
||||
source = &r.Headers
|
||||
case SourcePayload:
|
||||
source = payload
|
||||
source = &r.Payload
|
||||
case SourceContext:
|
||||
source = context
|
||||
source = &r.Context
|
||||
case SourceQuery, SourceQueryAlias:
|
||||
source = query
|
||||
source = &r.Query
|
||||
}
|
||||
|
||||
if source != nil {
|
||||
|
@ -595,14 +649,14 @@ func (h *Hook) ParseJSONParameters(headers, query, payload *map[string]interface
|
|||
|
||||
// ExtractCommandArguments creates a list of arguments, based on the
|
||||
// PassArgumentsToCommand property that is ready to be used with exec.Command()
|
||||
func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]interface{}, context *map[string]interface{}) ([]string, []error) {
|
||||
func (h *Hook) ExtractCommandArguments(r *Request) ([]string, []error) {
|
||||
args := make([]string, 0)
|
||||
errors := make([]error, 0)
|
||||
|
||||
args = append(args, h.ExecuteCommand)
|
||||
|
||||
for i := range h.PassArgumentsToCommand {
|
||||
arg, err := h.PassArgumentsToCommand[i].Get(headers, query, payload, context)
|
||||
arg, err := h.PassArgumentsToCommand[i].Get(r)
|
||||
if err != nil {
|
||||
args = append(args, "")
|
||||
errors = append(errors, &ArgumentError{h.PassArgumentsToCommand[i]})
|
||||
|
@ -622,11 +676,11 @@ func (h *Hook) ExtractCommandArguments(headers, query, payload *map[string]inter
|
|||
// ExtractCommandArgumentsForEnv creates a list of arguments in key=value
|
||||
// format, based on the PassEnvironmentToCommand property that is ready to be used
|
||||
// with exec.Command().
|
||||
func (h *Hook) ExtractCommandArgumentsForEnv(headers, query, payload *map[string]interface{}, context *map[string]interface{}) ([]string, []error) {
|
||||
func (h *Hook) ExtractCommandArgumentsForEnv(r *Request) ([]string, []error) {
|
||||
args := make([]string, 0)
|
||||
errors := make([]error, 0)
|
||||
for i := range h.PassEnvironmentToCommand {
|
||||
arg, err := h.PassEnvironmentToCommand[i].Get(headers, query, payload, context)
|
||||
arg, err := h.PassEnvironmentToCommand[i].Get(r)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.PassEnvironmentToCommand[i]})
|
||||
continue
|
||||
|
@ -658,11 +712,11 @@ type FileParameter struct {
|
|||
// ExtractCommandArgumentsForFile creates a list of arguments in key=value
|
||||
// format, based on the PassFileToCommand property that is ready to be used
|
||||
// with exec.Command().
|
||||
func (h *Hook) ExtractCommandArgumentsForFile(headers, query, payload *map[string]interface{}, context *map[string]interface{}) ([]FileParameter, []error) {
|
||||
func (h *Hook) ExtractCommandArgumentsForFile(r *Request) ([]FileParameter, []error) {
|
||||
args := make([]FileParameter, 0)
|
||||
errors := make([]error, 0)
|
||||
for i := range h.PassFileToCommand {
|
||||
arg, err := h.PassFileToCommand[i].Get(headers, query, payload, context)
|
||||
arg, err := h.PassFileToCommand[i].Get(r)
|
||||
if err != nil {
|
||||
errors = append(errors, &ArgumentError{h.PassFileToCommand[i]})
|
||||
continue
|
||||
|
@ -769,16 +823,16 @@ type Rules struct {
|
|||
|
||||
// Evaluate finds the first rule property that is not nil and returns the value
|
||||
// it evaluates to
|
||||
func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, context *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
func (r Rules) Evaluate(req *Request) (bool, error) {
|
||||
switch {
|
||||
case r.And != nil:
|
||||
return r.And.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
return r.And.Evaluate(req)
|
||||
case r.Or != nil:
|
||||
return r.Or.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
return r.Or.Evaluate(req)
|
||||
case r.Not != nil:
|
||||
return r.Not.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
return r.Not.Evaluate(req)
|
||||
case r.Match != nil:
|
||||
return r.Match.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
return r.Match.Evaluate(req)
|
||||
}
|
||||
|
||||
return false, nil
|
||||
|
@ -788,11 +842,11 @@ func (r Rules) Evaluate(headers, query, payload *map[string]interface{}, context
|
|||
type AndRule []Rules
|
||||
|
||||
// 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{}, context *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
func (r AndRule) Evaluate(req *Request) (bool, error) {
|
||||
res := true
|
||||
|
||||
for _, v := range r {
|
||||
rv, err := v.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
rv, err := v.Evaluate(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -810,11 +864,11 @@ func (r AndRule) Evaluate(headers, query, payload *map[string]interface{}, conte
|
|||
type OrRule []Rules
|
||||
|
||||
// Evaluate OrRule will return true if any of ChildRules evaluate to true
|
||||
func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, context *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
func (r OrRule) Evaluate(req *Request) (bool, error) {
|
||||
res := false
|
||||
|
||||
for _, v := range r {
|
||||
rv, err := v.Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
rv, err := v.Evaluate(req)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
@ -832,8 +886,8 @@ func (r OrRule) Evaluate(headers, query, payload *map[string]interface{}, contex
|
|||
type NotRule Rules
|
||||
|
||||
// Evaluate NotRule will return true if and only if ChildRule evaluates to false
|
||||
func (r NotRule) Evaluate(headers, query, payload *map[string]interface{}, context *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
rv, err := Rules(r).Evaluate(headers, query, payload, context, body, remoteAddr)
|
||||
func (r NotRule) Evaluate(req *Request) (bool, error) {
|
||||
rv, err := Rules(r).Evaluate(req)
|
||||
return !rv, err
|
||||
}
|
||||
|
||||
|
@ -851,6 +905,9 @@ type MatchRule struct {
|
|||
const (
|
||||
MatchValue string = "value"
|
||||
MatchRegex string = "regex"
|
||||
MatchHMACSHA1 string = "payload-hmac-sha1"
|
||||
MatchHMACSHA256 string = "payload-hmac-sha256"
|
||||
MatchHMACSHA512 string = "payload-hmac-sha512"
|
||||
MatchHashSHA1 string = "payload-hash-sha1"
|
||||
MatchHashSHA256 string = "payload-hash-sha256"
|
||||
MatchHashSHA512 string = "payload-hash-sha512"
|
||||
|
@ -859,16 +916,16 @@ const (
|
|||
)
|
||||
|
||||
// Evaluate MatchRule will return based on the type
|
||||
func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, context *map[string]interface{}, body *[]byte, remoteAddr string) (bool, error) {
|
||||
func (r MatchRule) Evaluate(req *Request) (bool, error) {
|
||||
if r.Type == IPWhitelist {
|
||||
return CheckIPWhitelist(remoteAddr, r.IPRange)
|
||||
return CheckIPWhitelist(req.RawRequest.RemoteAddr, r.IPRange)
|
||||
}
|
||||
|
||||
if r.Type == ScalrSignature {
|
||||
return CheckScalrSignature(*headers, *body, r.Secret, true)
|
||||
return CheckScalrSignature(req, r.Secret, true)
|
||||
}
|
||||
|
||||
arg, err := r.Parameter.Get(headers, query, payload, context)
|
||||
arg, err := r.Parameter.Get(req)
|
||||
if err == nil {
|
||||
switch r.Type {
|
||||
case MatchValue:
|
||||
|
@ -876,13 +933,22 @@ func (r MatchRule) Evaluate(headers, query, payload *map[string]interface{}, con
|
|||
case MatchRegex:
|
||||
return regexp.MatchString(r.Regex, arg)
|
||||
case MatchHashSHA1:
|
||||
_, err := CheckPayloadSignature(*body, r.Secret, arg)
|
||||
log.Print(`warn: use of deprecated option payload-hash-sha1; use payload-hmac-sha1 instead`)
|
||||
fallthrough
|
||||
case MatchHMACSHA1:
|
||||
_, err := CheckPayloadSignature(req.Body, r.Secret, arg)
|
||||
return err == nil, err
|
||||
case MatchHashSHA256:
|
||||
_, err := CheckPayloadSignature256(*body, r.Secret, arg)
|
||||
log.Print(`warn: use of deprecated option payload-hash-sha256: use payload-hmac-sha256 instead`)
|
||||
fallthrough
|
||||
case MatchHMACSHA256:
|
||||
_, err := CheckPayloadSignature256(req.Body, r.Secret, arg)
|
||||
return err == nil, err
|
||||
case MatchHashSHA512:
|
||||
_, err := CheckPayloadSignature512(*body, r.Secret, arg)
|
||||
log.Print(`warn: use of deprecated option payload-hash-sha512: use payload-hmac-sha512 instead`)
|
||||
fallthrough
|
||||
case MatchHMACSHA512:
|
||||
_, err := CheckPayloadSignature512(req.Body, r.Secret, arg)
|
||||
return err == nil, err
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package hook
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"os"
|
||||
"reflect"
|
||||
"strings"
|
||||
|
@ -49,12 +50,14 @@ var checkPayloadSignatureTests = []struct {
|
|||
{[]byte(`{"a": "z"}`), "secret", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha1=b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e,sha1=b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", true},
|
||||
{[]byte(``), "secret", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", true},
|
||||
// failures
|
||||
{[]byte(`{"a": "z"}`), "secret", "XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e,sha1=XXXe04cbb22afa8ffbff8796fc1894ed27badd9e", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", false},
|
||||
{[]byte(`{"a": "z"}`), "secreX", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "900225703e9342328db7307692736e2f7cc7b36e", false},
|
||||
{[]byte(`{"a": "z"}`), "", "b17e04cbb22afa8ffbff8796fc1894ed27badd9e", "", false},
|
||||
{[]byte(``), "secret", "XXXf6174a0fcecc4d346680a72b7ce644b9a88e8", "25af6174a0fcecc4d346680a72b7ce644b9a88e8", false},
|
||||
}
|
||||
|
||||
func TestCheckPayloadSignature(t *testing.T) {
|
||||
|
@ -80,11 +83,13 @@ var checkPayloadSignature256Tests = []struct {
|
|||
{[]byte(`{"a": "z"}`), "secret", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha256=f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89,sha256=f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", true},
|
||||
{[]byte(``), "secret", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", true},
|
||||
// failures
|
||||
{[]byte(`{"a": "z"}`), "secret", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89,sha256=XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", false},
|
||||
{[]byte(`{"a": "z"}`), "", "XXX7af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89", "", false},
|
||||
{[]byte(``), "secret", "XXX66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", "f9e66e179b6747ae54108f82f8ade8b3c25d76fd30afde6c395822c530196169", false},
|
||||
}
|
||||
|
||||
func TestCheckPayloadSignature256(t *testing.T) {
|
||||
|
@ -109,9 +114,11 @@ var checkPayloadSignature512Tests = []struct {
|
|||
}{
|
||||
{[]byte(`{"a": "z"}`), "secret", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", true},
|
||||
{[]byte(`{"a": "z"}`), "secret", "sha512=4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", true},
|
||||
{[]byte(``), "secret", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", true},
|
||||
// failures
|
||||
{[]byte(`{"a": "z"}`), "secret", "74a0081f5b5988f4f3e8b8dd34dadc6291611f2e6260635a7e1535f8e95edb97ff520ba8b152e8ca5760ac42639854f3242e29efc81be73a8bf52d474d31ffea", "4ab17cc8ec668ead8bf498f87f8f32848c04d5ca3c9bcfcd3db9363f0deb44e580b329502a7fdff633d4d8fca301cc5c94a55a2fec458c675fb0ff2655898324", false},
|
||||
{[]byte(`{"a": "z"}`), "", "74a0081f5b5988f4f3e8b8dd34dadc6291611f2e6260635a7e1535f8e95edb97ff520ba8b152e8ca5760ac42639854f3242e29efc81be73a8bf52d474d31ffea", "", false},
|
||||
{[]byte(``), "secret", "XXX9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", "b0e9650c5faf9cd8ae02276671545424104589b3656731ec193b25d01b07561c27637c2d4d68389d6cf5007a8632c26ec89ba80a01c77a6cdd389ec28db43901", false},
|
||||
}
|
||||
|
||||
func TestCheckPayloadSignature512(t *testing.T) {
|
||||
|
@ -130,7 +137,7 @@ func TestCheckPayloadSignature512(t *testing.T) {
|
|||
var checkScalrSignatureTests = []struct {
|
||||
description string
|
||||
headers map[string]interface{}
|
||||
payload []byte
|
||||
body []byte
|
||||
secret string
|
||||
expectedSignature string
|
||||
ok bool
|
||||
|
@ -169,7 +176,11 @@ var checkScalrSignatureTests = []struct {
|
|||
|
||||
func TestCheckScalrSignature(t *testing.T) {
|
||||
for _, testCase := range checkScalrSignatureTests {
|
||||
valid, err := CheckScalrSignature(testCase.headers, testCase.payload, testCase.secret, false)
|
||||
r := &Request{
|
||||
Headers: testCase.headers,
|
||||
Body: testCase.body,
|
||||
}
|
||||
valid, err := CheckScalrSignature(r, testCase.secret, false)
|
||||
if valid != testCase.ok {
|
||||
t.Errorf("failed to check scalr signature fot test case: %s\nexpected ok:%#v, got ok:%#v}",
|
||||
testCase.description, testCase.ok, valid)
|
||||
|
@ -217,6 +228,9 @@ var extractParameterTests = []struct {
|
|||
{"a.b.0", map[string]interface{}{"a": map[string]interface{}{"b": []interface{}{"x", "y", "z"}}}, "x", true},
|
||||
{"a.1.b", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": "y"}, map[string]interface{}{"b": "z"}}}, "z", true},
|
||||
{"a.1.b.c", map[string]interface{}{"a": []interface{}{map[string]interface{}{"b": map[string]interface{}{"c": "y"}}, map[string]interface{}{"b": map[string]interface{}{"c": "z"}}}}, "z", true},
|
||||
{"b", map[string]interface{}{"b": map[string]interface{}{"z": 1}}, `{"z":1}`, true},
|
||||
{"c", map[string]interface{}{"c": []interface{}{"y", "z"}}, `["y","z"]`, true},
|
||||
{"d", map[string]interface{}{"d": [2]interface{}{"y", "z"}}, `["y","z"]`, true},
|
||||
// failures
|
||||
{"check_nil", nil, "", false},
|
||||
{"a.X", map[string]interface{}{"a": map[string]interface{}{"b": "z"}}, "", false}, // non-existent parameter reference
|
||||
|
@ -233,63 +247,75 @@ func TestExtractParameter(t *testing.T) {
|
|||
for _, tt := range extractParameterTests {
|
||||
value, err := ExtractParameterAsString(tt.s, tt.params)
|
||||
if (err == nil) != tt.ok || value != tt.value {
|
||||
t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%s}", tt.s, tt.value, tt.ok, value, err)
|
||||
t.Errorf("failed to extract parameter %q:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.s, tt.value, tt.ok, value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var argumentGetTests = []struct {
|
||||
source, name string
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
value string
|
||||
ok bool
|
||||
}{
|
||||
{"header", "a", &map[string]interface{}{"A": "z"}, nil, nil, nil, "z", true},
|
||||
{"url", "a", nil, &map[string]interface{}{"a": "z"}, nil, nil, "z", true},
|
||||
{"payload", "a", nil, nil, &map[string]interface{}{"a": "z"}, nil, "z", true},
|
||||
{"context", "a", nil, nil, nil, &map[string]interface{}{"a": "z"}, "z", true},
|
||||
{"header", "a", map[string]interface{}{"A": "z"}, nil, nil, nil, "z", true},
|
||||
{"url", "a", nil, map[string]interface{}{"a": "z"}, nil, nil, "z", true},
|
||||
{"payload", "a", nil, nil, map[string]interface{}{"a": "z"}, nil, "z", true},
|
||||
{"context", "a", nil, nil, nil, map[string]interface{}{"a": "z"}, "z", true},
|
||||
{"string", "a", nil, nil, nil, nil, "a", true},
|
||||
// failures
|
||||
{"header", "a", nil, &map[string]interface{}{"a": "z"}, &map[string]interface{}{"a": "z"}, nil, "", false}, // nil headers
|
||||
{"url", "a", &map[string]interface{}{"A": "z"}, nil, &map[string]interface{}{"a": "z"}, nil, "", false}, // nil query
|
||||
{"payload", "a", &map[string]interface{}{"A": "z"}, &map[string]interface{}{"a": "z"}, nil, nil, "", false}, // nil payload
|
||||
{"header", "a", nil, map[string]interface{}{"a": "z"}, map[string]interface{}{"a": "z"}, nil, "", false}, // nil headers
|
||||
{"url", "a", map[string]interface{}{"A": "z"}, nil, map[string]interface{}{"a": "z"}, nil, "", false}, // nil query
|
||||
{"payload", "a", map[string]interface{}{"A": "z"}, map[string]interface{}{"a": "z"}, nil, nil, "", false}, // nil payload
|
||||
{"context", "a", nil, nil, nil, nil, "", false}, // nil context
|
||||
{"foo", "a", &map[string]interface{}{"A": "z"}, nil, nil, nil, "", false}, // invalid source
|
||||
{"foo", "a", map[string]interface{}{"A": "z"}, nil, nil, nil, "", false}, // invalid source
|
||||
}
|
||||
|
||||
func TestArgumentGet(t *testing.T) {
|
||||
for _, tt := range argumentGetTests {
|
||||
a := Argument{tt.source, tt.name, "", false}
|
||||
value, err := a.Get(tt.headers, tt.query, tt.payload, tt.context)
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
}
|
||||
value, err := a.Get(r)
|
||||
if (err == nil) != tt.ok || value != tt.value {
|
||||
t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%s}", tt.source, tt.name, tt.value, tt.ok, value, err)
|
||||
t.Errorf("failed to get {%q, %q}:\nexpected {value:%#v, ok:%#v},\ngot {value:%#v, err:%v}", tt.source, tt.name, tt.value, tt.ok, value, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var hookParseJSONParametersTests = []struct {
|
||||
params []Argument
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
rheaders, rquery, rpayload, rcontext *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
rheaders, rquery, rpayload, rcontext map[string]interface{}
|
||||
ok bool
|
||||
}{
|
||||
{[]Argument{Argument{"header", "a", "", false}}, &map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, nil, &map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, nil, true},
|
||||
{[]Argument{Argument{"url", "a", "", false}}, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
||||
{[]Argument{Argument{"payload", "a", "", false}}, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
||||
{[]Argument{Argument{"context", "a", "", false}}, nil, nil, nil, &map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, &map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
||||
{[]Argument{Argument{"header", "z", "", false}}, &map[string]interface{}{"Z": `{}`}, nil, nil, nil, &map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, nil, true},
|
||||
{[]Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": `{"b": "y"}`}, nil, nil, nil, map[string]interface{}{"A": map[string]interface{}{"b": "y"}}, nil, nil, nil, true},
|
||||
{[]Argument{Argument{"url", "a", "", false}}, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, nil, true},
|
||||
{[]Argument{Argument{"payload", "a", "", false}}, nil, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, nil, true},
|
||||
{[]Argument{Argument{"context", "a", "", false}}, nil, nil, nil, map[string]interface{}{"a": `{"b": "y"}`}, nil, nil, nil, map[string]interface{}{"a": map[string]interface{}{"b": "y"}}, true},
|
||||
{[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": `{}`}, nil, nil, nil, map[string]interface{}{"Z": map[string]interface{}{}}, nil, nil, nil, true},
|
||||
// failures
|
||||
{[]Argument{Argument{"header", "z", "", false}}, &map[string]interface{}{"Z": ``}, nil, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, nil, false}, // empty string
|
||||
{[]Argument{Argument{"header", "y", "", false}}, &map[string]interface{}{"X": `{}`}, nil, nil, nil, &map[string]interface{}{"X": `{}`}, nil, nil, nil, false}, // missing parameter
|
||||
{[]Argument{Argument{"string", "z", "", false}}, &map[string]interface{}{"Z": ``}, nil, nil, nil, &map[string]interface{}{"Z": ``}, nil, nil, nil, false}, // invalid argument source
|
||||
{[]Argument{Argument{"header", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, nil, false}, // empty string
|
||||
{[]Argument{Argument{"header", "y", "", false}}, map[string]interface{}{"X": `{}`}, nil, nil, nil, map[string]interface{}{"X": `{}`}, nil, nil, nil, false}, // missing parameter
|
||||
{[]Argument{Argument{"string", "z", "", false}}, map[string]interface{}{"Z": ``}, nil, nil, nil, map[string]interface{}{"Z": ``}, nil, nil, nil, false}, // invalid argument source
|
||||
}
|
||||
|
||||
func TestHookParseJSONParameters(t *testing.T) {
|
||||
for _, tt := range hookParseJSONParametersTests {
|
||||
h := &Hook{JSONStringParameters: tt.params}
|
||||
err := h.ParseJSONParameters(tt.headers, tt.query, tt.payload, tt.context)
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
}
|
||||
err := h.ParseJSONParameters(r)
|
||||
if (err == nil) != tt.ok || !reflect.DeepEqual(tt.headers, tt.rheaders) {
|
||||
t.Errorf("failed to parse %v:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.params, *tt.rheaders, tt.ok, *tt.headers, (err == nil))
|
||||
t.Errorf("failed to parse %v:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.params, tt.rheaders, tt.ok, tt.headers, (err == nil))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -297,19 +323,25 @@ func TestHookParseJSONParameters(t *testing.T) {
|
|||
var hookExtractCommandArgumentsTests = []struct {
|
||||
exec string
|
||||
args []Argument
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
value []string
|
||||
ok bool
|
||||
}{
|
||||
{"test", []Argument{Argument{"header", "a", "", false}}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []string{"test", "z"}, true},
|
||||
{"test", []Argument{Argument{"header", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, nil, []string{"test", "z"}, true},
|
||||
// failures
|
||||
{"fail", []Argument{Argument{"payload", "a", "", false}}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []string{"fail", ""}, false},
|
||||
{"fail", []Argument{Argument{"payload", "a", "", false}}, map[string]interface{}{"A": "z"}, nil, nil, nil, []string{"fail", ""}, false},
|
||||
}
|
||||
|
||||
func TestHookExtractCommandArguments(t *testing.T) {
|
||||
for _, tt := range hookExtractCommandArgumentsTests {
|
||||
h := &Hook{ExecuteCommand: tt.exec, PassArgumentsToCommand: tt.args}
|
||||
value, err := h.ExtractCommandArguments(tt.headers, tt.query, tt.payload, tt.context)
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
}
|
||||
value, err := h.ExtractCommandArguments(r)
|
||||
if (err == nil) != tt.ok || !reflect.DeepEqual(value, tt.value) {
|
||||
t.Errorf("failed to extract args {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, (err == nil))
|
||||
}
|
||||
|
@ -338,7 +370,7 @@ func TestHookExtractCommandArguments(t *testing.T) {
|
|||
var hookExtractCommandArgumentsForEnvTests = []struct {
|
||||
exec string
|
||||
args []Argument
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
value []string
|
||||
ok bool
|
||||
}{
|
||||
|
@ -346,14 +378,14 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
|
|||
{
|
||||
"test",
|
||||
[]Argument{Argument{"header", "a", "", false}},
|
||||
&map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
[]string{"HOOK_a=z"},
|
||||
true,
|
||||
},
|
||||
{
|
||||
"test",
|
||||
[]Argument{Argument{"header", "a", "MYKEY", false}},
|
||||
&map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
[]string{"MYKEY=z"},
|
||||
true,
|
||||
},
|
||||
|
@ -361,7 +393,7 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
|
|||
{
|
||||
"fail",
|
||||
[]Argument{Argument{"payload", "a", "", false}},
|
||||
&map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
map[string]interface{}{"A": "z"}, nil, nil, nil,
|
||||
[]string{},
|
||||
false,
|
||||
},
|
||||
|
@ -370,7 +402,13 @@ var hookExtractCommandArgumentsForEnvTests = []struct {
|
|||
func TestHookExtractCommandArgumentsForEnv(t *testing.T) {
|
||||
for _, tt := range hookExtractCommandArgumentsForEnvTests {
|
||||
h := &Hook{ExecuteCommand: tt.exec, PassEnvironmentToCommand: tt.args}
|
||||
value, err := h.ExtractCommandArgumentsForEnv(tt.headers, tt.query, tt.payload, tt.context)
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
}
|
||||
value, err := h.ExtractCommandArgumentsForEnv(r)
|
||||
if (err == nil) != tt.ok || !reflect.DeepEqual(value, tt.value) {
|
||||
t.Errorf("failed to extract args for env {cmd=%q, args=%v}:\nexpected %#v, ok: %v\ngot %#v, ok: %v", tt.exec, tt.args, tt.value, tt.ok, value, (err == nil))
|
||||
}
|
||||
|
@ -448,24 +486,31 @@ func TestHooksMatch(t *testing.T) {
|
|||
var matchRuleTests = []struct {
|
||||
typ, regex, secret, value, ipRange string
|
||||
param Argument
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
body []byte
|
||||
remoteAddr string
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{"value", "", "", "z", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", true, false},
|
||||
{"regex", "^z", "", "z", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", true, false},
|
||||
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
{"value", "", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", true, false},
|
||||
{"regex", "^z", "", "z", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", true, false},
|
||||
{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "b17e04cbb22afa8ffbff8796fc1894ed27badd9e"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "f417af3a21bd70379b5796d5f013915e7029f62c580fb0f500f59a35a6f04c89"}, nil, nil, nil, []byte(`{"a": "z"}`), "", true, false},
|
||||
// failures
|
||||
{"value", "", "", "X", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, false},
|
||||
{"regex", "^X", "", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, false},
|
||||
{"value", "", "2", "X", "", Argument{"header", "a", "", false}, &map[string]interface{}{"Y": "z"}, nil, nil, nil, []byte{}, "", false, true}, // reference invalid header
|
||||
{"value", "", "", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, false},
|
||||
{"regex", "^X", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, false},
|
||||
{"value", "", "2", "X", "", Argument{"header", "a", "", false}, map[string]interface{}{"Y": "z"}, nil, nil, nil, []byte{}, "", false, true}, // reference invalid header
|
||||
// errors
|
||||
{"regex", "*", "", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, true}, // invalid regex
|
||||
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, &map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"regex", "*", "", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, "", false, true}, // invalid regex
|
||||
{"payload-hmac-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hash-sha1", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hmac-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hash-sha256", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hmac-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
{"payload-hash-sha512", "", "secret", "", "", Argument{"header", "a", "", false}, map[string]interface{}{"A": ""}, nil, nil, nil, []byte{}, "", false, true}, // invalid hmac
|
||||
|
||||
// IP whitelisting, valid cases
|
||||
{"ip-whitelist", "", "", "", "192.168.0.1/24", Argument{}, nil, 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, nil, []byte{}, "192.168.0.2:9000", true, false}, // valid IPv4, with range
|
||||
|
@ -484,7 +529,17 @@ var matchRuleTests = []struct {
|
|||
func TestMatchRule(t *testing.T) {
|
||||
for i, tt := range matchRuleTests {
|
||||
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.context, &tt.body, tt.remoteAddr)
|
||||
req := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
Body: tt.body,
|
||||
RawRequest: &http.Request{
|
||||
RemoteAddr: tt.remoteAddr,
|
||||
},
|
||||
}
|
||||
ok, err := r.Evaluate(req)
|
||||
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)
|
||||
}
|
||||
|
@ -494,7 +549,7 @@ func TestMatchRule(t *testing.T) {
|
|||
var andRuleTests = []struct {
|
||||
desc string // description of the test case
|
||||
rule AndRule
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
|
@ -505,7 +560,7 @@ var andRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"A": "z", "B": "y"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "z", "B": "y"}, nil, nil, nil, []byte{},
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
|
@ -514,7 +569,7 @@ var andRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "z", "B": "Y"}, nil, nil, nil, []byte{},
|
||||
false, false,
|
||||
},
|
||||
// Complex test to cover Rules.Evaluate
|
||||
|
@ -540,7 +595,7 @@ var andRuleTests = []struct {
|
|||
},
|
||||
},
|
||||
},
|
||||
&map[string]interface{}{"A": "z", "B": "y", "C": "x", "D": "w", "E": "X", "F": "X"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "z", "B": "y", "C": "x", "D": "w", "E": "X", "F": "X"}, nil, nil, nil, []byte{},
|
||||
true, false,
|
||||
},
|
||||
{"empty rule", AndRule{{}}, nil, nil, nil, nil, nil, false, false},
|
||||
|
@ -548,14 +603,21 @@ var andRuleTests = []struct {
|
|||
{
|
||||
"invalid rule",
|
||||
AndRule{{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}},
|
||||
&map[string]interface{}{"Y": "z"}, nil, nil, nil, nil,
|
||||
map[string]interface{}{"Y": "z"}, nil, nil, nil, nil,
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestAndRule(t *testing.T) {
|
||||
for _, tt := range andRuleTests {
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, tt.context, &tt.body, "")
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
Body: tt.body,
|
||||
}
|
||||
ok, err := tt.rule.Evaluate(r)
|
||||
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)
|
||||
}
|
||||
|
@ -565,7 +627,7 @@ func TestAndRule(t *testing.T) {
|
|||
var orRuleTests = []struct {
|
||||
desc string // description of the test case
|
||||
rule OrRule
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
|
@ -576,7 +638,7 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"A": "z", "B": "X"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "z", "B": "X"}, nil, nil, nil, []byte{},
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
|
@ -585,7 +647,7 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"A": "X", "B": "y"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "X", "B": "y"}, nil, nil, nil, []byte{},
|
||||
true, false,
|
||||
},
|
||||
{
|
||||
|
@ -594,7 +656,7 @@ var orRuleTests = []struct {
|
|||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
{Match: &MatchRule{"value", "", "", "y", Argument{"header", "b", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"A": "Z", "B": "Y"}, nil, nil, nil, []byte{},
|
||||
false, false,
|
||||
},
|
||||
// failures
|
||||
|
@ -603,14 +665,21 @@ var orRuleTests = []struct {
|
|||
OrRule{
|
||||
{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}},
|
||||
},
|
||||
&map[string]interface{}{"Y": "Z"}, nil, nil, nil, []byte{},
|
||||
map[string]interface{}{"Y": "Z"}, nil, nil, nil, []byte{},
|
||||
false, true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestOrRule(t *testing.T) {
|
||||
for _, tt := range orRuleTests {
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, tt.context, &tt.body, "")
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
Body: tt.body,
|
||||
}
|
||||
ok, err := tt.rule.Evaluate(r)
|
||||
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)
|
||||
}
|
||||
|
@ -620,18 +689,25 @@ func TestOrRule(t *testing.T) {
|
|||
var notRuleTests = []struct {
|
||||
desc string // description of the test case
|
||||
rule NotRule
|
||||
headers, query, payload, context *map[string]interface{}
|
||||
headers, query, payload, context map[string]interface{}
|
||||
body []byte
|
||||
ok bool
|
||||
err bool
|
||||
}{
|
||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, true, false},
|
||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, &map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, false, false},
|
||||
{"(a=z): !a=X", NotRule{Match: &MatchRule{"value", "", "", "X", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, true, false},
|
||||
{"(a=z): !a=z", NotRule{Match: &MatchRule{"value", "", "", "z", Argument{"header", "a", "", false}, ""}}, map[string]interface{}{"A": "z"}, nil, nil, nil, []byte{}, false, false},
|
||||
}
|
||||
|
||||
func TestNotRule(t *testing.T) {
|
||||
for _, tt := range notRuleTests {
|
||||
ok, err := tt.rule.Evaluate(tt.headers, tt.query, tt.payload, tt.context, &tt.body, "")
|
||||
r := &Request{
|
||||
Headers: tt.headers,
|
||||
Query: tt.query,
|
||||
Payload: tt.payload,
|
||||
Context: tt.context,
|
||||
Body: tt.body,
|
||||
}
|
||||
ok, err := tt.rule.Evaluate(r)
|
||||
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)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue