Skip to content

Commit

Permalink
fix(misconf): correctly handle all YAML tags in K8S templates (#8259)
Browse files Browse the repository at this point in the history
Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
  • Loading branch information
nikpivkin authored Jan 23, 2025
1 parent 4316bcb commit f12054e
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 15 deletions.
55 changes: 40 additions & 15 deletions pkg/iac/scanners/kubernetes/parser/manifest_node.go
Original file line number Diff line number Diff line change
@@ -1,22 +1,28 @@
package parser

import (
"encoding/base64"
"fmt"
"strconv"
"time"

"gopkg.in/yaml.v3"

"github.com/aquasecurity/trivy/pkg/log"
)

type TagType string

const (
TagBool TagType = "!!bool"
TagInt TagType = "!!int"
TagFloat TagType = "!!float"
TagStr TagType = "!!str"
TagString TagType = "!!string"
TagSlice TagType = "!!seq"
TagMap TagType = "!!map"
TagBool TagType = "!!bool"
TagInt TagType = "!!int"
TagFloat TagType = "!!float"
TagStr TagType = "!!str"
TagString TagType = "!!string"
TagSlice TagType = "!!seq"
TagMap TagType = "!!map"
TagTimestamp TagType = "!!timestamp"
TagBinary TagType = "!!binary"
)

type ManifestNode struct {
Expand All @@ -33,8 +39,14 @@ func (r *ManifestNode) ToRego() any {
return nil
}
switch r.Type {
case TagBool, TagInt, TagString, TagStr:
case TagBool, TagInt, TagFloat, TagString, TagStr, TagBinary:
return r.Value
case TagTimestamp:
t, ok := r.Value.(time.Time)
if !ok {
return nil
}
return t.Format(time.RFC3339)
case TagSlice:
var output []any
for _, node := range r.Value.([]ManifestNode) {
Expand All @@ -58,40 +70,53 @@ func (r *ManifestNode) ToRego() any {
}

func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {

r.StartLine = node.Line
r.EndLine = node.Line
r.Type = TagType(node.Tag)

switch TagType(node.Tag) {
case TagString, TagStr:

r.Value = node.Value
case TagInt:
val, err := strconv.Atoi(node.Value)
if err != nil {
return err
return fmt.Errorf("failed to parse int: %w", err)
}
r.Value = val
case TagFloat:
val, err := strconv.ParseFloat(node.Value, 64)
if err != nil {
return err
return fmt.Errorf("failed to parse float: %w", err)
}
r.Value = val
case TagBool:
val, err := strconv.ParseBool(node.Value)
if err != nil {
return err
return fmt.Errorf("failed to parse bool: %w", err)
}
r.Value = val
case TagTimestamp:
var val time.Time
if err := node.Decode(&val); err != nil {
return fmt.Errorf("failed to decode timestamp: %w", err)
}
r.Value = val
case TagBinary:
val, err := base64.StdEncoding.DecodeString(node.Value)
if err != nil {
return fmt.Errorf("failed to decode binary data: %w", err)
}
r.Value = val
case TagMap:
return r.handleMapTag(node)
case TagSlice:
return r.handleSliceTag(node)

default:
return fmt.Errorf("node tag is not supported %s", node.Tag)
log.WithPrefix("k8s").Debug("Skipping unsupported node tag",
log.String("tag", node.Tag),
log.FilePath(r.Path),
log.Int("line", node.Line),
)
}
return nil
}
Expand Down
69 changes: 69 additions & 0 deletions pkg/iac/scanners/kubernetes/parser/manifest_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package parser_test

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"

"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
)

func TestManifestToRego(t *testing.T) {
tests := []struct {
name string
src string
expected any
}{
{
name: "timestamp tag",
src: `field: !!timestamp 2024-04-01`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"offset": 0,
"startline": 1,
"endline": 1,
},
"field": "2024-04-01T00:00:00Z",
},
},
{
name: "binary tag",
src: `field: !!binary dGVzdA==`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"offset": 0,
"startline": 1,
"endline": 1,
},
"field": []uint8{0x74, 0x65, 0x73, 0x74},
},
},
{
name: "float tag",
src: `field: 1.1`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"offset": 0,
"startline": 1,
"endline": 1,
},
"field": 1.1,
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var manifest parser.Manifest
err := yaml.Unmarshal([]byte(tt.src), &manifest)
require.NoError(t, err)
data := manifest.ToRego()
assert.Equal(t, tt.expected, data)
})
}
}

0 comments on commit f12054e

Please sign in to comment.