Skip to content

Commit

Permalink
Merge pull request #85 from intelops/save_results
Browse files Browse the repository at this point in the history
Enhance printing of results of Rego evaluation
  • Loading branch information
devopstoday11 committed Jun 3, 2024
2 parents 773d248 + a571450 commit e5a5174
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 30 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ genval
cosign
.todo
!.devcontainer/Dockerfile
results.json
18 changes: 18 additions & 0 deletions pkg/validate/celval.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ func EvaluateCELPolicies(policies []CELPolicy, inputFile string, t table.Writer)
green := color.New(color.FgGreen).SprintFunc()
red := color.New(color.FgRed).SprintFunc()

var allResults []Results
var idCounter int

for _, policy := range policies {
result, err := evaluateCEL(inputFile, policy.Rule)
var resultColorized string
Expand All @@ -72,6 +75,21 @@ func EvaluateCELPolicies(policies []CELPolicy, inputFile string, t table.Writer)
policy.Metadata.Severity,
policy.Metadata.Benchmark,
})
idCounter++
allResults = append(allResults, Results{
ID: fmt.Sprintf("%d", idCounter),
PolicyName: policy.Metadata.Name,
Status: resultColorized,
Description: policy.Metadata.Description,
Severity: policy.Metadata.Severity,
Benchmark: policy.Metadata.Benchmark,
})
}

if len(allResults) > 0 {
if err := SaveResults("results.json", allResults); err != nil {
return fmt.Errorf("error saving results: %v", err)
}
}

return nil
Expand Down
77 changes: 70 additions & 7 deletions pkg/validate/printresults.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package validate

import (
"encoding/json"
"errors"
"fmt"
"os"
Expand All @@ -18,38 +19,55 @@ func PrintResults(result rego.ResultSet, metas []*regoMetadata) error {
t.AppendHeader(table.Row{"Policy Name", "Status", "Description", "Severity", "Benchmark", "Category"})

var policyError error

// Match policy metadata outside the loop
matchedKey, meta, err := MatchPolicyMetadata(metas, result)
if err != nil {
return fmt.Errorf("error matching key and metadata name: %v", err)
}
var allResults []Results
var idCounter int

for _, r := range result {
if len(r.Expressions) > 0 {
keys := r.Expressions[0].Value.(map[string]interface{})
for key, value := range keys {
// Match policy metadata for each key
matchedKey, meta, err := MatchPolicyMetadata(metas, key)
if err != nil {
return fmt.Errorf("error matching key and metadata name: %v", err)
}
// Construct rows using the matched metadata
if key == matchedKey {
var status string
var saveStatus string
if policies, ok := value.([]interface{}); ok {
// Check if the slice is empty
if len(policies) > 0 {
saveStatus = "passed"
status = color.New(color.FgGreen).Sprint("passed")
} else {
saveStatus = "failed"
status = color.New(color.FgRed).Sprint("failed")
policyError = errors.New("policy evaluation failed: " + key)
}
} else {
// Handle other types of values (non-slice)
if value != nil {
saveStatus = "passed"
status = color.New(color.FgGreen).Sprint("passed")
} else {
saveStatus = "failed"
status = color.New(color.FgRed).Sprint("failed")
policyError = errors.New("policy evaluation failed: " + key)
}
}
t.AppendRow([]interface{}{key, status, meta.Description, meta.Severity, meta.Benchmark, meta.Category})
idCounter++
// Append results to allResults
allResults = append(allResults, Results{
ID: fmt.Sprintf("%d", idCounter),
PolicyName: key,
Status: saveStatus,
Description: meta.Description,
Severity: meta.Severity,
Benchmark: meta.Benchmark,
Category: meta.Category,
})
}
}
} else {
Expand All @@ -58,8 +76,53 @@ func PrintResults(result rego.ResultSet, metas []*regoMetadata) error {
}
}

// Render the table
fmt.Println("Rendering table")
// Render the table after processing all results
t.Render()

// Save all results to file as a single JSON array
if len(allResults) > 0 {
if err := SaveResults("results.json", allResults); err != nil {
return fmt.Errorf("error saving results: %v", err)
}
}

return policyError
}

type Results struct {
ID string `json:"id"`
PolicyName string `json:"policyName"`
Status string `json:"status"`
Description string `json:"description"`
Severity string `json:"severity"`
Benchmark string `json:"benchmark"`
Category string `json:"category"`
}

// SaveResults saves the results to a file as a JSON array
func SaveResults(filename string, newResults []Results) error {
// Serialize the results slice to JSON
data, err := json.MarshalIndent(newResults, "", " ")
if err != nil {
return fmt.Errorf("error marshalling results to JSON: %v", err)
}

// Check if the file exists
_, err = os.Stat(filename)
if os.IsNotExist(err) {
// If the file does not exist, create it
file, err := os.Create(filename)
if err != nil {
return fmt.Errorf("error creating file: %v", err)
}
defer file.Close()
}

// Write the JSON data to the file
if err := os.WriteFile(filename, data, 0o644); err != nil {
return fmt.Errorf("error writing JSON data to file: %v", err)
}

return nil
}
19 changes: 5 additions & 14 deletions pkg/validate/regometa.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"os"
"path/filepath"

"github.com/open-policy-agent/opa/rego"
)

func FetchRegoMetadata(policyDir, metaExt, regoExt string) ([]string, []string, error) {
Expand Down Expand Up @@ -60,18 +58,11 @@ func LoadRegoMetadata(filePaths []string) ([]*regoMetadata, error) {
}

// MatchPolicyMetadata matches the RegoMeta policy names with the Rego evaluation results and returns the matched key
func MatchPolicyMetadata(metas []*regoMetadata, results rego.ResultSet) (string, *regoMetadata, error) {
for _, r := range results {
if len(r.Expressions) > 0 {
keys := r.Expressions[0].Value.(map[string]interface{})
for key := range keys {
for _, meta := range metas {
if key == meta.PolicyName {
return key, meta, nil
}
}
}
func MatchPolicyMetadata(metas []*regoMetadata, key string) (string, *regoMetadata, error) {
for _, meta := range metas {
if key == meta.PolicyName {
return key, meta, nil
}
}
return "", nil, fmt.Errorf("no matching policy name found")
return "", nil, fmt.Errorf("no matching policy name found for key: %s", key)
}
11 changes: 11 additions & 0 deletions pkg/validate/results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
[
{
"id": "1",
"policyName": "multi_stage",
"status": "passed",
"description": "Use multi-stage Docker builds to minimize size and performance",
"severity": "High",
"benchmark": "CIS-4.2",
"category": "Infrastructure security"
}
]
2 changes: 1 addition & 1 deletion pkg/validate/testdata/rego/test/deny_latest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
"policy_file": "deny_latest.rego",
"policy_name": "deny_latest",
"severity": "High",
"Description": "Ensure images are pinned to a specific version/digest",
"Description": "Image does not have latest tag",
"Benchmark": "CIS-4.2",
"Category": "Infrastructure security"
}
13 changes: 5 additions & 8 deletions pkg/validate/validatedockerfile.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import (
log "github.com/sirupsen/logrus"
)

// ValidateDockerfileUsingRego validates a Dockerfile using Rego.
// ValidateDockerfileUsingRego validates a Dockerfile using Rego.
func ValidateDockerfile(dockerfileContent string, regoPolicyPath string) error {
metaFiles, regoPolicy, err := FetchRegoMetadata(regoPolicyPath, metaExt, policyExt)
Expand All @@ -28,7 +27,7 @@ func ValidateDockerfile(dockerfileContent string, regoPolicyPath string) error {
}

// Declare a slice to store the results of each policy evaluation
var allResults []rego.ResultSet
var allResults rego.ResultSet

for _, regoFile := range regoPolicy {
dockerPolicy, err := utils.ReadFile(regoFile)
Expand All @@ -38,7 +37,7 @@ func ValidateDockerfile(dockerfileContent string, regoPolicyPath string) error {

pkg, err := utils.ExtractPackageName(dockerPolicy)
if err != nil {
return fmt.Errorf("errr fetching package name from polcy %v: %v", dockerPolicy, err)
return fmt.Errorf("error fetching package name from policy %v: %v", dockerPolicy, err)
}

// Prepare Rego input data
Expand Down Expand Up @@ -88,14 +87,12 @@ func ValidateDockerfile(dockerfileContent string, regoPolicyPath string) error {
}

// Store the results in the slice
allResults = append(allResults, rs)
allResults = append(allResults, rs...)
}

// Print all results accumulated from each policy evaluation
for _, rs := range allResults {
if err := PrintResults(rs, metas); err != nil {
return fmt.Errorf("error evaluating rego results for %s: %v", regoPolicyPath, err)
}
if err := PrintResults(allResults, metas); err != nil {
return fmt.Errorf("error evaluating rego results for %s: %v", regoPolicyPath, err)
}

return nil
Expand Down

0 comments on commit e5a5174

Please sign in to comment.