Skip to content

Commit

Permalink
fix(misconf): fix incorrect k8s locations due to JSON to YAML convers…
Browse files Browse the repository at this point in the history
…ion (aquasecurity#8073)

Signed-off-by: nikpivkin <[email protected]>
  • Loading branch information
nikpivkin authored and dstrelbytskyi committed Mar 5, 2025
1 parent 8291af1 commit 5b31b36
Show file tree
Hide file tree
Showing 6 changed files with 156 additions and 15 deletions.
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ require (
github.com/docker/go-units v0.5.0
github.com/fatih/color v1.18.0
github.com/go-git/go-git/v5 v5.13.2
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 // Replace with encoding/json/v2 when proposal is accepted. Track https://github.com/golang/go/issues/71497
github.com/go-openapi/runtime v0.28.0 // indirect
github.com/go-openapi/strfmt v0.23.0 // indirect
github.com/go-redis/redis/v8 v8.11.5
Expand Down Expand Up @@ -133,7 +134,7 @@ require (
k8s.io/api v0.32.2
k8s.io/utils v0.0.0-20241104100929-3ea5e8cea738
modernc.org/sqlite v1.35.0
sigs.k8s.io/yaml v1.4.0
sigs.k8s.io/yaml v1.4.0 // indirect
)

require (
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1126,6 +1126,8 @@ github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535 h1:yE7argOs92u+sSCRgqqe6eF+cDaVhSPlioy1UkA0p/w=
github.com/go-json-experiment/json v0.0.0-20250211171154-1ae217ad3535/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U=
github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk=
Expand Down
71 changes: 71 additions & 0 deletions pkg/iac/scanners/kubernetes/parser/manifest.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package parser

import (
"bytes"
"errors"
"fmt"
"io"
"reflect"

"github.com/go-json-experiment/json"
"github.com/go-json-experiment/json/jsontext"
"gopkg.in/yaml.v3"
)

Expand Down Expand Up @@ -31,3 +37,68 @@ func (m *Manifest) UnmarshalYAML(value *yaml.Node) error {
func (m *Manifest) ToRego() any {
return m.Content.ToRego()
}

func ManifestFromJSON(path string, data []byte) (*Manifest, error) {
root := &ManifestNode{
Path: path,
}

if err := json.Unmarshal(data, root, json.WithUnmarshalers(
json.UnmarshalFromFunc(func(dec *jsontext.Decoder, node *ManifestNode, opts json.Options) error {
startOffset := dec.InputOffset()
if err := unmarshalManifestNode(dec, node); err != nil {
return err
}
endOffset := dec.InputOffset()
node.StartLine = 1 + countLines(data, int(startOffset))
node.EndLine = 1 + countLines(data, int(endOffset))
node.Path = path
return nil
})),
); err != nil && !errors.Is(err, io.EOF) {
return nil, err
}

return &Manifest{
Path: path,
Content: root,
}, nil
}

func unmarshalManifestNode(dec *jsontext.Decoder, node *ManifestNode) error {
var valPtr any
var nodeType TagType
switch k := dec.PeekKind(); k {
case 't', 'f':
valPtr = new(bool)
nodeType = TagBool
case '"':
nodeType = TagStr
valPtr = new(string)
case '0':
nodeType = TagInt
valPtr = new(uint64)
case '[', 'n':
valPtr = new([]*ManifestNode)
nodeType = TagSlice
case '{':
valPtr = new(map[string]*ManifestNode)
nodeType = TagMap
case 0:
return dec.SkipValue()
default:
return fmt.Errorf("unexpected token kind %q at %d", k.String(), dec.InputOffset())
}

if err := json.UnmarshalDecode(dec, valPtr); err != nil {
return err
}

node.Value = reflect.ValueOf(valPtr).Elem().Interface()
node.Type = nodeType
return nil
}

func countLines(data []byte, offset int) int {
return bytes.Count(data[:offset], []byte("\n"))
}
12 changes: 6 additions & 6 deletions pkg/iac/scanners/kubernetes/parser/manifest_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ func (r *ManifestNode) ToRego() any {
return t.Format(time.RFC3339)
case TagSlice:
var output []any
for _, node := range r.Value.([]ManifestNode) {
for _, node := range r.Value.([]*ManifestNode) {
output = append(output, node.ToRego())
}
return output
Expand All @@ -61,7 +61,7 @@ func (r *ManifestNode) ToRego() any {
"filepath": r.Path,
"offset": r.Offset,
}
for key, node := range r.Value.(map[string]ManifestNode) {
for key, node := range r.Value.(map[string]*ManifestNode) {
output[key] = node.ToRego()
}
return output
Expand Down Expand Up @@ -122,7 +122,7 @@ func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
}

func (r *ManifestNode) handleSliceTag(node *yaml.Node) error {
var nodes []ManifestNode
var nodes []*ManifestNode
maxLine := node.Line
for _, contentNode := range node.Content {
newNode := new(ManifestNode)
Expand All @@ -133,15 +133,15 @@ func (r *ManifestNode) handleSliceTag(node *yaml.Node) error {
if newNode.EndLine > maxLine {
maxLine = newNode.EndLine
}
nodes = append(nodes, *newNode)
nodes = append(nodes, newNode)
}
r.EndLine = maxLine
r.Value = nodes
return nil
}

func (r *ManifestNode) handleMapTag(node *yaml.Node) error {
output := make(map[string]ManifestNode)
output := make(map[string]*ManifestNode)
var key string
maxLine := node.Line
for i, contentNode := range node.Content {
Expand All @@ -153,7 +153,7 @@ func (r *ManifestNode) handleMapTag(node *yaml.Node) error {
if err := contentNode.Decode(newNode); err != nil {
return err
}
output[key] = *newNode
output[key] = newNode
if newNode.EndLine > maxLine {
maxLine = newNode.EndLine
}
Expand Down
73 changes: 73 additions & 0 deletions pkg/iac/scanners/kubernetes/parser/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,79 @@ import (
"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
)

func TestJsonManifestToRego(t *testing.T) {
content := `{
"apiVersion": "v1",
"kind": "Pod",
"metadata": {
"name": "hello-cpu-limit"
},
"spec": {
"containers": [
{
"command": [
"sh",
"-c",
"echo 'Hello' && sleep 1h"
],
"image": "busybox",
"name": "hello"
}
]
}
}`

const filePath = "pod.json"
manifest, err := parser.ManifestFromJSON(filePath, []byte(content))
require.NoError(t, err)

expected := map[string]any{
"__defsec_metadata": map[string]any{
"filepath": filePath,
"offset": 0,
"startline": 1,
"endline": 20,
},
"apiVersion": "v1",
"kind": "Pod",
"metadata": map[string]any{
"__defsec_metadata": map[string]any{
"filepath": filePath,
"offset": 0,
"startline": 4,
"endline": 6,
},
"name": "hello-cpu-limit",
},
"spec": map[string]any{
"__defsec_metadata": map[string]any{
"filepath": filePath,
"offset": 0,
"startline": 7,
"endline": 19,
},
"containers": []any{
map[string]any{
"__defsec_metadata": map[string]any{
"filepath": filePath,
"offset": 0,
"startline": 8,
"endline": 17,
},
"command": []any{
"sh",
"-c",
"echo 'Hello' && sleep 1h",
},
"image": "busybox",
"name": "hello",
},
},
},
}
assert.Equal(t, expected, manifest.ToRego())
}

func TestManifestToRego(t *testing.T) {
tests := []struct {
name string
Expand Down
10 changes: 2 additions & 8 deletions pkg/iac/scanners/kubernetes/parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,12 @@ package parser

import (
"context"
"encoding/json"
"fmt"
"io"
"regexp"
"strings"

"gopkg.in/yaml.v3"
kyaml "sigs.k8s.io/yaml"
)

func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
Expand All @@ -23,15 +21,11 @@ func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
}

if strings.TrimSpace(string(contents))[0] == '{' {
var target any
if err := json.Unmarshal(contents, &target); err != nil {
return nil, err
}

contents, err = kyaml.JSONToYAML(contents) // convert into yaml to reuse file parsing logic
manifest, err := ManifestFromJSON(path, contents)
if err != nil {
return nil, err
}
return []any{manifest.ToRego()}, nil
}

var results []any
Expand Down

0 comments on commit 5b31b36

Please sign in to comment.