|
1 | 1 | package rules |
2 | 2 |
|
3 | 3 | import ( |
4 | | - "bytes" |
5 | | - "fmt" |
6 | 4 | "github.com/thedevsaddam/gojsonq/v2" |
7 | | - "regexp" |
8 | | - "strings" |
9 | 5 | ) |
10 | 6 |
|
11 | | -// TODO(ajm): tighten these matches, they could be "[seccomp..." or " seccomp...", and "unconfined]" or "unconfined " |
12 | | -// TODO(ajm): space delimiting matches is insufficient as this could be set to `unconfined blah` |
13 | | -func SeccompAny(json []byte) int { |
14 | | - containers := 0 |
15 | | - startWordBoundaryRegex := "[\\[ ]" |
16 | | - endWordBoundaryRegex := "[\\] ]" |
17 | | - |
18 | | - capDrop := gojsonq.New().Reader(bytes.NewReader(json)). |
19 | | - From("metadata.annotations").Get() |
20 | | - |
21 | | - capDropString := fmt.Sprintf("%v", capDrop) |
| 7 | +// isSeccompUnconfined checks the seccompProfile.type field and returns a checkSecurityContextResult struct. |
| 8 | +// If the field is set then unset=false. If, on top of that, the value of Unconfined matches the expected value |
| 9 | +// then return valid=true. |
| 10 | +func isSeccompUnconfined(jq *gojsonq.JSONQ, expectedUnconfined bool) checkSecurityContextResult { |
| 11 | + value := jq.From("securityContext.seccompProfile.type").Get() |
22 | 12 |
|
23 | | - if capDrop != nil && strings.Contains(capDropString, "seccomp.security.alpha.kubernetes.io/pod:") { |
24 | | - if !strings.Contains(capDropString, "seccomp.security.alpha.kubernetes.io/pod:unconfined") { |
25 | | - containers++ |
26 | | - } |
27 | | - } else if capDrop != nil { |
| 13 | + v, ok := value.(string) |
28 | 14 |
|
29 | | - keyNameRegex := "seccomp\\.security\\.alpha\\.kubernetes\\.io/[a-zA-Z-.]+" |
30 | | - // TODO(ajm) match end of string in regex |
31 | | - isNamedPodMatch, _ := regexp.MatchString(startWordBoundaryRegex+keyNameRegex+":", capDropString) |
| 15 | + res := checkSecurityContextResult{} |
| 16 | + if !ok { |
| 17 | + res.unset = true |
| 18 | + return res |
| 19 | + } |
32 | 20 |
|
33 | | - if isNamedPodMatch { |
34 | | - isUnconfinedNamedPodMatch, _ := regexp.MatchString(startWordBoundaryRegex+keyNameRegex+":unconfined"+endWordBoundaryRegex, capDropString) |
35 | | - if !isUnconfinedNamedPodMatch { |
36 | | - containers++ |
37 | | - } |
38 | | - } |
| 21 | + return checkSecurityContextResult{ |
| 22 | + valid: (v == "Unconfined") == expectedUnconfined, |
39 | 23 | } |
| 24 | +} |
40 | 25 |
|
41 | | - return containers |
| 26 | +// SeccompAny retrieves the number of instances in a manifest where the Seccomp profile has been specified |
| 27 | +// to a value other than 'Unconfined' |
| 28 | +func SeccompAny(json []byte) int { |
| 29 | + return checkSecurityContext( |
| 30 | + json, |
| 31 | + true, // present in PodSecurityContext |
| 32 | + func(jq *gojsonq.JSONQ) checkSecurityContextResult { |
| 33 | + return isSeccompUnconfined(jq, false) |
| 34 | + }) |
42 | 35 | } |
0 commit comments