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
8 changes: 4 additions & 4 deletions docs/examples/policies/_testutils.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Copy link
Member

@migmartri migmartri Aug 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi! could you give some color on why we need this?

thanks!

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently ((TESTS_PASSED++)) uses post increment so here it returns 0 which causes the script to exit (bash expr). ((++TESTS_PASSED)) would fix that as well but as defensive measure I've used || true
since from what I understood from other tests that use this utils, it is meant to return exit code with test_summary .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but is this issue related to your change? Why wasn't it failing before? Out of curiosity what's the root cause

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, it was failing before, I'm not sure which released version caused it to not work, unless it wasn't working since the beginning

else
echo -e "${RED}✗ FAILED${NC}"
echo "Output: $output"
((TESTS_FAILED++))
((TESTS_FAILED++)) || true
fi
echo ""
}
Expand Down Expand Up @@ -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")
Expand All @@ -186,7 +186,7 @@ test_policy_eval() {
echo "Reason: $skip_reason"
fi
echo "Output: $output"
((TESTS_FAILED++))
((TESTS_FAILED++)) || true
fi
echo ""
}
Expand Down
174 changes: 87 additions & 87 deletions docs/examples/policies/json-field-validator/policy.yaml
Original file line number Diff line number Diff line change
@@ -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)
98 changes: 49 additions & 49 deletions docs/examples/policies/sbom-freshness/policy.yaml
Original file line number Diff line number Diff line change
@@ -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
2 changes: 1 addition & 1 deletion docs/examples/policies/sbom-freshness/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Loading