Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: migrate REGO functions to Go #1527

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -85,3 +85,5 @@ require (
gopkg.in/yaml.v2 v2.4.0 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
)

replace github.com/aquasecurity/trivy-policies => github.com/nikpivkin/trivy-policies v0.0.0-20240122112512-4cefa920d85c
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ github.com/apparentlymart/go-textseg/v13 v13.0.0 h1:Y+KvPE1NYz0xl601PVImeQfFyEy6
github.com/apparentlymart/go-textseg/v13 v13.0.0/go.mod h1:ZK2fH7c4NqDTLtiYLvIkEghdlcqw7yxLeM89kiTRPUo=
github.com/apparentlymart/go-textseg/v15 v15.0.0 h1:uYvfpb3DyLSCGWnctWKGj857c6ew1u1fNQOlOtuGxQY=
github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4=
github.com/aquasecurity/trivy-policies v0.8.0 h1:LvmIdw/DfTF72Lc8L+CKLYzfb5BFYzLBGFFR95PKC74=
github.com/aquasecurity/trivy-policies v0.8.0/go.mod h1:qF/t59pgK/0JTV6tXaeA3Iw3opzoMgzGCDcTDBmqb30=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
Expand Down Expand Up @@ -122,6 +120,8 @@ github.com/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQ
github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0=
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
github.com/nikpivkin/trivy-policies v0.0.0-20240122112512-4cefa920d85c h1:haHvIlIYDbyg3FcFrsPWcaEwBGBAkuPwPQ6GegoWXUE=
github.com/nikpivkin/trivy-policies v0.0.0-20240122112512-4cefa920d85c/go.mod h1:I8cjIJ/PzMkhIY+2DVT2Vco0USiOqXjw04HzSEZXp9c=
github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
github.com/open-policy-agent/opa v0.60.0 h1:ZPoPt4yeNs5UXCpd/P/btpSyR8CR0wfhVoh9BOwgJNs=
github.com/open-policy-agent/opa v0.60.0/go.mod h1:aD5IK6AiLNYBjNXn7E02++yC8l4Z+bRDvgM6Ss0bBzA=
Expand Down
109 changes: 0 additions & 109 deletions pkg/rego/custom.go

This file was deleted.

198 changes: 123 additions & 75 deletions pkg/rego/result.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ import (
"github.com/open-policy-agent/opa/rego"
)

const denyMessage = "Rego policy resulted in DENY"

type regoResult struct {
Filepath string
Resource string
Expand All @@ -24,6 +26,13 @@ type regoResult struct {
Parent *regoResult
}

func messageResult(msg string) *regoResult {
return &regoResult{
Managed: true,
Message: msg,
}
}

func (r regoResult) GetMetadata() defsecTypes.Metadata {
var m defsecTypes.Metadata
if !r.Managed {
Expand All @@ -46,102 +55,147 @@ func (r regoResult) GetRawValue() interface{} {
return nil
}

func parseResult(raw interface{}) *regoResult {
var result regoResult
result.Managed = true
switch val := raw.(type) {
case []interface{}:
var msg string
for _, item := range val {
switch raw := item.(type) {
case map[string]interface{}:
result = parseCause(raw)
case string:
msg = raw
}
func (r *regoResult) applyOffset(offset int) {
r.StartLine += offset
r.EndLine += offset
}

func (r *regoResult) updateMeta(raw map[string]any) {
for k, v := range raw {
switch k {
case "startline", "StartLine":
r.StartLine = parseLineNumber(v)
case "endline", "EndLine":
r.EndLine = parseLineNumber(v)
case "filepath", "Path":
r.Filepath = getString(v)
case "sourceprefix":
r.SourcePrefix = getString(v)
case "explicit":
r.Explicit = getBool(v)
case "managed":
r.Managed = getBool(v)
case "fskey":
r.FSKey = getString(v)
case "resource":
r.Resource = getString(v)
}
result.Message = msg
case string:
result.Message = val
case map[string]interface{}:
result = parseCause(val)
default:
result.Message = "Rego policy resulted in DENY"
}
return &result
}

func parseCause(cause map[string]interface{}) regoResult {
var result regoResult
result.Managed = true
if msg, ok := cause["msg"]; ok {
result.Message = fmt.Sprintf("%s", msg)
func getString(raw any) string {
if str, ok := raw.(string); ok {
return str
}
if filepath, ok := cause["filepath"]; ok {
result.Filepath = fmt.Sprintf("%s", filepath)
return ""
}

func getBool(raw any) bool {
if b, ok := raw.(bool); ok {
return b
}
if msg, ok := cause["fskey"]; ok {
result.FSKey = fmt.Sprintf("%s", msg)
return false
}

func newRegoResult(rawInput any) *regoResult {
result := &regoResult{
Managed: true,
}
if msg, ok := cause["resource"]; ok {
result.Resource = fmt.Sprintf("%s", msg)

input, ok := rawInput.(map[string]any)
if !ok {
return result
}
if start, ok := cause["startline"]; ok {
result.StartLine = parseLineNumber(start)

if rawMsg, exists := input["msg"]; exists {
if msg, ok := rawMsg.(string); ok {
result.Message = msg
}
}
if end, ok := cause["endline"]; ok {
result.EndLine = parseLineNumber(end)

meta := parseMetadata(input)
result.updateMeta(meta)

if parent, ok := meta["parent"]; ok {
result.Parent = newRegoResult(map[string]any{"metadata": parent})
}
if prefix, ok := cause["sourceprefix"]; ok {
result.SourcePrefix = fmt.Sprintf("%s", prefix)

return result
}

func parseMetadata(input map[string]any) map[string]any {
res := make(map[string]any)
rawMetadata, exists := input["metadata"]
if !exists {
// for backward compatibility
rawMetadata = input
}
if explicit, ok := cause["explicit"]; ok {
if set, ok := explicit.(bool); ok {
result.Explicit = set
}

cause, ok := rawMetadata.(map[string]any)
if !ok {
return res
}
if managed, ok := cause["managed"]; ok {
if set, ok := managed.(bool); ok {
result.Managed = set

rawDefsecMeta, exists := cause["__defsec_metadata"]
if !exists {
res = cause
} else {
defsecMeta, ok := rawDefsecMeta.(map[string]any)
if !ok {
return res
}
res = defsecMeta
}
if parent, ok := cause["parent"]; ok {
if m, ok := parent.(map[string]interface{}); ok {
parentResult := parseCause(m)
result.Parent = &parentResult

return res
}

func parseResult(raw any) *regoResult {

switch val := raw.(type) {
case []any:
var msg string
var result *regoResult
for _, item := range val {
switch raw := item.(type) {
case map[string]any:
if res := newRegoResult(raw); res != nil {
result = res
}
case string:
msg = raw
}
}
if result != nil {
result.Message = msg
return result
}
return messageResult(msg)
case string:
return messageResult(val)
case map[string]any:
return newRegoResult(val)
default:
return messageResult(denyMessage)
}
return result
}

func parseLineNumber(raw interface{}) int {
str := fmt.Sprintf("%s", raw)
n, _ := strconv.Atoi(str)
func parseLineNumber(raw any) int {
n, _ := strconv.Atoi(fmt.Sprintf("%s", raw))
return n
}

func (s *Scanner) convertResults(set rego.ResultSet, input Input, namespace string, rule string, traces []string) scan.Results {
var results scan.Results

offset := 0
if input.Contents != nil {
if xx, ok := input.Contents.(map[string]interface{}); ok {
if md, ok := xx["__defsec_metadata"]; ok {
if md2, ok := md.(map[string]interface{}); ok {
if sl, ok := md2["offset"]; ok {
offset, _ = sl.(int)
}
}
}
}
}
offset := input.GetOffset()

for _, result := range set {
for _, expression := range result.Expressions {
values, ok := expression.Value.([]interface{})
values, ok := expression.Value.([]any)
if !ok {
values = []interface{}{expression.Value}
values = []any{expression.Value}
}

for _, value := range values {
regoResult := parseResult(value)
regoResult.FS = input.FS
Expand All @@ -151,16 +205,10 @@ func (s *Scanner) convertResults(set rego.ResultSet, input Input, namespace stri
if regoResult.Message == "" {
regoResult.Message = fmt.Sprintf("Rego policy rule: %s.%s", namespace, rule)
}
regoResult.StartLine += offset
regoResult.EndLine += offset
regoResult.applyOffset(offset)
results.AddRego(regoResult.Message, namespace, rule, traces, regoResult)
}
}
}
return results
}

func (s *Scanner) embellishResultsWithRuleMetadata(results scan.Results, metadata StaticMetadata) scan.Results {
results.SetRule(metadata.ToRule())
return results
}
Loading