Skip to content
Draft
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
29 changes: 27 additions & 2 deletions policy/release/trusted_task/trusted_task.rego
Original file line number Diff line number Diff line change
Expand Up @@ -162,13 +162,14 @@ deny contains result if {
# description: >-
# Confirm certain parameters provided to each builder Task have come from trusted Tasks.
# Trust can be defined using pattern-based rules (trusted_task_rules) or an explicit allow
# list with expiry dates (trusted_tasks).
# list with expiry dates (trusted_tasks). Additionally, images from registries listed in the
# `trusted_build_image_registries` rule data key are implicitly trusted.
# custom:
# short_name: trusted_parameters
# failure_msg: 'The %q parameter of the %q PipelineTask includes an untrusted digest: %s'
# solution: >-
# Update your build Pipeline to ensure all the parameters provided to your builder Tasks come
# from trusted Tasks.
# from trusted Tasks, or add the image registry to the trusted_build_image_registries rule data.
# collections:
# - redhat
# effective_on: 2021-07-04T00:00:00Z
Expand Down Expand Up @@ -391,8 +392,27 @@ _trusted_build_digests contains digest if {
some digest in _digests_from_values({runner_image_result_value})
}

# Digests from images in trusted registries are considered trustworthy.
# Trusted registries are configured via the `trusted_build_image_registries` rule data key.
_trusted_build_digests contains digest if {
some attestation in lib.pipelinerun_attestations
some build_task in tekton.build_tasks(attestation)
some param_name, param_value in tekton.task_params(build_task)
not endswith(param_name, "_ARTIFACT")
some value in lib.param_values(param_value)
_from_trusted_registry(value)
some digest in _digests_from_values({value})
}

_pre_build_run_script_runner_image_result := "SCRIPT_RUNNER_IMAGE_REFERENCE"

# Returns true if the given value is an image reference from a trusted registry.
_from_trusted_registry(value) if {
parsed := image.parse(value)
some registry in lib.rule_data("trusted_build_image_registries")
startswith(parsed.repo, registry)
}

# Extracts SHA256 digests from a set of values using regex patterns
_digests_from_values(values) := {digest |
some value in values
Expand Down Expand Up @@ -451,6 +471,11 @@ _format_denial_reason(reason) := msg if {

pattern_lines := [sprintf(" - %s", [pattern]) | some pattern in reason.pattern]
msg := sprintf("%s\n%s", [reason.type, concat("\n", pattern_lines)])
} else := msg if {
reason.type == "signature_verification_failed"
count(reason.messages) > 0
message_lines := [sprintf(" - %s", [m]) | some m in reason.messages]
msg := sprintf("%s\n%s", [reason.type, concat("\n", message_lines)])
} else := reason.type

# Format error for rules system with Trusted Artifacts
Expand Down
122 changes: 122 additions & 0 deletions policy/release/trusted_task/trusted_task_test.rego
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,99 @@ test_trusted_build_digests_from_snapshot_components if {
lib.assert_equal(trusted_task._trusted_build_digests, expected) with input.snapshot.components as components
}

test_trusted_build_digests_from_trusted_registry if {
# A digest from a parameter referencing a trusted registry should appear in _trusted_build_digests
attestation := _mock_att_with_task({
"ref": {"name": "some-task", "bundle": "registry.local/trusty:1.0@sha256:digest"},
"results": [
{"name": "SOME_IMAGE_URL", "value": "registry.io/whatever/image", "type": "string"},
# regal ignore:line-length
{"name": "SOME_IMAGE_DIGEST", "value": "sha256:2222222222222222222222222222222222222222222222222222222222222222", "type": "string"},
],
# regal ignore:line-length
"invocation": {"parameters": {"image": "trusted.registry.io/repository/image@sha256:5555555555555555555555555555555555555555555555555555555555555555"}},
})
expected := {
# From build task results (existing behavior)
"sha256:2222222222222222222222222222222222222222222222222222222222222222",
# From trusted registry parameter (new behavior)
"sha256:5555555555555555555555555555555555555555555555555555555555555555",
}
lib.assert_equal(trusted_task._trusted_build_digests, expected) with input.attestations as [attestation]
with data.trusted_tasks as trusted_tasks_data
with data.rule_data.trusted_build_image_registries as ["trusted.registry.io/"]
}

test_trusted_build_digests_from_untrusted_registry if {
# A digest from a parameter referencing an untrusted registry should NOT appear in _trusted_build_digests
attestation := _mock_att_with_task({
"ref": {"name": "some-task", "bundle": "registry.local/trusty:1.0@sha256:digest"},
"results": [
{"name": "SOME_IMAGE_URL", "value": "registry.io/whatever/image", "type": "string"},
# regal ignore:line-length
{"name": "SOME_IMAGE_DIGEST", "value": "sha256:2222222222222222222222222222222222222222222222222222222222222222", "type": "string"},
],
# regal ignore:line-length
"invocation": {"parameters": {"image": "untrusted.registry.io/repository/image@sha256:5555555555555555555555555555555555555555555555555555555555555555"}},
})

# Only the build task result digest should be trusted, not the parameter digest
expected := {"sha256:2222222222222222222222222222222222222222222222222222222222222222"}
lib.assert_equal(trusted_task._trusted_build_digests, expected) with input.attestations as [attestation]
with data.trusted_tasks as trusted_tasks_data
with data.rule_data.trusted_build_image_registries as ["trusted.registry.io/"]
}

test_trusted_parameters_with_trusted_registry if {
# When a parameter references a trusted registry, the deny rule should not fire
evil_attestation := json.patch(attestation_ta, [{
"op": "add",
"path": "/statement/predicate/buildConfig/tasks/3/invocation/parameters/image",
# regal ignore:line-length
"value": "trusted.registry.io/repository/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
}])

# With the registry trusted, no violation should be produced
lib.assert_empty(trusted_task.deny) with data.trusted_tasks as trusted_tasks_data
with input.attestations as [evil_attestation]
with data.rule_data.trusted_build_image_registries as ["trusted.registry.io/"]
}

test_trusted_parameters_with_untrusted_registry if {
# When a parameter references an untrusted registry, the deny rule should fire
evil_attestation := json.patch(attestation_ta, [{
"op": "add",
"path": "/statement/predicate/buildConfig/tasks/3/invocation/parameters/image",
# regal ignore:line-length
"value": "untrusted.registry.io/repository/image@sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff",
}])

lib.assert_equal_results(trusted_task.deny, {{
"code": "trusted_task.trusted_parameters",
# regal ignore:line-length
"msg": `The "image" parameter of the "task_image_index" PipelineTask includes an untrusted digest: sha256:ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff`,
}}) with data.trusted_tasks as trusted_tasks_data
with input.attestations as [evil_attestation]
with data.rule_data.trusted_build_image_registries as ["trusted.registry.io/"]
}

test_from_trusted_registry if {
# regal ignore:line-length
trusted_task._from_trusted_registry("trusted.io/repo/image@sha256:abc123") with data.rule_data.trusted_build_image_registries as ["trusted.io/"]

# regal ignore:line-length
trusted_task._from_trusted_registry("trusted.io/repo/image:tag@sha256:abc123") with data.rule_data.trusted_build_image_registries as ["trusted.io/repo"]

# regal ignore:line-length
not trusted_task._from_trusted_registry("untrusted.io/repo/image@sha256:abc123") with data.rule_data.trusted_build_image_registries as ["trusted.io/"]

# regal ignore:line-length
not trusted_task._from_trusted_registry("not-an-image-ref") with data.rule_data.trusted_build_image_registries as ["trusted.io/"]

# regal ignore:line-length
not trusted_task._from_trusted_registry("trusted.io/repo/image@sha256:abc123") with data.rule_data.trusted_build_image_registries as []
}

#########################################
# Pipeline Tasks using bundles resolver #
#########################################
Expand Down Expand Up @@ -1310,6 +1403,35 @@ test_mixed_trusted_and_untrusted_tasks if {
with data.rule_data.trusted_task_rules as trusted_task_rules_data
}

test_signature_verification_failed_error_rules if {
att := {"statement": {
"predicateType": "https://slsa.dev/provenance/v0.2",
"predicate": {
"buildType": lib.tekton_pipeline_run,
"buildConfig": {"tasks": [trusted_bundle_pipeline_task]},
},
}}

rules := {"allow": [{
"name": "signed catalog",
"pattern": "oci://registry.local/trusty*",
"signature_verification": {
"certificate_identity_regexp": "https://tekton.dev/chains/.*",
"certificate_oidc_issuer": "https://accounts.google.com",
},
}]}

results := trusted_task.deny with input.attestations as [att]
with data.trusted_task_rules as rules
with ec.sigstore.verify_image as _mock_verify_image_failure

count(results) > 0
some result in results
contains(result.msg, "signature_verification_failed")
}

_mock_verify_image_failure(_, _) := {"success": false, "errors": ["signature verification failed"]}

#####################################################
# Helper Functions for trusted_task_rules tests
#####################################################
Expand Down
Loading