From b3c256629155488b30b55b3fa6c1005d1f8eca08 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Mon, 11 Aug 2025 15:41:31 +0200 Subject: [PATCH 1/5] add handler for empty kind Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/eval.go | 13 +- app/cli/internal/policydevel/eval_test.go | 61 ++++++++ .../policy-with-and-without-kind.yaml | 141 ++++++++++++++++++ .../testdata/policy-without-kind.yaml | 51 +++++++ 4 files changed, 262 insertions(+), 4 deletions(-) create mode 100644 app/cli/internal/policydevel/testdata/policy-with-and-without-kind.yaml create mode 100644 app/cli/internal/policydevel/testdata/policy-without-kind.yaml diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index eb7393888..faf7bdc3e 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..2fad87872 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,63 @@ func TestEvaluate(t *testing.T) { require.Error(t, err) assert.Contains(t, err.Error(), "invalid material kind") }) + + t.Run("evaulation 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" + } From e4143576fc41789bf628faca7b352f4c372f7c72 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Mon, 11 Aug 2025 16:02:07 +0200 Subject: [PATCH 2/5] fix typo Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/eval_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cli/internal/policydevel/eval_test.go b/app/cli/internal/policydevel/eval_test.go index 2fad87872..f11f99b42 100644 --- a/app/cli/internal/policydevel/eval_test.go +++ b/app/cli/internal/policydevel/eval_test.go @@ -127,7 +127,7 @@ func TestEvaluate(t *testing.T) { assert.Contains(t, err.Error(), "invalid material kind") }) - t.Run("evaulation for policy without specified kind", func(t *testing.T) { + t.Run("evaluation for policy without specified kind", func(t *testing.T) { materialPath := "testdata/sbom_cyclonedx.json" opts := &EvalOptions{ From e4a76087a35a6fad8f2e4cf50928152008ab3748 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Mon, 11 Aug 2025 17:36:05 +0200 Subject: [PATCH 3/5] Update app/cli/internal/policydevel/eval.go Co-authored-by: Miguel Martinez Trivino Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/eval.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index faf7bdc3e..91705b9d7 100644 --- a/app/cli/internal/policydevel/eval.go +++ b/app/cli/internal/policydevel/eval.go @@ -29,7 +29,7 @@ import ( ) const ( - DefaultMaterialName = "material" + defaultMaterialName = "material" ) type EvalOptions struct { From 68f44b5c4a6b56623c8e91f72c1f9b24d2fc4e3e Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Mon, 11 Aug 2025 17:40:41 +0200 Subject: [PATCH 4/5] rename const Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/eval.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/app/cli/internal/policydevel/eval.go b/app/cli/internal/policydevel/eval.go index 91705b9d7..631c0b9e2 100644 --- a/app/cli/internal/policydevel/eval.go +++ b/app/cli/internal/policydevel/eval.go @@ -78,7 +78,7 @@ func createCraftingSchema(policyPath string, inputs map[string]string) (*v1.Craf { Policy: &v1.PolicyAttachment_Ref{Ref: fmt.Sprintf("file://%s", policyPath)}, With: inputs, - Selector: &v1.PolicyAttachment_MaterialSelector{Name: DefaultMaterialName}, + Selector: &v1.PolicyAttachment_MaterialSelector{Name: defaultMaterialName}, }, }, Attestation: nil, @@ -147,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), DefaultMaterialName, 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, DefaultMaterialName, backend, logger) + m, err := craft(materialPath, kind, defaultMaterialName, backend, logger) if err == nil { return m, nil } From a5269496d733b4da4416c4f8bca7d4bd60149ae1 Mon Sep 17 00:00:00 2001 From: Sylwester Piskozub Date: Mon, 11 Aug 2025 17:46:09 +0200 Subject: [PATCH 5/5] fix linter Signed-off-by: Sylwester Piskozub --- app/cli/internal/policydevel/eval_test.go | 1 - 1 file changed, 1 deletion(-) diff --git a/app/cli/internal/policydevel/eval_test.go b/app/cli/internal/policydevel/eval_test.go index f11f99b42..101073e0d 100644 --- a/app/cli/internal/policydevel/eval_test.go +++ b/app/cli/internal/policydevel/eval_test.go @@ -184,5 +184,4 @@ func TestEvaluate(t *testing.T) { require.True(t, foundWithoutKind, "expected violation 'without kind'") require.False(t, foundEvidenceKid, "did not expect violation 'evidence kind'") }) - }