-
Notifications
You must be signed in to change notification settings - Fork 317
/
Copy pathhelm_detect.go
188 lines (167 loc) · 5.13 KB
/
helm_detect.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
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package helm
import (
"fmt"
"sort"
"strconv"
"strings"
"github.com/Checkmarx/kics/pkg/detector"
"github.com/Checkmarx/kics/pkg/model"
"github.com/agnivade/levenshtein"
"github.com/rs/zerolog"
)
// DetectKindLine defines a kindDetectLine type
type DetectKindLine struct {
}
type detectCurlLine struct {
foundRes bool
lineRes int
breakRes bool
lastUnique dupHistory
}
// dupHistory keeps the history of uniques
type dupHistory struct {
unique bool
lastUniqueLine int
}
const (
undetectedVulnerabilityLine = -1
)
// DetectLine is used to detect line on the helm template,
// it looks only at the keys of the template and will make use of the auxiliary added
// lines (ex: "# KICS_HELM_ID_")
func (d DetectKindLine) DetectLine(file *model.FileMetadata, searchKey string,
outputLines int, logWithFields *zerolog.Logger) model.VulnerabilityLines {
searchKey = fmt.Sprintf("%s.%s", strings.TrimRight(strings.TrimLeft(file.HelmID, "# "), ":"), searchKey)
lines := *file.LinesOriginalData
curLineRes := detectCurlLine{
foundRes: false,
lineRes: 0,
breakRes: false,
}
var extractedString [][]string
extractedString = detector.GetBracketValues(searchKey, extractedString, "")
sanitizedSubstring := searchKey
for idx, str := range extractedString {
sanitizedSubstring = strings.Replace(sanitizedSubstring, str[0], `{{`+strconv.Itoa(idx)+`}}`, -1)
}
helmID, err := strconv.Atoi(strings.TrimSuffix(strings.TrimPrefix(file.HelmID, "# KICS_HELM_ID_"), ":"))
if err != nil {
helmID = -1
}
// Since we are only looking at keys we can ignore the second value passed through '=' and '[]'
for _, key := range strings.Split(sanitizedSubstring, ".") {
substr1, _ := detector.GenerateSubstrings(key, extractedString)
curLineRes = curLineRes.detectCurrentLine(lines, fmt.Sprintf("%s:", substr1), "", true, file.IDInfo, helmID)
if curLineRes.breakRes {
break
}
}
// Look at dupHistory to see if the last element was duplicate, if so
// change the line to the last unique key
if !curLineRes.lastUnique.unique {
curLineRes.lineRes = curLineRes.lastUnique.lastUniqueLine
}
if curLineRes.foundRes {
lineRemove := make(map[int]int)
count := 0
for i, line := range lines { // Remove auxiliary lines
if strings.Contains(line, "# KICS_HELM_ID_") {
count++
lineRemove[i] = count
lines = append(lines[:i], lines[i+1:]...)
}
}
// Update found line
curLineRes.lineRes = removeLines(curLineRes.lineRes, lineRemove)
return model.VulnerabilityLines{
Line: curLineRes.lineRes + 1,
VulnLines: detector.GetAdjacentVulnLines(curLineRes.lineRes, outputLines, lines),
LineWithVulnerability: strings.Split(lines[curLineRes.lineRes], ": ")[0],
ResolvedFile: file.FilePath,
}
}
logWithFields.Warn().Msgf("Failed to detect line, query response %s", searchKey)
return model.VulnerabilityLines{
Line: undetectedVulnerabilityLine,
VulnLines: &[]model.CodeLine{},
ResolvedFile: file.FilePath,
}
}
// removeLines is used to update the vulnerability line after removing the "# KICS_HELM_ID_"
func removeLines(current int, lineRemove map[int]int) int {
orderByKey := make([]int, len(lineRemove))
i := 0
for k := range lineRemove {
orderByKey[i] = k
i++
}
remove := 0
sort.Ints(orderByKey)
for _, k := range orderByKey {
if current > k {
remove = lineRemove[k]
} else {
break
}
}
current -= remove
return current
}
func (d detectCurlLine) detectCurrentLine(lines []string, str1,
str2 string, byKey bool, idInfo map[int]interface{}, id int) detectCurlLine {
distances := make(map[int]int)
for i := d.lineRes; i < len(lines); i++ {
if str1 != "" && str2 != "" {
if strings.Contains(lines[i], str1) && strings.Contains(lines[i], str2) {
distances[i] = levenshtein.ComputeDistance(detector.ExtractLineFragment(lines[i], str2, byKey), str2)
}
} else if str1 != "" {
if strings.Contains(lines[i], str1) {
distances[i] = levenshtein.ComputeDistance(
detector.ExtractLineFragment(strings.TrimSpace(lines[i]), str1, byKey), str1)
}
}
}
lastSingle := d.lastUnique.lastUniqueLine
if len(distances) == 0 {
return detectCurlLine{
foundRes: d.foundRes,
lineRes: d.lineRes,
breakRes: true,
lastUnique: dupHistory{
lastUniqueLine: lastSingle,
unique: d.lastUnique.unique,
},
}
}
lineResponse := detector.SelectLineWithMinimumDistance(distances, d.lineRes)
// if lineResponse is unique
unique := detectLastSingle(lineResponse, distances, idInfo, id)
if unique {
lastSingle = lineResponse
}
return detectCurlLine{
foundRes: true,
lineRes: lineResponse,
breakRes: false,
lastUnique: dupHistory{
unique: unique,
lastUniqueLine: lastSingle,
},
}
}
// detectLastSingle checks if the line is unique or a duplicate
func detectLastSingle(line int, dis map[int]int, idInfo map[int]interface{}, id int) bool {
if idInfo == nil {
return true
}
for key, value := range dis {
if value == dis[line] && key != line {
// check if we are only looking at original data equivalent to the vulnerability
if ok := idInfo[id].(map[int]int)[key]; ok != 0 {
return false
}
}
}
return true
}