From 53ef97231df3f32a6b7d908a5e13e1ad4e049cac Mon Sep 17 00:00:00 2001 From: Fabio Bozzo Date: Tue, 17 Sep 2024 13:18:12 +0200 Subject: [PATCH] iterative glob.go and limit FuzzMatch input size --- capability/policy/glob.go | 64 +++++++++++++-------------------- capability/policy/match_test.go | 6 ++++ 2 files changed, 31 insertions(+), 39 deletions(-) diff --git a/capability/policy/glob.go b/capability/policy/glob.go index 23fe8f2..5ab2f9b 100644 --- a/capability/policy/glob.go +++ b/capability/policy/glob.go @@ -22,7 +22,7 @@ func validateGlobPattern(pattern string) bool { return true } -// globMatch matches a string against a pattern with '*' wildcards, handling escaped '\*' literals. +// globMatch matches a string against a pattern with * wildcards, handling escaped '\*' literals. func globMatch(pattern, str string) bool { if !validateGlobPattern(pattern) { return false @@ -31,55 +31,41 @@ func globMatch(pattern, str string) bool { // i is the index for the pattern // j is the index for the string var i, j int - for i < len(pattern) && j < len(str) { - switch pattern[i] { - case '*': - // skip consecutive '*' characters - for i < len(pattern) && pattern[i] == '*' { - i++ - } - if i == len(pattern) { - return true - } - // match the rest of the pattern - for j < len(str) { - if globMatch(pattern[i:], str[j:]) { - return true - } - j++ - } + // starIdx keeps track of the position of the last * in the pattern. + // matchIdx keeps track of the position in the string where the last * matched. + var starIdx, matchIdx int = -1, -1 - return false - case '\\': - // handle escaped '*' - i++ - if i < len(pattern) && pattern[i] == '*' { - if str[j] != '*' { - return false - } + for j < len(str) { + if i < len(pattern) && (pattern[i] == str[j] || pattern[i] == '\\' && i+1 < len(pattern) && pattern[i+1] == str[j]) { + // characters match or if there's an escaped character that matches + if pattern[i] == '\\' { + // skip the escape character i++ - j++ - } else { - if i >= len(pattern) || pattern[i] != str[j] { - return false - } - i++ - j++ - } - default: - if pattern[i] != str[j] { - return false } i++ j++ + } else if i < len(pattern) && pattern[i] == '*' { + // there's a * wildcard in the pattern + starIdx = i + matchIdx = j + i++ + } else if starIdx != -1 { + // there's a previous * wildcard, backtrack + i = starIdx + 1 + matchIdx++ + j = matchIdx + } else { + // no match found + return false } } - // check for remaining characters in pattern + // check for remaining characters in the pattern for i < len(pattern) && pattern[i] == '*' { i++ } - return i == len(pattern) && j == len(str) + // the entire pattern is processed, it's a match + return i == len(pattern) } diff --git a/capability/policy/match_test.go b/capability/policy/match_test.go index 4f7dc9b..d7d9360 100644 --- a/capability/policy/match_test.go +++ b/capability/policy/match_test.go @@ -491,6 +491,8 @@ func TestPolicyExamples(t *testing.T) { }) } +const maxFuzzInputLength = 128 + func FuzzMatch(f *testing.F) { // Policy + Data examples f.Add([]byte(`[["==", ".status", "draft"]]`), []byte(`{"status": "draft"}`)) @@ -520,6 +522,10 @@ func FuzzMatch(f *testing.F) { ) f.Fuzz(func(t *testing.T, policyBytes []byte, dataBytes []byte) { + if len(policyBytes) > maxFuzzInputLength || len(dataBytes) > maxFuzzInputLength { + t.Skip() + } + policyNode, err := ipld.Decode(policyBytes, dagjson.Decode) if err != nil { t.Skip()