refactor(misconf): add ManifestFromYAML for unified manifest parsing (#9680)

Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
Nikita Pivkin
2025-10-21 23:18:10 +06:00
committed by GitHub
parent 263aee07a3
commit c32ddfc522
4 changed files with 50 additions and 55 deletions

View File

@@ -10,13 +10,17 @@ import (
)
type Manifest struct {
Path string
Content *ManifestNode
FilePath string
Content *ManifestNode
}
func NewManifest(path string, root *ManifestNode) *Manifest {
func NewManifest(path string, root *ManifestNode) (*Manifest, error) {
if root.Type != TagMap {
return &Manifest{FilePath: path}, nil
}
root.Walk(func(n *ManifestNode) {
n.Path = path
n.FilePath = path
switch v := n.Value.(type) {
case []*ManifestNode:
n.Value = lo.Filter(v, func(vv *ManifestNode, _ int) bool {
@@ -29,38 +33,32 @@ func NewManifest(path string, root *ManifestNode) *Manifest {
}
})
return &Manifest{
Path: path,
Content: root,
}
}
func (m *Manifest) UnmarshalYAML(value *yaml.Node) error {
switch value.Tag {
case string(TagMap):
node := new(ManifestNode)
node.Path = m.Path
if err := value.Decode(node); err != nil {
return err
}
m.Content = node
default:
return fmt.Errorf("failed to handle tag: %s", value.Tag)
}
return nil
FilePath: path,
Content: root,
}, nil
}
func (m *Manifest) ToRego() any {
if m.Content == nil {
return nil
}
return m.Content.ToRego()
}
func ManifestFromJSON(path string, data []byte) (*Manifest, error) {
var root = &ManifestNode{}
if err := xjson.Unmarshal(data, root); err != nil {
return nil, err
return nil, fmt.Errorf("unmarshal json: %w", err)
}
return NewManifest(path, root), nil
return NewManifest(path, root)
}
func ManifestFromYAML(path string, data []byte) (*Manifest, error) {
var root = &ManifestNode{}
if err := yaml.Unmarshal(data, root); err != nil {
return nil, fmt.Errorf("unmarshal yaml: %w", err)
}
return NewManifest(path, root)
}

View File

@@ -31,10 +31,10 @@ const (
type ManifestNode struct {
xjson.Location
Offset int
Value any
Type TagType
Path string
Offset int
Value any
Type TagType
FilePath string
}
func (n *ManifestNode) ToRego() any {
@@ -72,7 +72,7 @@ func (n *ManifestNode) metadata() map[string]any {
return map[string]any{
"startline": n.StartLine,
"endline": n.EndLine,
"filepath": n.Path,
"filepath": n.FilePath,
"offset": n.Offset,
}
}
@@ -122,7 +122,7 @@ func (n *ManifestNode) UnmarshalYAML(node *yaml.Node) error {
default:
log.WithPrefix("k8s").Debug("Skipping unsupported node tag",
log.String("tag", node.Tag),
log.FilePath(n.Path),
log.FilePath(n.FilePath),
log.Int("line", node.Line),
)
}
@@ -134,7 +134,7 @@ func (n *ManifestNode) handleSliceTag(node *yaml.Node) error {
maxLine := node.Line
for _, contentNode := range node.Content {
newNode := new(ManifestNode)
newNode.Path = n.Path
newNode.FilePath = n.FilePath
if err := contentNode.Decode(newNode); err != nil {
return err
}
@@ -157,7 +157,7 @@ func (n *ManifestNode) handleMapTag(node *yaml.Node) error {
key = contentNode.Value
} else {
newNode := new(ManifestNode)
newNode.Path = n.Path
newNode.FilePath = n.FilePath
if err := contentNode.Decode(newNode); err != nil {
return err
}

View File

@@ -5,7 +5,6 @@ import (
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v3"
"github.com/aquasecurity/trivy/pkg/iac/scanners/kubernetes/parser"
)
@@ -86,6 +85,7 @@ func TestJsonManifestToRego(t *testing.T) {
}
func TestManifestToRego(t *testing.T) {
const filePath = "pod.json"
tests := []struct {
name string
src string
@@ -96,7 +96,7 @@ func TestManifestToRego(t *testing.T) {
src: `field: !!timestamp 2024-04-01`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"filepath": filePath,
"offset": 0,
"startline": 1,
"endline": 1,
@@ -109,7 +109,7 @@ func TestManifestToRego(t *testing.T) {
src: `field: !!binary dGVzdA==`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"filepath": filePath,
"offset": 0,
"startline": 1,
"endline": 1,
@@ -122,7 +122,7 @@ func TestManifestToRego(t *testing.T) {
src: `field: 1.1`,
expected: map[string]any{
"__defsec_metadata": map[string]any{
"filepath": "",
"filepath": filePath,
"offset": 0,
"startline": 1,
"endline": 1,
@@ -134,8 +134,7 @@ func TestManifestToRego(t *testing.T) {
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var manifest parser.Manifest
err := yaml.Unmarshal([]byte(tt.src), &manifest)
manifest, err := parser.ManifestFromYAML(filePath, []byte(tt.src))
require.NoError(t, err)
data := manifest.ToRego()
assert.Equal(t, tt.expected, data)

View File

@@ -3,12 +3,9 @@ package parser
import (
"bytes"
"context"
"fmt"
"io"
"regexp"
"strings"
"gopkg.in/yaml.v3"
)
func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
@@ -29,22 +26,23 @@ func Parse(_ context.Context, r io.Reader, path string) ([]any, error) {
return []any{manifest.ToRego()}, nil
}
var results []any
var manifests []any
re := regexp.MustCompile(`(?m:^---\r?\n)`)
pos := 0
offset := 0
for _, partial := range re.Split(string(contents), -1) {
var result Manifest
result.Path = path
if err := yaml.Unmarshal([]byte(partial), &result); err != nil {
return nil, fmt.Errorf("unmarshal yaml: %w", err)
manifest, err := ManifestFromYAML(path, []byte(partial))
if err != nil {
return nil, err
}
if result.Content != nil {
result.Content.Offset = pos
results = append(results, result.ToRego())
if manifest.Content != nil {
manifest.Content.Offset = offset
manifests = append(manifests, manifest.ToRego())
}
pos += len(strings.Split(partial, "\n"))
offset += len(strings.Split(partial, "\n"))
}
return results, nil
return manifests, nil
}