-
Notifications
You must be signed in to change notification settings - Fork 2.4k
/
rule.go
137 lines (116 loc) · 3.08 KB
/
rule.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package ignore
import (
"regexp"
"slices"
"strings"
"time"
"github.com/samber/lo"
"github.com/aquasecurity/trivy/pkg/iac/types"
)
// Ignorer represents a function that checks if the rule should be ignored.
type Ignorer func(resultMeta types.Metadata, ignoredParam any) bool
type Rules []Rule
// Ignore checks if the rule should be ignored based on provided metadata, IDs, and ignorer functions.
func (r Rules) Ignore(m types.Metadata, ids []string, ignorers map[string]Ignorer) bool {
return slices.ContainsFunc(r, func(r Rule) bool {
return r.ignore(m, ids, ignorers)
})
}
func (r Rules) shift() {
var (
currentRange *types.Range
offset int
)
for i := len(r) - 1; i > 0; i-- {
currentRule, prevRule := r[i], r[i-1]
if !prevRule.isStartLine {
continue
}
if currentRange == nil {
currentRange = ¤tRule.rng
}
if prevRule.rng.GetStartLine()+1+offset == currentRule.rng.GetStartLine() {
r[i-1].rng = *currentRange
offset++
} else {
currentRange = nil
offset = 0
}
}
}
// Rule represents a rule for ignoring vulnerabilities.
type Rule struct {
rng types.Range
isStartLine bool
sections map[string]any
}
func (r Rule) ignore(m types.Metadata, ids []string, ignorers map[string]Ignorer) bool {
matchMeta, ok := r.matchRange(&m)
if !ok {
return false
}
ignorers = lo.Assign(defaultIgnorers(ids), ignorers)
for ignoreID, ignore := range ignorers {
if param, exists := r.sections[ignoreID]; exists {
if !ignore(*matchMeta, param) {
return false
}
}
}
return true
}
func (r Rule) matchRange(m *types.Metadata) (*types.Metadata, bool) {
metaHierarchy := m
for metaHierarchy != nil {
if r.rng.GetFilename() != metaHierarchy.Range().GetFilename() {
metaHierarchy = metaHierarchy.Parent()
continue
}
if metaHierarchy.Range().GetStartLine() == r.rng.GetStartLine()+1 ||
metaHierarchy.Range().GetStartLine() == r.rng.GetStartLine() {
return metaHierarchy, true
}
metaHierarchy = metaHierarchy.Parent()
}
return nil, false
}
func defaultIgnorers(ids []string) map[string]Ignorer {
return map[string]Ignorer{
"id": func(_ types.Metadata, param any) bool {
id, ok := param.(string)
if !ok {
return false
}
if id == "*" || len(ids) == 0 {
return true
}
return slices.ContainsFunc(ids, func(s string) bool {
return MatchPattern(s, id)
})
},
"exp": func(_ types.Metadata, param any) bool {
expiry, ok := param.(time.Time)
return ok && time.Now().Before(expiry)
},
}
}
// MatchPattern checks if the pattern string matches the input pattern.
// The wildcard '*' in the pattern matches any sequence of characters.
func MatchPattern(input, pattern string) bool {
matched, err := regexp.MatchString(regexpFromPattern(pattern), input)
return err == nil && matched
}
func regexpFromPattern(pattern string) string {
parts := strings.Split(pattern, "*")
if len(parts) == 1 {
return "^" + pattern + "$"
}
var sb strings.Builder
for i, literal := range parts {
if i > 0 {
sb.WriteString(".*")
}
sb.WriteString(regexp.QuoteMeta(literal))
}
return "^" + sb.String() + "$"
}