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