From b366ded6e0426c09c00e256a1d0ba536ef31fb83 Mon Sep 17 00:00:00 2001 From: Michael Schurter Date: Wed, 18 Dec 2024 09:55:50 -0800 Subject: [PATCH] test: in and contains are dangerous The `in` and `contains` operators work on multiple data types: strings (as a substring test) and containers (as a membership test). Unfortunately it has been observed that some authorization servers will mix types in their responses which are processed by bexpr in auth binding rules. For example an authorization server may return either: ``` {"Name": "Alice", "Role": "db_admin"} {"Name": "Bob", "Role": ["admin", "foo"]} ``` A binding rule attempting to match on the `admin` role would be written as: ``` admin in Role Role contains admin ``` Unfortunately these bexpr expressions will match *both* Alice and Bob due to substring matching. --- evaluate_test.go | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/evaluate_test.go b/evaluate_test.go index 89d312b..138ad38 100644 --- a/evaluate_test.go +++ b/evaluate_test.go @@ -622,6 +622,47 @@ func TestCustomTag(t *testing.T) { } } +func TestInOnOperator(t *testing.T) { + type testStruct struct { + Role any + Match bool + } + + exprs := []string{ + "Role contains admin", + "admin in Role", + } + cases := []testStruct{ + { + Role: []string{"admin", "foo"}, + Match: true, + }, + { + Role: "admin", + Match: true, + }, + { + Role: "dbadmin", + Match: true, // dangerous! + }, + { + Role: []string{"dbadmin", "foo"}, + Match: false, + }, + } + + for _, q := range exprs { + expr, err := CreateEvaluator(q) + require.NoError(t, err) + + for _, tc := range cases { + match, err := expr.Evaluate(tc) + require.NoError(t, err) + require.Equal(t, tc.Match, match) + } + } +} + func BenchmarkEvaluate(b *testing.B) { for name, tcase := range evaluateTests { // capture these values in the closure