Skip to content

Commit f12054e

Browse files
authored
fix(misconf): correctly handle all YAML tags in K8S templates (#8259)
Signed-off-by: nikpivkin <[email protected]>
1 parent 4316bcb commit f12054e

File tree

2 files changed

+109
-15
lines changed

2 files changed

+109
-15
lines changed

pkg/iac/scanners/kubernetes/parser/manifest_node.go

+40-15
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,28 @@
11
package parser
22

33
import (
4+
"encoding/base64"
45
"fmt"
56
"strconv"
7+
"time"
68

79
"gopkg.in/yaml.v3"
10+
11+
"github.com/aquasecurity/trivy/pkg/log"
812
)
913

1014
type TagType string
1115

1216
const (
13-
TagBool TagType = "!!bool"
14-
TagInt TagType = "!!int"
15-
TagFloat TagType = "!!float"
16-
TagStr TagType = "!!str"
17-
TagString TagType = "!!string"
18-
TagSlice TagType = "!!seq"
19-
TagMap TagType = "!!map"
17+
TagBool TagType = "!!bool"
18+
TagInt TagType = "!!int"
19+
TagFloat TagType = "!!float"
20+
TagStr TagType = "!!str"
21+
TagString TagType = "!!string"
22+
TagSlice TagType = "!!seq"
23+
TagMap TagType = "!!map"
24+
TagTimestamp TagType = "!!timestamp"
25+
TagBinary TagType = "!!binary"
2026
)
2127

2228
type ManifestNode struct {
@@ -33,8 +39,14 @@ func (r *ManifestNode) ToRego() any {
3339
return nil
3440
}
3541
switch r.Type {
36-
case TagBool, TagInt, TagString, TagStr:
42+
case TagBool, TagInt, TagFloat, TagString, TagStr, TagBinary:
3743
return r.Value
44+
case TagTimestamp:
45+
t, ok := r.Value.(time.Time)
46+
if !ok {
47+
return nil
48+
}
49+
return t.Format(time.RFC3339)
3850
case TagSlice:
3951
var output []any
4052
for _, node := range r.Value.([]ManifestNode) {
@@ -58,40 +70,53 @@ func (r *ManifestNode) ToRego() any {
5870
}
5971

6072
func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
61-
6273
r.StartLine = node.Line
6374
r.EndLine = node.Line
6475
r.Type = TagType(node.Tag)
6576

6677
switch TagType(node.Tag) {
6778
case TagString, TagStr:
68-
6979
r.Value = node.Value
7080
case TagInt:
7181
val, err := strconv.Atoi(node.Value)
7282
if err != nil {
73-
return err
83+
return fmt.Errorf("failed to parse int: %w", err)
7484
}
7585
r.Value = val
7686
case TagFloat:
7787
val, err := strconv.ParseFloat(node.Value, 64)
7888
if err != nil {
79-
return err
89+
return fmt.Errorf("failed to parse float: %w", err)
8090
}
8191
r.Value = val
8292
case TagBool:
8393
val, err := strconv.ParseBool(node.Value)
8494
if err != nil {
85-
return err
95+
return fmt.Errorf("failed to parse bool: %w", err)
96+
}
97+
r.Value = val
98+
case TagTimestamp:
99+
var val time.Time
100+
if err := node.Decode(&val); err != nil {
101+
return fmt.Errorf("failed to decode timestamp: %w", err)
102+
}
103+
r.Value = val
104+
case TagBinary:
105+
val, err := base64.StdEncoding.DecodeString(node.Value)
106+
if err != nil {
107+
return fmt.Errorf("failed to decode binary data: %w", err)
86108
}
87109
r.Value = val
88110
case TagMap:
89111
return r.handleMapTag(node)
90112
case TagSlice:
91113
return r.handleSliceTag(node)
92-
93114
default:
94-
return fmt.Errorf("node tag is not supported %s", node.Tag)
115+
log.WithPrefix("k8s").Debug("Skipping unsupported node tag",
116+
log.String("tag", node.Tag),
117+
log.FilePath(r.Path),
118+
log.Int("line", node.Line),
119+
)
95120
}
96121
return nil
97122
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package parser_test
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
"github.com/stretchr/testify/require"
8+
"gopkg.in/yaml.v3"
9+
10+
"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
11+
)
12+
13+
func TestManifestToRego(t *testing.T) {
14+
tests := []struct {
15+
name string
16+
src string
17+
expected any
18+
}{
19+
{
20+
name: "timestamp tag",
21+
src: `field: !!timestamp 2024-04-01`,
22+
expected: map[string]any{
23+
"__defsec_metadata": map[string]any{
24+
"filepath": "",
25+
"offset": 0,
26+
"startline": 1,
27+
"endline": 1,
28+
},
29+
"field": "2024-04-01T00:00:00Z",
30+
},
31+
},
32+
{
33+
name: "binary tag",
34+
src: `field: !!binary dGVzdA==`,
35+
expected: map[string]any{
36+
"__defsec_metadata": map[string]any{
37+
"filepath": "",
38+
"offset": 0,
39+
"startline": 1,
40+
"endline": 1,
41+
},
42+
"field": []uint8{0x74, 0x65, 0x73, 0x74},
43+
},
44+
},
45+
{
46+
name: "float tag",
47+
src: `field: 1.1`,
48+
expected: map[string]any{
49+
"__defsec_metadata": map[string]any{
50+
"filepath": "",
51+
"offset": 0,
52+
"startline": 1,
53+
"endline": 1,
54+
},
55+
"field": 1.1,
56+
},
57+
},
58+
}
59+
60+
for _, tt := range tests {
61+
t.Run(tt.name, func(t *testing.T) {
62+
var manifest parser.Manifest
63+
err := yaml.Unmarshal([]byte(tt.src), &manifest)
64+
require.NoError(t, err)
65+
data := manifest.ToRego()
66+
assert.Equal(t, tt.expected, data)
67+
})
68+
}
69+
}

0 commit comments

Comments
 (0)