fix(misconf): do not skip loading documents from subdirectories (#8526)

Signed-off-by: nikpivkin <nikita.pivkin@smartforce.io>
This commit is contained in:
Nikita Pivkin
2025-03-22 12:01:04 +06:00
committed by GitHub
parent f07030daf2
commit de7eb13938
2 changed files with 66 additions and 13 deletions

View File

@@ -3,26 +3,43 @@ package rego
import (
"fmt"
"io/fs"
"os"
"path/filepath"
"path"
"slices"
"strings"
"github.com/open-policy-agent/opa/v1/loader"
"github.com/open-policy-agent/opa/v1/storage"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/set"
)
// initialize a store populated with OPA data files found in dataPaths
func initStore(dataFS fs.FS, dataPaths, namespaces []string) (storage.Store, error) {
// FilteredPaths will recursively find all file paths that contain a valid document
// extension from the given list of data paths.
allDocumentPaths, _ := loader.FilteredPathsFS(dataFS, dataPaths,
func(abspath string, info os.FileInfo, depth int) bool {
return !isDataFile(info)
},
)
dataFiles := set.New[string]()
documents, err := loader.NewFileLoader().WithFS(dataFS).All(allDocumentPaths)
// The virtual file system uses a slash ('/') as a path separator,
// but OPA uses the filepath package, which is OS-dependent.
// Therefore, we need to collect all the paths ourselves and pass them to OPA.
for _, root := range dataPaths {
if err := fs.WalkDir(dataFS, root, func(path string, d fs.DirEntry, err error) error {
if err != nil {
return err
}
if d.IsDir() {
return nil
}
if isDataFile(path) {
dataFiles.Append(path)
}
return nil
}); err != nil {
log.Error("Failed to collect data file paths", log.String("root", root), log.Err(err))
}
}
documents, err := loader.NewFileLoader().WithFS(dataFS).All(dataFiles.Items())
if err != nil {
return nil, fmt.Errorf("load documents: %w", err)
}
@@ -37,10 +54,10 @@ func initStore(dataFS fs.FS, dataPaths, namespaces []string) (storage.Store, err
return store, nil
}
func isDataFile(fi fs.FileInfo) bool {
return !fi.IsDir() && slices.Contains([]string{
func isDataFile(filePath string) bool {
return slices.Contains([]string{
".yaml",
".yml",
".json",
}, strings.ToLower(filepath.Ext(fi.Name())))
}, strings.ToLower(path.Ext(filePath)))
}

View File

@@ -0,0 +1,36 @@
package rego
import (
"encoding/json"
"testing"
"testing/fstest"
"github.com/open-policy-agent/opa/v1/storage"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestInitStore(t *testing.T) {
fsys := fstest.MapFS{
"test1.yml": &fstest.MapFile{Data: []byte("foo: 1")},
"test2.yaml": &fstest.MapFile{Data: []byte("bar: 2")},
"test3.json": &fstest.MapFile{Data: []byte(`{"baz": 3}`)},
"dir/test4.yaml": &fstest.MapFile{Data: []byte("qux: 4")},
}
store, err := initStore(fsys, []string{"."}, []string{"builtin.aws.test", "user.test"})
require.NoError(t, err)
tx, err := store.NewTransaction(t.Context())
require.NoError(t, err)
doc, err := store.Read(t.Context(), tx, storage.MustParsePath("/"))
require.NoError(t, err)
expected := map[string]any{
"foo": json.Number("1"),
"bar": json.Number("2"),
"baz": json.Number("3"),
"qux": json.Number("4"),
"namespaces": []any{"builtin.aws.test", "user.test"},
}
assert.Equal(t, expected, doc)
}