Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions contains_in_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package bexpr

import (
"testing"

"github.com/hashicorp/go-bexpr/grammar"
)

func TestContainsVsIn(t *testing.T) {
claims := map[string]any{
"userinfo": map[string]any{
"groups": "totallynotanadmin",
"email": "admin@company.com",
},
}

tests := []struct {
name string
filter string
expect bool
}{
{
name: "in does not find admin in totallynotanadmin",
filter: `"admin" in "/userinfo/groups"`,
expect: false,
},
{
name: "contains finds admin in totallynotanadmin",
filter: `"/userinfo/groups" contains "admin"`,
expect: true,
},
{
name: "contains on email does substring match",
filter: `"/userinfo/email" contains "@company.com"`,
expect: true,
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
ast, _ := grammar.Parse("", []byte(tt.filter))
expr := ast.(*grammar.MatchExpression)

eval, _ := CreateEvaluator(tt.filter)
result, _ := eval.Evaluate(claims)

t.Logf("\n Filter: %s\n Operator: %s\n Result: %v [expect: %v]",
tt.filter, expr.Operator, result, tt.expect)

if result != tt.expect {
t.Errorf("expected %v, got %v", tt.expect, result)
}
})
}
}
25 changes: 23 additions & 2 deletions evaluate.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,13 +178,26 @@ func doMatchIn(expression *grammar.MatchExpression, value reflect.Value) (bool,
}

case reflect.String:
return strings.Contains(value.String(), matchValue.(string)), nil
return false, nil

default:
return false, fmt.Errorf("cannot perform in/contains operations on type %s for selector: %q", kind, expression.Selector)
return false, fmt.Errorf("cannot perform in operations on type %s for selector: %q", kind, expression.Selector)
}
}

func doMatchContains(expression *grammar.MatchExpression, value reflect.Value) (bool, error) {
if value.Kind() != reflect.String {
return false, fmt.Errorf("contains operator only works on strings, not %s for selector: %q", value.Kind(), expression.Selector)
}

matchValue, err := getMatchExprValue(expression, reflect.String)
if err != nil {
return false, fmt.Errorf("error getting match value in expression: %w", err)
}

return strings.Contains(value.String(), matchValue.(string)), nil
}

func doMatchIsEmpty(matcher *grammar.MatchExpression, value reflect.Value) (bool, error) {
// NOTE: see preconditions in evaluategrammar.MatchExpressionRecurse
return value.Len() == 0, nil
Expand Down Expand Up @@ -369,6 +382,14 @@ func evaluateMatchExpression(expression *grammar.MatchExpression, datum interfac
return !result, nil
}
return false, err
case grammar.MatchContains:
return doMatchContains(expression, rvalue)
case grammar.MatchNotContains:
result, err := doMatchContains(expression, rvalue)
if err == nil {
return !result, nil
}
return false, err
default:
return false, fmt.Errorf("invalid match operation: %d", expression.Operator)
}
Expand Down
10 changes: 10 additions & 0 deletions grammar/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ const (
MatchIsNotEmpty
MatchMatches
MatchNotMatches
MatchContains
MatchNotContains
)

func (op MatchOperator) String() string {
Expand All @@ -79,6 +81,10 @@ func (op MatchOperator) String() string {
return "Matches"
case MatchNotMatches:
return "Not Matches"
case MatchContains:
return "Contains"
case MatchNotContains:
return "Not Contains"
default:
return "UNKNOWN"
}
Expand Down Expand Up @@ -113,6 +119,10 @@ func (op MatchOperator) NotPresentDisposition() bool {
case MatchNotMatches:
// M["x"] not matches <anything> is true. Nothing matches a missing key
return true
case MatchContains:
return false
case MatchNotContains:
return true
default:
// Should never be reached as every operator should explicitly define its
// behavior.
Expand Down
4 changes: 2 additions & 2 deletions grammar/grammar.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions grammar/grammar.peg
Original file line number Diff line number Diff line change
Expand Up @@ -135,10 +135,10 @@ MatchNotIn <- _ "not" _ "in" _ {
return MatchNotIn, nil
}
MatchContains <- _ "contains" _ {
return MatchIn, nil
return MatchContains, nil
}
MatchNotContains <- _ "not" _ "contains" _ {
return MatchNotIn, nil
return MatchNotContains, nil
}
MatchMatches <- _ "matches" _ {
return MatchMatches, nil
Expand Down
Loading