diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index eb7393888..631c0b9e2 100644 --- a/app/cli/internal/policydevel/eval.go +++ b/app/cli/internal/policydevel/eval.go @@ -28,6 +28,10 @@ import ( "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials" ) +const ( + defaultMaterialName = "material" +) + type EvalOptions struct { PolicyPath string MaterialKind string @@ -72,8 +76,9 @@ func createCraftingSchema(policyPath string, inputs map[string]string) (*v1.Craf Policies: &v1.Policies{ Materials: []*v1.PolicyAttachment{ { - Policy: &v1.PolicyAttachment_Ref{Ref: fmt.Sprintf("file://%s", policyPath)}, - With: inputs, + Policy: &v1.PolicyAttachment_Ref{Ref: fmt.Sprintf("file://%s", policyPath)}, + With: inputs, + Selector: &v1.PolicyAttachment_MaterialSelector{Name: defaultMaterialName}, }, }, Attestation: nil, @@ -142,12 +147,12 @@ func craftMaterial(materialPath, materialKind string, logger *zerolog.Logger) (* if !ok { return nil, fmt.Errorf("invalid material kind: %s", materialKind) } - return craft(materialPath, v1.CraftingSchema_Material_MaterialType(kind), "material", backend, logger) + return craft(materialPath, v1.CraftingSchema_Material_MaterialType(kind), defaultMaterialName, backend, logger) } // Auto-detect kind for _, kind := range v1.CraftingMaterialInValidationOrder { - m, err := craft(materialPath, kind, "auto-detected-material", backend, logger) + m, err := craft(materialPath, kind, defaultMaterialName, backend, logger) if err == nil { return m, nil } diff --git a/app/cli/internal/policydevel/eval_test.go b/app/cli/internal/policydevel/eval_test.go index 140144374..101073e0d 100644 --- a/app/cli/internal/policydevel/eval_test.go +++ b/app/cli/internal/policydevel/eval_test.go @@ -28,6 +28,8 @@ func TestEvaluate(t *testing.T) { tempDir := t.TempDir() logger := zerolog.New(os.Stderr) policyPath := "testdata/policy-test.yaml" + policyWithoutKind := "testdata/policy-without-kind.yaml" + policyWithAndWithoutKind := "testdata/policy-with-and-without-kind.yaml" t.Run("evaluation with explicit kind", func(t *testing.T) { testFile := filepath.Join(tempDir, "test.txt") @@ -124,4 +126,62 @@ func TestEvaluate(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "invalid material kind") }) + + t.Run("evaluation for policy without specified kind", func(t *testing.T) { + materialPath := "testdata/sbom_cyclonedx.json" + + opts := &EvalOptions{ + PolicyPath: policyWithoutKind, + MaterialKind: "", + MaterialPath: materialPath, + Annotations: map[string]string{"key": "value"}, + } + + results, err := Evaluate(opts, logger) + require.NoError(t, err) + require.NotEmpty(t, results) + + // Check that at least one violation was returned + foundViolations := false + for _, r := range results { + if len(r.Violations) > 0 { + foundViolations = true + break + } + } + + require.True(t, foundViolations, "expected at least one violation in the results") + }) + + t.Run("evaluation for policy with not matching kind and without kind", func(t *testing.T) { + materialPath := "testdata/sbom_cyclonedx.json" + + opts := &EvalOptions{ + PolicyPath: policyWithAndWithoutKind, + MaterialKind: "", + MaterialPath: materialPath, + Annotations: map[string]string{"key": "value"}, + } + + results, err := Evaluate(opts, logger) + require.NoError(t, err) + require.NotEmpty(t, results) + + foundWithoutKind := false + foundEvidenceKid := false + + for _, r := range results { + for _, v := range r.Violations { + if v == "without kind" { + foundWithoutKind = true + } + if v == "evidence kind" { + foundEvidenceKid = true + } + } + } + + require.True(t, foundWithoutKind, "expected violation 'without kind'") + require.False(t, foundEvidenceKid, "did not expect violation 'evidence kind'") + }) } diff --git a/app/cli/internal/policydevel/testdata/policy-with-and-without-kind.yaml b/app/cli/internal/policydevel/testdata/policy-with-and-without-kind.yaml new file mode 100644 index 000000000..ff8a4aef6 --- /dev/null +++ b/app/cli/internal/policydevel/testdata/policy-with-and-without-kind.yaml @@ -0,0 +1,141 @@ +apiVersion: workflowcontract.chainloop.dev/v1 +kind: Policy +metadata: + name: policy + description: Chainloop validation policy +spec: + policies: + - embedded: | + package main + + import rego.v1 + + ################################ + # Common section do NOT change # + ################################ + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + "ignore": ignore, + } + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" + } + + default skipped := true + + skipped := false if valid_input + + default ignore := false + + ######################################## + # EO Common section, custom code below # + ######################################## + # Validates if the input is valid and can be understood by this policy + valid_input := true + + # insert code here + + # If the input is valid, check for any policy violation here + # default violations := [] + + violations contains msg if { + valid_input + msg := "evidence kind" + } + kind: EVIDENCE + - embedded: | + package main + + import rego.v1 + + ################################ + # Common section do NOT change # + ################################ + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + "ignore": ignore, + } + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" + } + + default skipped := true + + skipped := false if valid_input + + default ignore := false + + ######################################## + # EO Common section, custom code below # + ######################################## + # Validates if the input is valid and can be understood by this policy + valid_input := true + + # insert code here + + # If the input is valid, check for any policy violation here + # default violations := [] + + violations contains msg if { + valid_input + msg := "without kind" + } + - embedded: | + package main + + import rego.v1 + + ################################ + # Common section do NOT change # + ################################ + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + "ignore": ignore, + } + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" + } + + default skipped := true + + skipped := false if valid_input + + default ignore := false + + ######################################## + # EO Common section, custom code below # + ######################################## + # Validates if the input is valid and can be understood by this policy + valid_input := true + + # insert code here + + # If the input is valid, check for any policy violation here + # default violations := [] + + violations contains msg if { + valid_input + msg := "sbom kind" + } + kind: SBOM_CYCLONEDX_JSON diff --git a/app/cli/internal/policydevel/testdata/policy-without-kind.yaml b/app/cli/internal/policydevel/testdata/policy-without-kind.yaml new file mode 100644 index 000000000..42fe0ea2b --- /dev/null +++ b/app/cli/internal/policydevel/testdata/policy-without-kind.yaml @@ -0,0 +1,51 @@ +apiVersion: workflowcontract.chainloop.dev/v1 +kind: Policy +metadata: + name: policy + description: Chainloop validation policy +spec: + policies: + - embedded: | + package main + + import rego.v1 + + ################################ + # Common section do NOT change # + ################################ + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + "ignore": ignore, + } + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" + } + + default skipped := true + + skipped := false if valid_input + + default ignore := false + + ######################################## + # EO Common section, custom code below # + ######################################## + # Validates if the input is valid and can be understood by this policy + valid_input := true + + # insert code here + + # If the input is valid, check for any policy violation here + # default violations := [] + + violations contains msg if { + valid_input + msg := "without kind" + }