diff --git a/ast/builtins.go b/ast/builtins.go index d03c99c585..bfec6f914a 100644 --- a/ast/builtins.go +++ b/ast/builtins.go @@ -2604,7 +2604,10 @@ var GlobMatch = &Builtin{ Decl: types.NewFunction( types.Args( types.Named("pattern", types.S), - types.Named("delimiters", types.NewArray(nil, types.S)).Description("glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset."), + types.Named("delimiters", types.NewAny( + types.NewArray(nil, types.S), + types.NewNull(), + )).Description("glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset. If `delimiters` is `null`, glob match without delimiter."), types.Named("match", types.S), ), types.Named("result", types.B).Description("true if `match` can be found in `pattern` which is separated by `delimiters`"), diff --git a/builtin_metadata.json b/builtin_metadata.json index 11bd60f52e..c78eef0068 100644 --- a/builtin_metadata.json +++ b/builtin_metadata.json @@ -3677,9 +3677,9 @@ "type": "string" }, { - "description": "glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset.", + "description": "glob pattern delimiters, e.g. `[\".\", \":\"]`, defaults to `[\".\"]` if unset. If `delimiters` is `null`, glob match without delimiter.", "name": "delimiters", - "type": "array[string]" + "type": "any\u003cnull, array[string]\u003e" }, { "name": "match", diff --git a/capabilities.json b/capabilities.json index 9269d89a53..f61c6f85c8 100644 --- a/capabilities.json +++ b/capabilities.json @@ -894,10 +894,18 @@ "type": "string" }, { - "dynamic": { - "type": "string" - }, - "type": "array" + "of": [ + { + "type": "null" + }, + { + "dynamic": { + "type": "string" + }, + "type": "array" + } + ], + "type": "any" }, { "type": "string" diff --git a/docs/content/policy-reference.md b/docs/content/policy-reference.md index f22e945cf9..bbe0a1defc 100644 --- a/docs/content/policy-reference.md +++ b/docs/content/policy-reference.md @@ -337,6 +337,7 @@ The following table shows examples of how ``glob.match`` works: | -------- | ---------- | ----------- | | ``output := glob.match("*.github.com", [], "api.github.com")`` | ``true`` | A glob with the default ``["."]`` delimiter. | | ``output := glob.match("*.github.com", [], "api.cdn.github.com")`` | ``false`` | A glob with the default ``["."]`` delimiter. | +| ``output := glob.match("*hub.com", null, "api.cdn.github.com")`` | ``true`` | A glob without delimiter. | | ``output := glob.match("*:github:com", [":"], "api:github:com")`` | ``true`` | A glob with delimiters ``[":"]``. | | ``output := glob.match("api.**.com", [], "api.github.com")`` | ``true`` | A super glob. | | ``output := glob.match("api.**.com", [], "api.cdn.github.com")`` | ``true`` | A super glob. | diff --git a/internal/compiler/wasm/opa/callgraph.csv b/internal/compiler/wasm/opa/callgraph.csv index bc23b52f85..fe88adcb54 100644 --- a/internal/compiler/wasm/opa/callgraph.csv +++ b/internal/compiler/wasm/opa/callgraph.csv @@ -941,24 +941,23 @@ opa_glob_match,opa_value_get opa_glob_match,operator\20new\28unsigned\20long\29 opa_glob_match,memcpy opa_glob_match,operator\20delete\28void*\29 +opa_glob_match,void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29 opa_glob_match,opa_builtin_cache_get opa_glob_match,opa_builtin_cache_set opa_glob_match,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 opa_glob_match,std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29 opa_glob_match,std::__1::basic_string\2c\20std::__1::allocator\20>::operator=\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 opa_glob_match,glob_translate\28char\20const*\2c\20unsigned\20long\2c\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20const&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>*\29 -opa_glob_match,cache\28\29 opa_glob_match,std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29 opa_glob_match,std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29 -opa_glob_match,std::__1::pair\2c\20std::__1::allocator\20>\20>::~pair\28\29 opa_glob_match,opa_string opa_glob_match,opa_regex_match opa_glob_match,abort +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,operator\20new\28unsigned\20long\29 +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,operator\20delete\28void*\29 +void\20std::__1::vector\2c\20std::__1::allocator\20>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>::__push_back_slow_path\2c\20std::__1::allocator\20>\20>\28std::__1::basic_string\2c\20std::__1::allocator\20>&&\29,abort std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29,std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>::operator\28\29\28cache_key\20const&\29\20const std::__1::__hash_iterator\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::find\28cache_key\20const&\29,std::__1::equal_to::operator\28\29\28cache_key\20const&\2c\20cache_key\20const&\29\20const -cache\28\29,opa_builtin_cache_get -cache\28\29,operator\20new\28unsigned\20long\29 -cache\28\29,opa_builtin_cache_set std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,std::__1::basic_string\2c\20std::__1::allocator\20>::basic_string\28std::__1::basic_string\2c\20std::__1::allocator\20>\20const&\29 std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,operator\20new\28unsigned\20long\29 std::__1::pair\2c\20std::__1::allocator\20>\20>::pair\2c\20std::__1::allocator\20>&\2c\20false>\28cache_key&\2c\20std::__1::basic_string\2c\20std::__1::allocator\20>&\29,abort @@ -967,7 +966,6 @@ std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,operator\20new\28unsigned\20long\29 std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::__next_prime\28unsigned\20long\29 std::__1::pair\2c\20std::__1::allocator\20>\20>\2c\20void*>*>\2c\20bool>\20std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__emplace_unique_key_args\2c\20std::__1::allocator\20>\20>\20>\28cache_key\20const&\2c\20std::__1::pair\2c\20std::__1::allocator\20>\20>&&\29,std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29 -std::__1::pair\2c\20std::__1::allocator\20>\20>::~pair\28\29,operator\20delete\28void*\29 std::__1::equal_to::operator\28\29\28cache_key\20const&\2c\20cache_key\20const&\29\20const,memcmp std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,operator\20new\28unsigned\20long\29 std::__1::__hash_table\2c\20std::__1::allocator\20>\20>\2c\20std::__1::__unordered_map_hasher\2c\20std::__1::allocator\20>\20>\2c\20std::__1::hash\2c\20std::__1::equal_to\2c\20true>\2c\20std::__1::__unordered_map_equal\2c\20std::__1::allocator\20>\20>\2c\20std::__1::equal_to\2c\20std::__1::hash\2c\20true>\2c\20std::__1::allocator\2c\20std::__1::allocator\20>\20>\20>\20>::__rehash\28unsigned\20long\29,operator\20delete\28void*\29 diff --git a/test/cases/testdata/globmatch/test-globmatch-0159.yaml b/test/cases/testdata/globmatch/test-globmatch-0159.yaml new file mode 100644 index 0000000000..f69746b37b --- /dev/null +++ b/test/cases/testdata/globmatch/test-globmatch-0159.yaml @@ -0,0 +1,29 @@ +cases: +- data: {} + modules: + - | + package generated + + p[x] { + glob.match("*", null, "foo.bar", x) + } + note: globmatch/glob match single without default delimiter + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true +- data: {} + modules: + - | + package generated + + p[x] { + glob.match("foo*", null, "foo.bar", x) + } + note: globmatch/glob match single without default delimiter, glob non-empty + query: data.generated.p = x + sort_bindings: true + want_result: + - x: + - true \ No newline at end of file diff --git a/topdown/glob.go b/topdown/glob.go index fd55cad6ad..e22cda44fd 100644 --- a/topdown/glob.go +++ b/topdown/glob.go @@ -18,16 +18,22 @@ func builtinGlobMatch(a, b, c ast.Value) (ast.Value, error) { if err != nil { return nil, err } + var delimiters []rune + switch b.(type) { + case ast.Null: + delimiters = []rune{} + case *ast.Array: + delimiters, err = builtins.RuneSliceOperand(b, 2) + if err != nil { + return nil, err + } - delimiters, err := builtins.RuneSliceOperand(b, 2) - if err != nil { - return nil, err - } - - if len(delimiters) == 0 { - delimiters = []rune{'.'} + if len(delimiters) == 0 { + delimiters = []rune{'.'} + } + default: + return nil, builtins.NewOperandTypeErr(2, b, "array", "null") } - match, err := builtins.StringOperand(c, 3) if err != nil { return nil, err diff --git a/wasm/src/glob-compiler.cc b/wasm/src/glob-compiler.cc index 40fbc54a51..5b6aaad785 100644 --- a/wasm/src/glob-compiler.cc +++ b/wasm/src/glob-compiler.cc @@ -128,7 +128,7 @@ std::string glob_translate(const char *glob, size_t n, const std::vectorv, s->len)); prev = curr; } + + // NOTE(sr): If we're passed an empty array, use "." as default delimiter. + // If we're passed OPA_NULL, use no delimiter; but separate glob parts by '.*' + if (opa_value_type(delimiters) == OPA_ARRAY) { + if (v.empty()) + { + v.push_back(std::string(".")); + } + } glob_cache *c = cache(); cache_key key = cache_key(std::string(p->v, p->len), v); diff --git a/wasm/tests/test-glob.cc b/wasm/tests/test-glob.cc index e349bdc80a..504aeaabf5 100644 --- a/wasm/tests/test-glob.cc +++ b/wasm/tests/test-glob.cc @@ -234,6 +234,7 @@ void test_glob_translate() for (int i = 0; i < sizeof(delimiters)/sizeof(const char*); i++) { \ v.push_back(delimiters[i]); \ } \ + if (v.empty()) { v.push_back(std::string(".")); } \ glob_translate(pattern, strlen(pattern), v, &re2); \ test_str_eq(test_case, expected, re2.c_str()); \ re2::RE2::Options options; \