diff --git a/pkg/policies/engine/rego/rego.go b/pkg/policies/engine/rego/rego.go index a71e018fa..e21ac98b3 100644 --- a/pkg/policies/engine/rego/rego.go +++ b/pkg/policies/engine/rego/rego.go @@ -101,7 +101,6 @@ const ( EnvironmentModePermissive EnvironmentMode = 1 inputArgs = "args" inputElements = "elements" - deprecatedRule = "violations" mainRule = "result" ) @@ -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)} diff --git a/pkg/policies/engine/rego/rego_test.go b/pkg/policies/engine/rego/rego_test.go index 048681937..70e0d6528 100644 --- a/pkg/policies/engine/rego/rego_test.go +++ b/pkg/policies/engine/rego/rego_test.go @@ -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") }) } diff --git a/pkg/policies/engine/rego/testfiles/arguments.rego b/pkg/policies/engine/rego/testfiles/arguments.rego index b60572a04..4a32f63bc 100644 --- a/pkg/policies/engine/rego/testfiles/arguments.rego +++ b/pkg/policies/engine/rego/testfiles/arguments.rego @@ -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" } diff --git a/pkg/policies/engine/rego/testfiles/arguments_array.rego b/pkg/policies/engine/rego/testfiles/arguments_array.rego index 0d7fd417e..d75197ddc 100644 --- a/pkg/policies/engine/rego/testfiles/arguments_array.rego +++ b/pkg/policies/engine/rego/testfiles/arguments_array.rego @@ -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" } diff --git a/pkg/policies/engine/rego/testfiles/check_qa.rego b/pkg/policies/engine/rego/testfiles/check_qa.rego index 36ffcb1c5..bc19ac54a 100644 --- a/pkg/policies/engine/rego/testfiles/check_qa.rego +++ b/pkg/policies/engine/rego/testfiles/check_qa.rego @@ -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" diff --git a/pkg/policies/engine/rego/testfiles/policy_without_violations.rego b/pkg/policies/engine/rego/testfiles/policy_without_violations.rego index 372f31ecf..d7630e288 100644 --- a/pkg/policies/engine/rego/testfiles/policy_without_violations.rego +++ b/pkg/policies/engine/rego/testfiles/policy_without_violations.rego @@ -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" } diff --git a/pkg/policies/engine/rego/testfiles/restricted_mode_networking_allowed_host.rego b/pkg/policies/engine/rego/testfiles/restricted_mode_networking_allowed_host.rego index b210a68cf..14d18748e 100644 --- a/pkg/policies/engine/rego/testfiles/restricted_mode_networking_allowed_host.rego +++ b/pkg/policies/engine/rego/testfiles/restricted_mode_networking_allowed_host.rego @@ -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 := "" } \ No newline at end of file diff --git a/pkg/policies/engine/rego/testfiles/restrictive_mode_networking.rego b/pkg/policies/engine/rego/testfiles/restrictive_mode_networking.rego index 516c5a02c..2ed5fb068 100644 --- a/pkg/policies/engine/rego/testfiles/restrictive_mode_networking.rego +++ b/pkg/policies/engine/rego/testfiles/restrictive_mode_networking.rego @@ -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 := "" } diff --git a/pkg/policies/testdata/materials.rego b/pkg/policies/testdata/materials.rego index 519d7f79c..54597b8bf 100644 --- a/pkg/policies/testdata/materials.rego +++ b/pkg/policies/testdata/materials.rego @@ -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 } diff --git a/pkg/policies/testdata/sbom_syft.rego b/pkg/policies/testdata/sbom_syft.rego index e8784c553..c138fd3a7 100644 --- a/pkg/policies/testdata/sbom_syft.rego +++ b/pkg/policies/testdata/sbom_syft.rego @@ -1,14 +1,43 @@ package main -import future.keywords.in +import rego.v1 -violations[msg] { - not made_with_syft +################################ +# 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 made_with_syft msg := "Not made with syft" } -made_with_syft { +made_with_syft if { some creator in input.creationInfo.creators contains(creator, "syft") } \ No newline at end of file diff --git a/pkg/policies/testdata/with_arguments.rego b/pkg/policies/testdata/with_arguments.rego index d462dc18a..9a46d583b 100644 --- a/pkg/policies/testdata/with_arguments.rego +++ b/pkg/policies/testdata/with_arguments.rego @@ -2,9 +2,38 @@ 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 not valid_developer - msg := "Invalid developer" } diff --git a/pkg/policies/testdata/workflow.rego b/pkg/policies/testdata/workflow.rego index 7558fb2fb..e902c4c8b 100644 --- a/pkg/policies/testdata/workflow.rego +++ b/pkg/policies/testdata/workflow.rego @@ -1,23 +1,53 @@ package main -violations[msg] { - not is_workflow +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 + not is_workflow msg := "incorrect workflow" } -violations[msg] { +violations contains msg if { + valid_input not is_github - msg := "incorrect runner" } - -is_workflow { +is_workflow if { input.predicate.metadata.name == "chainloop-vault-release" } -is_github { +is_github if { input.predicate.runnerType == "GITHUB_ACTION" input.predicate.env.GITHUB_SHA } \ No newline at end of file diff --git a/pkg/policies/testdata/workflow_embedded.yaml b/pkg/policies/testdata/workflow_embedded.yaml index 9422ff879..333a4967f 100644 --- a/pkg/policies/testdata/workflow_embedded.yaml +++ b/pkg/policies/testdata/workflow_embedded.yaml @@ -6,22 +6,56 @@ spec: type: ATTESTATION embedded: | package main - violations[msg] { - not is_workflow - msg := "incorrect workflow" + + import rego.v1 + + ################################ + # Common section do NOT change # + ################################ + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, } - - violations[msg] { - not is_github - msg := "incorrect runner" + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" } - - is_workflow { - input.predicate.metadata.name == "chainloop-vault-release" + + 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 is_workflow + msg := "incorrect workflow" + } + + violations contains msg if { + valid_input + not is_github + msg := "incorrect runner" } - - is_github { - input.predicate.runnerType == "GITHUB_ACTION" - input.predicate.env.GITHUB_SHA + + is_workflow if { + input.predicate.metadata.name == "chainloop-vault-release" + } + + is_github if { + input.predicate.runnerType == "GITHUB_ACTION" + input.predicate.env.GITHUB_SHA }