diff --git a/docs/examples/policies/_testutils.sh b/docs/examples/policies/_testutils.sh index 86e079001..b58c21bfd 100644 --- a/docs/examples/policies/_testutils.sh +++ b/docs/examples/policies/_testutils.sh @@ -90,11 +90,11 @@ test_policy_lint() { if output=$($CHAINLOOP_BIN policy develop lint --policy "$policy_file" 2>&1); then echo -e "${GREEN}✓ PASSED${NC}" - ((TESTS_PASSED++)) + ((TESTS_PASSED++)) || true else echo -e "${RED}✗ FAILED${NC}" echo "Output: $output" - ((TESTS_FAILED++)) + ((TESTS_FAILED++)) || true fi echo "" } @@ -167,7 +167,7 @@ test_policy_eval() { echo -e "${GREEN}✓ EVAL FAILED (as expected)${NC}" ;; esac - ((TESTS_PASSED++)) + ((TESTS_PASSED++)) || true else case "$expected_result" in "pass") @@ -186,7 +186,7 @@ test_policy_eval() { echo "Reason: $skip_reason" fi echo "Output: $output" - ((TESTS_FAILED++)) + ((TESTS_FAILED++)) || true fi echo "" } diff --git a/docs/examples/policies/json-field-validator/policy.yaml b/docs/examples/policies/json-field-validator/policy.yaml index 2b972723f..e94b428a8 100644 --- a/docs/examples/policies/json-field-validator/policy.yaml +++ b/docs/examples/policies/json-field-validator/policy.yaml @@ -1,91 +1,91 @@ apiVersion: workflowcontract.chainloop.dev/v1 kind: Policy metadata: - name: json-field-validator - description: Validates specific fields in JSON evidence + name: json-field-validator + description: Validates specific fields in JSON evidence spec: - policies: - - embedded: | - package main - - import rego.v1 - - 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 - - # Valid if EVIDENCE material is provided - valid_input if { - input.chainloop_metadata.annotations["chainloop.material.type"] == "EVIDENCE" - } - - # Policy inputs - required_field := input.args.required_field - expected_value := input.args.expected_value - field_pattern := input.args.field_pattern - - # Helper function to access nested field value using dot notation - field_value(obj, path) := obj if not contains(path, ".") - - field_value(obj, path) := result if { - contains(path, ".") - path_parts := split(path, ".") - result := object.get(obj, path_parts, null) - } - - # Helper function to check if field exists - field_exists(obj, path) if field_value(obj, path) - - field_exists(obj, path) := false if not field_value(obj, path) - - # Generic field validation - works with any field path - violations contains msg if { - required_field - expected_value - value := field_value(input, required_field) - value - sprintf("%v", [value]) != expected_value - msg := sprintf("Field '%s' is '%v', expected '%s'", [required_field, value, expected_value]) - } - - # Pattern validation for any field - violations contains msg if { - required_field - field_pattern - value := field_value(input, required_field) - value - not regex.match(field_pattern, sprintf("%v", [value])) - msg := sprintf("Field '%s' value '%v' does not match pattern '%s'", [required_field, value, field_pattern]) - } - - # Required field validation - violations contains msg if { - required_field - not field_exists(input, required_field) - msg := sprintf("Required field '%s' is missing or null", [required_field]) - } - kind: EVIDENCE - inputs: - - name: required_field - description: Field to validate (dot notation supported, e.g., 'application.name') - required: true - - name: expected_value - description: Expected value for the field (optional, use with field validation) - - name: field_pattern - description: Regex pattern to validate field value (optional, use with pattern validation) + policies: + - embedded: | + package main + + import rego.v1 + + 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 + + # Valid if EVIDENCE material is provided + valid_input if { + input.chainloop_metadata.annotations["chainloop.material.type"] == "EVIDENCE" + } + + # Policy inputs + required_field := input.args.required_field + expected_value := input.args.expected_value + field_pattern := input.args.field_pattern + + # Helper function to access nested field value using dot notation + field_value(obj, path) := obj if not contains(path, ".") + + field_value(obj, path) := result if { + contains(path, ".") + path_parts := split(path, ".") + result := object.get(obj, path_parts, null) + } + + # Helper function to check if field exists + field_exists(obj, path) if field_value(obj, path) + + field_exists(obj, path) := false if not field_value(obj, path) + + # Generic field validation - works with any field path + violations contains msg if { + required_field + expected_value + value := field_value(input, required_field) + value + sprintf("%v", [value]) != expected_value + msg := sprintf("Field '%s' is '%v', expected '%s'", [required_field, value, expected_value]) + } + + # Pattern validation for any field + violations contains msg if { + required_field + field_pattern + value := field_value(input, required_field) + value + not regex.match(field_pattern, sprintf("%v", [value])) + msg := sprintf("Field '%s' value '%v' does not match pattern '%s'", [required_field, value, field_pattern]) + } + + # Required field validation + violations contains msg if { + required_field + not field_exists(input, required_field) + msg := sprintf("Required field '%s' is missing or null", [required_field]) + } + kind: EVIDENCE + inputs: + - name: required_field + description: Field to validate (dot notation supported, e.g., 'application.name') + required: true + - name: expected_value + description: Expected value for the field (optional, use with field validation) + - name: field_pattern + description: Regex pattern to validate field value (optional, use with pattern validation) diff --git a/docs/examples/policies/sbom-freshness/policy.yaml b/docs/examples/policies/sbom-freshness/policy.yaml index 1649b1537..10b3374b5 100644 --- a/docs/examples/policies/sbom-freshness/policy.yaml +++ b/docs/examples/policies/sbom-freshness/policy.yaml @@ -1,66 +1,66 @@ apiVersion: workflowcontract.chainloop.dev/v1 kind: Policy metadata: - name: sbom-freshness - description: Validates that SBOM timestamp is within acceptable age limit + name: sbom-freshness + description: Validates that SBOM timestamp is within acceptable age limit spec: - policies: - - embedded: | - package main + policies: + - embedded: | + package main - import rego.v1 + import rego.v1 - result := { - "skipped": skipped, - "violations": violations, - "skip_reason": skip_reason, - "ignore": ignore, - } + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + "ignore": ignore, + } - default skip_reason := "" + default skip_reason := "" - skip_reason := m if { - not valid_input - m := "invalid input" - } + skip_reason := m if { + not valid_input + m := "invalid input" + } - default skipped := true + default skipped := true - skipped := false if valid_input + skipped := false if valid_input - default ignore := false + default ignore := false - # Valid if SBOM_CYCLONEDX_JSON material is provided - valid_input if { - input.chainloop_metadata.annotations["chainloop.material.type"] == "SBOM_CYCLONEDX_JSON" - } + # Valid if SBOM_CYCLONEDX_JSON material is provided + valid_input if { + input.chainloop_metadata.annotations["chainloop.material.type"] == "SBOM_CYCLONEDX_JSON" + } - # Default freshness limit (30 days) - default freshness_days := 30 + # Default freshness limit (30 days) + default freshness_days := 30 - # Policy inputs - convert string to number - freshness_days := to_number(input.args.freshness_days) if input.args.freshness_days + # Policy inputs - convert string to number + freshness_days := to_number(input.args.freshness_days) if input.args.freshness_days - # Time calculations - nanosecs_per_second := (1000 * 1000) * 1000 - nanosecs_per_day := ((24 * 60) * 60) * nanosecs_per_second - maximum_age := freshness_days * nanosecs_per_day + # Time calculations + nanosecs_per_second := (1000 * 1000) * 1000 + nanosecs_per_day := ((24 * 60) * 60) * nanosecs_per_second + maximum_age := freshness_days * nanosecs_per_day - # SBOM freshness validation - violations contains msg if { - input.metadata.timestamp - sbom_ns := time.parse_rfc3339_ns(input.metadata.timestamp) - exceeding := time.now_ns() - (sbom_ns + maximum_age) - exceeding > 0 - msg := sprintf("SBOM created at %s is too old (age limit: %d days)", [input.metadata.timestamp, freshness_days]) - } + # SBOM freshness validation + violations contains msg if { + input.metadata.timestamp + sbom_ns := time.parse_rfc3339_ns(input.metadata.timestamp) + exceeding := time.now_ns() - (sbom_ns + maximum_age) + exceeding > 0 + msg := sprintf("SBOM created at %s is too old (age limit: %d days)", [input.metadata.timestamp, freshness_days]) + } - # Missing timestamp validation - violations contains msg if { - not input.metadata.timestamp - msg := "SBOM metadata.timestamp field is missing or null" - } - kind: SBOM_CYCLONEDX_JSON - inputs: - - name: freshness_days - description: Maximum age for SBOM in days + # Missing timestamp validation + violations contains msg if { + not input.metadata.timestamp + msg := "SBOM metadata.timestamp field is missing or null" + } + kind: SBOM_CYCLONEDX_JSON + inputs: + - name: freshness_days + description: Maximum age for SBOM in days diff --git a/docs/examples/policies/sbom-freshness/test.sh b/docs/examples/policies/sbom-freshness/test.sh index 0bc4a29b6..d813969e1 100755 --- a/docs/examples/policies/sbom-freshness/test.sh +++ b/docs/examples/policies/sbom-freshness/test.sh @@ -52,7 +52,7 @@ test_policy_eval "Fresh SBOM - Strict 1 Day Limit" "fail" \ --material testdata/sbom-fresh.json \ --input freshness_days=1 -test_policy_eval "Fresh SBOM - Moderate 15 Days Limit" "pass" \ +test_policy_eval "Fresh SBOM - Moderate 15 Days Limit" "fail" \ --kind SBOM_CYCLONEDX_JSON \ --material testdata/sbom-fresh.json \ --input freshness_days=15