github.com/DataDog/datadog-agent/pkg/security/secl@v0.55.0-devel.0.20240517055856-10c4965fea94/compiler/eval/glob.go (about) 1 // Unless explicitly stated otherwise all files in this repository are licensed 2 // under the Apache License Version 2.0. 3 // This product includes software developed at Datadog (https://www.datadoghq.com/). 4 // Copyright 2016-present Datadog, Inc. 5 6 // Package eval holds eval related files 7 package eval 8 9 import ( 10 "errors" 11 "strings" 12 ) 13 14 // Glob describes file glob object 15 type Glob struct { 16 pattern string 17 elements []patternElement 18 isScalar bool 19 caseInsensitive bool 20 normalizePaths bool 21 } 22 23 func (g *Glob) contains(filename string) bool { 24 if len(g.elements) == 0 || len(filename) == 0 { 25 return false 26 } 27 28 // pattern "/" 29 if len(g.elements) == 2 && g.elements[1].pattern == "" { 30 return true 31 } 32 33 // normalize */ == /*/ 34 if g.elements[0].pattern == "*" { 35 filename = filename[1:] 36 } 37 38 for start, end, i := 0, 0, 0; end != len(filename); end++ { 39 if filename[end] == '/' { 40 elf, elp := filename[start:end], g.elements[i] 41 if !PatternMatchesWithSegments(elp, elf, g.caseInsensitive) && elp.pattern != "**" { 42 return false 43 } 44 start = end + 1 45 i++ 46 } 47 48 if i+1 > len(g.elements) { 49 return true 50 } 51 52 if end+1 >= len(filename) { 53 elf, elp := filename[start:end+1], g.elements[i] 54 if len(elf) == 0 { 55 return true 56 } 57 if !PatternMatchesWithSegments(elp, elf, g.caseInsensitive) && elp.pattern != "**" { 58 return false 59 } 60 } 61 } 62 63 return true 64 } 65 66 func (g *Glob) matches(filename string) bool { 67 if len(g.elements) == 0 || len(filename) == 0 { 68 return false 69 } 70 71 // normalize */ == /*/ 72 if g.elements[0].pattern == "*" { 73 filename = filename[1:] 74 } 75 76 var elp patternElement 77 var elf string 78 var start, end, i int 79 80 for start, end, i = 0, 0, 0; end != len(filename); end++ { 81 if filename[end] == '/' { 82 elf, elp = filename[start:end], g.elements[i] 83 if !PatternMatchesWithSegments(elp, elf, g.caseInsensitive) && elp.pattern != "**" { 84 return false 85 } 86 start = end + 1 87 i++ 88 } 89 90 if i+1 > len(g.elements) { 91 return elp.pattern == "**" 92 } 93 94 if end+1 >= len(filename) { 95 elf, elp = filename[start:end+1], g.elements[i] 96 if len(elf) == 0 { 97 return elp.pattern == "*" 98 } 99 if PatternMatchesWithSegments(elp, elf, g.caseInsensitive) && i+1 == len(g.elements) { 100 return true 101 } else if elp.pattern != "**" { 102 return false 103 } 104 } 105 } 106 107 elf, elp = filename[end:], g.elements[i+1] 108 if len(elf) == 0 { 109 return false 110 } 111 return PatternMatchesWithSegments(elp, elf, g.caseInsensitive) 112 } 113 114 // Contains returns whether the glob pattern matches the beginning of the filename 115 func (g *Glob) Contains(filename string) bool { 116 if g.normalizePaths { 117 // normalize to linux-like paths 118 filename = strings.ReplaceAll(filename, "\\", "/") 119 } 120 121 return g.contains(filename) 122 } 123 124 // Matches the given filename 125 func (g *Glob) Matches(filename string) bool { 126 if g.normalizePaths { 127 // normalize to linux-like paths 128 filename = strings.ReplaceAll(filename, "\\", "/") 129 } 130 131 if g.isScalar { 132 if g.caseInsensitive { 133 return strings.EqualFold(g.pattern, filename) 134 } 135 return g.pattern == filename 136 } 137 return g.matches(filename) 138 } 139 140 // NewGlob returns a new glob object from the given pattern 141 func NewGlob(pattern string, caseInsensitive bool, normalizePaths bool) (*Glob, error) { 142 if normalizePaths { 143 // normalize to linux-like paths 144 pattern = strings.ReplaceAll(pattern, "\\", "/") 145 } 146 147 els := strings.Split(pattern, "/") 148 elements := make([]patternElement, 0, len(els)) 149 for i, el := range els { 150 if el == "**" && i+1 != len(els) || strings.Contains(el, "**") && len(el) != len("**") { 151 return nil, errors.New("`**` is allowed only at the end of patterns") 152 } 153 elements = append(elements, newPatternElement(el)) 154 } 155 156 return &Glob{ 157 pattern: pattern, 158 elements: elements, 159 isScalar: !strings.Contains(pattern, "*"), 160 caseInsensitive: caseInsensitive, 161 normalizePaths: normalizePaths, 162 }, nil 163 }