Skip to content
This repository has been archived by the owner on Jan 22, 2025. It is now read-only.

Commit

Permalink
mv pkg/rego and pkg/rules from trivy-policies
Browse files Browse the repository at this point in the history
  • Loading branch information
simar7 committed Oct 18, 2023
1 parent c9527a1 commit 14e6103
Show file tree
Hide file tree
Showing 10 changed files with 1,293 additions and 0 deletions.
84 changes: 84 additions & 0 deletions pkg/rego/build.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package rego

import (
"io/fs"
"path/filepath"
"strings"

"github.com/aquasecurity/defsec/pkg/types"
"github.com/aquasecurity/trivy-iac/pkg/rego/schemas"
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/util"
)

func BuildSchemaSetFromPolicies(policies map[string]*ast.Module, paths []string, fsys fs.FS) (*ast.SchemaSet, bool, error) {
schemaSet := ast.NewSchemaSet()
schemaSet.Put(ast.MustParseRef("schema.input"), map[string]interface{}{}) // for backwards compat only
var customFound bool
for _, policy := range policies {
for _, annotation := range policy.Annotations {
for _, ss := range annotation.Schemas {
schemaName, err := ss.Schema.Ptr()
if err != nil {
continue
}
if schemaName != "input" {
if schema, ok := schemas.SchemaMap[types.Source(schemaName)]; ok {
customFound = true
schemaSet.Put(ast.MustParseRef(ss.Schema.String()), util.MustUnmarshalJSON([]byte(schema)))
} else {
b, err := findSchemaInFS(paths, fsys, schemaName)
if err != nil {
return schemaSet, true, err
}
if b != nil {
customFound = true
schemaSet.Put(ast.MustParseRef(ss.Schema.String()), util.MustUnmarshalJSON(b))
}
}
}
}
}
}

return schemaSet, customFound, nil
}

// findSchemaInFS tries to find the schema anywhere in the specified FS
func findSchemaInFS(paths []string, srcFS fs.FS, schemaName string) ([]byte, error) {
var schema []byte
for _, path := range paths {
if err := fs.WalkDir(srcFS, sanitisePath(path), func(path string, info fs.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
if !IsJSONFile(info.Name()) {
return nil
}
if info.Name() == schemaName+".json" {
schema, err = fs.ReadFile(srcFS, filepath.ToSlash(path))
if err != nil {
return err
}
return nil
}
return nil
}); err != nil {
return nil, err
}
}
return schema, nil
}

func IsJSONFile(name string) bool {
return strings.HasSuffix(name, ".json")
}

func sanitisePath(path string) string {
vol := filepath.VolumeName(path)
path = strings.TrimPrefix(path, vol)
return strings.TrimPrefix(strings.TrimPrefix(filepath.ToSlash(path), "./"), "/")
}
109 changes: 109 additions & 0 deletions pkg/rego/custom.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package rego

import (
"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/rego"
"github.com/open-policy-agent/opa/types"
)

func init() {
rego.RegisterBuiltin2(&rego.Function{
Name: "result.new",
Decl: types.NewFunction(types.Args(types.S, types.A), types.A),
},
createResult,
)

rego.RegisterBuiltin1(&rego.Function{
Name: "isManaged",
Decl: types.NewFunction(types.Args(types.A), types.B),
},
func(c rego.BuiltinContext, resource *ast.Term) (*ast.Term, error) {
metadata, err := createResult(c, ast.StringTerm(""), resource)
if err != nil {
return nil, err
}
return metadata.Get(ast.StringTerm("managed")), nil
},
)
}

func createResult(ctx rego.BuiltinContext, msg, cause *ast.Term) (*ast.Term, error) {

metadata := map[string]*ast.Term{
"startline": ast.IntNumberTerm(0),
"endline": ast.IntNumberTerm(0),
"sourceprefix": ast.StringTerm(""),
"filepath": ast.StringTerm(""),
"explicit": ast.BooleanTerm(false),
"managed": ast.BooleanTerm(true),
"fskey": ast.StringTerm(""),
"resource": ast.StringTerm(""),
"parent": ast.NullTerm(),
}
if msg != nil {
metadata["msg"] = msg
}

// universal
input := cause.Get(ast.StringTerm("__defsec_metadata"))
if input == nil {
// docker
input = cause
}
metadata = updateMetadata(metadata, input)

if term := input.Get(ast.StringTerm("parent")); term != nil {
var err error
metadata["parent"], err = createResult(ctx, nil, term)
if err != nil {
return nil, err
}
}

var values [][2]*ast.Term
for key, val := range metadata {
values = append(values, [2]*ast.Term{
ast.StringTerm(key),
val,
})
}
return ast.ObjectTerm(values...), nil
}

func updateMetadata(metadata map[string]*ast.Term, input *ast.Term) map[string]*ast.Term {
if term := input.Get(ast.StringTerm("startline")); term != nil {
metadata["startline"] = term
}
if term := input.Get(ast.StringTerm("StartLine")); term != nil {
metadata["startline"] = term
}
if term := input.Get(ast.StringTerm("endline")); term != nil {
metadata["endline"] = term
}
if term := input.Get(ast.StringTerm("EndLine")); term != nil {
metadata["endline"] = term
}
if term := input.Get(ast.StringTerm("filepath")); term != nil {
metadata["filepath"] = term
}
if term := input.Get(ast.StringTerm("sourceprefix")); term != nil {
metadata["sourceprefix"] = term
}
if term := input.Get(ast.StringTerm("Path")); term != nil {
metadata["filepath"] = term
}
if term := input.Get(ast.StringTerm("explicit")); term != nil {
metadata["explicit"] = term
}
if term := input.Get(ast.StringTerm("managed")); term != nil {
metadata["managed"] = term
}
if term := input.Get(ast.StringTerm("fskey")); term != nil {
metadata["fskey"] = term
}
if term := input.Get(ast.StringTerm("resource")); term != nil {
metadata["resource"] = term
}
return metadata
}
125 changes: 125 additions & 0 deletions pkg/rego/embed/embed.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
package embed

import (
"context"
"io/fs"
"path/filepath"
"strings"

"github.com/open-policy-agent/opa/ast"
"github.com/open-policy-agent/opa/bundle"

"github.com/aquasecurity/trivy-iac/pkg/rego"
"github.com/aquasecurity/trivy-iac/pkg/rules"
rules2 "github.com/aquasecurity/trivy-policies/rules"
)

func init() {

modules, err := LoadEmbeddedPolicies()
if err != nil {
// we should panic as the policies were not embedded properly
panic(err)
}
loadedLibs, err := LoadEmbeddedLibraries()
if err != nil {
panic(err)
}
for name, policy := range loadedLibs {
modules[name] = policy
}

RegisterRegoRules(modules)
}

func RegisterRegoRules(modules map[string]*ast.Module) {
ctx := context.TODO()

schemaSet, _, _ := rego.BuildSchemaSetFromPolicies(modules, nil, nil)

compiler := ast.NewCompiler().
WithSchemas(schemaSet).
WithCapabilities(nil).
WithUseTypeCheckAnnotations(true)

compiler.Compile(modules)
if compiler.Failed() {
// we should panic as the embedded rego policies are syntactically incorrect...
panic(compiler.Errors)
}

retriever := rego.NewMetadataRetriever(compiler)
for _, module := range modules {
metadata, err := retriever.RetrieveMetadata(ctx, module)
if err != nil {
continue
}
if metadata.AVDID == "" {
continue
}
rules.Register(
metadata.ToRule(),
nil,
)
}
}

func LoadEmbeddedPolicies() (map[string]*ast.Module, error) {
return LoadPoliciesFromDirs(rules2.EmbeddedPolicyFileSystem, ".")
}

func LoadEmbeddedLibraries() (map[string]*ast.Module, error) {
return LoadPoliciesFromDirs(rules2.EmbeddedLibraryFileSystem, ".")
}

func IsRegoFile(name string) bool {
return strings.HasSuffix(name, bundle.RegoExt) && !strings.HasSuffix(name, "_test"+bundle.RegoExt)
}

func IsDotFile(name string) bool {
return strings.HasPrefix(name, ".")
}

func sanitisePath(path string) string {
vol := filepath.VolumeName(path)
path = strings.TrimPrefix(path, vol)
return strings.TrimPrefix(strings.TrimPrefix(filepath.ToSlash(path), "./"), "/")
}

func LoadPoliciesFromDirs(target fs.FS, paths ...string) (map[string]*ast.Module, error) {
modules := make(map[string]*ast.Module)
for _, path := range paths {
if err := fs.WalkDir(target, sanitisePath(path), func(path string, info fs.DirEntry, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}

if strings.HasSuffix(filepath.Dir(filepath.ToSlash(path)), "policies/advanced/optional") {
return fs.SkipDir
}

if !IsRegoFile(info.Name()) || IsDotFile(info.Name()) {
return nil
}
data, err := fs.ReadFile(target, filepath.ToSlash(path))
if err != nil {
return err
}
module, err := ast.ParseModuleWithOpts(path, string(data), ast.ParserOptions{
ProcessAnnotation: true,
})
if err != nil {
// s.debug.Log("Failed to load module: %s, err: %s", filepath.ToSlash(path), err.Error())
return err
}
modules[path] = module
return nil
}); err != nil {
return nil, err
}
}
return modules, nil
}
Loading

0 comments on commit 14e6103

Please sign in to comment.