Skip to content

Commit

Permalink
fix(misconf): fix incorrect k8s locations due to JSON to YAML conversion
Browse files Browse the repository at this point in the history
Signed-off-by: nikpivkin <[email protected]>
  • Loading branch information
nikpivkin committed Dec 9, 2024
1 parent 51f2123 commit 59d2317
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 18 deletions.
7 changes: 5 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
module github.com/aquasecurity/trivy

go 1.22.9
go 1.23

toolchain go1.23.4

require (
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible
Expand Down Expand Up @@ -47,6 +49,7 @@ require (
github.com/docker/go-connections v0.5.0
github.com/fatih/color v1.18.0
github.com/go-git/go-git/v5 v5.12.0
github.com/go-json-experiment/json v0.0.0-20241208081943-bc21dd91e076
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 @@ -134,7 +137,7 @@ require (
k8s.io/api v0.31.2
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8
modernc.org/sqlite v1.34.1
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 @@ -649,6 +649,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.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-json-experiment/json v0.0.0-20241208081943-bc21dd91e076 h1:ojZyugKGeS0t4PtYFdkbQvvFUdNLM6VOMlx/46SQUHA=
github.com/go-json-experiment/json v0.0.0-20241208081943-bc21dd91e076/go.mod h1:BWmvoE1Xia34f3l/ibJweyhrT+aROb/FQ6d+37F0e2s=
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
Expand Down
81 changes: 81 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 All @@ -28,6 +34,81 @@ func (m *Manifest) UnmarshalYAML(value *yaml.Node) error {
return nil
}

func (m *Manifest) UnmarshalJSONV2(d *jsontext.Decoder, opts json.Options) error {
var node ManifestNode
if err := json.UnmarshalDecode(d, &node); err != nil {
return err
}
m.Content = &node
return nil
}

func (m *Manifest) ToRego() any {
return m.Content.ToRego()
}

func ParseManifestFromJSON(path string, data []byte) (*Manifest, error) {

root := &ManifestNode{
Path: path,
}

if err := json.Unmarshal(data, root, json.WithUnmarshalers(
json.UnmarshalFuncV2(func(dec *jsontext.Decoder, node *ManifestNode, opts json.Options) error {
startOffset := dec.InputOffset()
if err := unmarshalManifestNode(dec, node, opts); 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, opts json.Options) 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, opts); 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"))
}
14 changes: 6 additions & 8 deletions pkg/iac/scanners/kubernetes/parser/manifest_node.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func (r *ManifestNode) ToRego() any {
return r.Value
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 @@ -49,7 +49,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 All @@ -65,7 +65,6 @@ func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {

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

r.Value = node.Value
case TagInt:
val, err := strconv.Atoi(node.Value)
Expand All @@ -89,15 +88,14 @@ func (r *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
return r.handleMapTag(node)
case TagSlice:
return r.handleSliceTag(node)

default:
return fmt.Errorf("node tag is not supported %s", node.Tag)
}
return nil
}

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 @@ -108,15 +106,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 @@ -128,7 +126,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
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 := ParseManifestFromJSON(path, contents)
if err != nil {
return nil, err
}
return []any{manifest.ToRego()}, nil
}

var results []any
Expand Down

0 comments on commit 59d2317

Please sign in to comment.