Skip to content
Merged
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
50 changes: 2 additions & 48 deletions pkg/policies/engine/rego/rego.go
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,6 @@ const (
EnvironmentModePermissive EnvironmentMode = 1
inputArgs = "args"
inputElements = "elements"
deprecatedRule = "violations"
mainRule = "result"
)

Expand Down Expand Up @@ -185,63 +184,18 @@ func (r *Engine) Verify(ctx context.Context, policy *engine.Policy, input []byte
}
}

// Try the main rule first
// Try the main rule
if err := executeQuery(getRuleName(parsedModule.Package.Path, mainRule), r.operatingMode == EnvironmentModeRestrictive); err != nil {
return nil, err
}

// If res is nil, it means that the rule hasn't been found
// TODO: Remove when this deprecated rule is not used anymore
if res == nil {
// Try with the deprecated main rule
if err := executeQuery(getRuleName(parsedModule.Package.Path, deprecatedRule), r.operatingMode == EnvironmentModeRestrictive); err != nil {
return nil, err
}

if res == nil {
return nil, fmt.Errorf("failed to evaluate policy: neither '%s' nor '%s' rule found", mainRule, deprecatedRule)
}

return parseViolationsRule(res, policy, rawData)
return nil, fmt.Errorf("failed to evaluate policy: '%s' rule not found", mainRule)
}

return parseResultRule(res, policy, rawData)
}

// Parse deprecated list of violations.
// TODO: Remove this path once `result` rule is consolidated
func parseViolationsRule(res rego.ResultSet, policy *engine.Policy, rawData *engine.RawData) (*engine.EvaluationResult, error) {
violations := make([]*engine.PolicyViolation, 0)
for _, exp := range res {
for _, val := range exp.Expressions {
ruleResults, ok := val.Value.([]interface{})
if !ok {
return nil, engine.ResultFormatError{Field: deprecatedRule}
}

for _, result := range ruleResults {
reasonStr, ok := result.(string)
if !ok {
return nil, engine.ResultFormatError{Field: deprecatedRule}
}

violations = append(violations, &engine.PolicyViolation{
Subject: policy.Name,
Violation: reasonStr,
})
}
}
}

return &engine.EvaluationResult{
Violations: violations,
Skipped: false, // best effort
SkipReason: "",
Ignore: false, // Assume old rules should not be ignored
RawData: rawData,
}, nil
}

// parse `result` rule
func parseResultRule(res rego.ResultSet, policy *engine.Policy, rawData *engine.RawData) (*engine.EvaluationResult, error) {
result := &engine.EvaluationResult{Violations: make([]*engine.PolicyViolation, 0)}
Expand Down
2 changes: 1 addition & 1 deletion pkg/policies/engine/rego/rego_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ func TestRego_VerifyInvalidPolicy(t *testing.T) {
t.Run("doesn't eval a main rule", func(t *testing.T) {
_, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil)
assert.Error(t, err)
assert.Contains(t, err.Error(), "neither 'result' nor 'violations' rule found")
assert.Contains(t, err.Error(), "'result' rule not found")
})
}

Expand Down
31 changes: 30 additions & 1 deletion pkg/policies/engine/rego/testfiles/arguments.rego
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@ package main

import rego.v1

################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

# If the input is valid, check for any policy violation here
violations contains msg if {
valid_input
input.args.foo == "bar"

msg := "foo is bar"
}
31 changes: 30 additions & 1 deletion pkg/policies/engine/rego/testfiles/arguments_array.rego
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,37 @@ package main

import rego.v1

################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

# If the input is valid, check for any policy violation here
violations contains msg if {
valid_input
"bar" in input.args.foo

msg := "foo has bar"
}
46 changes: 38 additions & 8 deletions pkg/policies/engine/rego/testfiles/check_qa.rego
Original file line number Diff line number Diff line change
@@ -1,25 +1,55 @@
package main

violations[msg] {
not is_released
import rego.v1

################################
# Common section do NOT change #
################################

msg:= "Container image is not released"
result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

violations[msg] {
not is_approved
default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

msg:= "Container image is not approved"
########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

# If the input is valid, check for any policy violation here
violations contains msg if {
not is_released
msg := "Container image is not released"
}

violations contains msg if {
valid_input
not is_approved
msg := "Container image is not approved"
}

is_approved {
is_approved if {
input.kind == "CONTAINER_IMAGE"

input.references[i].metadata.name == "chainloop-platform-qa-approval"
input.references[i].annotations.approval == "true"
}

is_released {
is_released if {
input.kind == "CONTAINER_IMAGE"

input.references[i].metadata.name == "chainloop-platform-release-production"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package main

is_approved {
input.kind == "CONTAINER_IMAGE"
import rego.v1

# This policy intentionally has no violations or result rule to test error handling

is_approved if {
input.kind == "CONTAINER_IMAGE"
input.references[i].metadata.name == "chainloop-platform-qa-approval"
input.references[i].annotations.approval == "true"
}

is_released {
is_released if {
input.kind == "CONTAINER_IMAGE"

input.references[i].metadata.name == "chainloop-platform-release-production"
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,35 @@ package main

import rego.v1

################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

violations contains msg if {
kev := http.send({"method": "GET", "url": "https://www.chainloop.dev", "cache": true}).body

msg := ""
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,35 @@ package main

import rego.v1

################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

violations contains msg if {
http.send({"method": "GET", "url": "https://github.com"})

msg := ""
}
42 changes: 35 additions & 7 deletions pkg/policies/testdata/materials.rego
Original file line number Diff line number Diff line change
@@ -1,26 +1,54 @@
package main

import future.keywords.in
import future.keywords.contains
import rego.v1

# Verifies there is a VEX material, even if not enforced by contract

violations[msg] {
not has_vex
################################
# Common section do NOT change #
################################

result := {
"skipped": skipped,
"violations": violations,
"skip_reason": skip_reason,
}

default skip_reason := ""

skip_reason := m if {
not valid_input
m := "invalid input"
}

default skipped := true

skipped := false if valid_input

########################################
# EO Common section, custom code below #
########################################

# Validates if the input is valid and can be understood by this policy
valid_input := true

# If the input is valid, check for any policy violation here
violations contains msg if {
valid_input
not has_vex
msg := "missing VEX material"
}

# Collect all material types
kinds contains kind {
kinds contains kind if {
some material in input.predicate.materials
kind := material.annotations["chainloop.material.type"]
}

has_vex {
has_vex if {
"CSAF_VEX" in kinds
}

has_vex {
has_vex if {
"OPENVEX" in kinds
}
Loading
Loading