Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: update constraint templates to work with new type field #1217

Merged
63 changes: 63 additions & 0 deletions library/rego/sbom-validation.rego
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@

# Copyright The Ratify Authors.
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

# http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

package ratify.policy

# This template defines policy for SBOM validation.
# It checks the following:
# - If there are any system errors
# - If there are errors for any of the images
# - There is at least one SBOM report that was verified
# - Only considers ONE SBOM report
# - The SBOM is valid (isSuccess = true)
# - The SBOM has a valid notary project signature (if require_signature = true)s

import future.keywords.if
import future.keywords.in

default require_signature := false # change to true to require notary project signature on vulnerability report
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
default valid := false

valid {
not failed_verify(input)
}

failed_verify(reports) {
not process_sboms(reports)
}

process_sboms(subject_result) if {
# collect verifier reports from sbom verifier
sbom_results := [res | subject_result.verifierReports[i].verifierReports[j].name == "sbom"; res := subject_result.verifierReports[i].verifierReports[j]]
akashsinghal marked this conversation as resolved.
Show resolved Hide resolved
count(sbom_results) > 0
# validate SBOM contents for ONLY the first report found
process_sbom(sbom_results[0])
}

process_sbom(report) if {
report.isSuccess == true
valid_signatures(report)
}

valid_signatures(_) := true {
require_signature == false
}

valid_signatures(report) := true {
require_signature
count(report.nestedResults) > 0
some nestedResult in report.nestedResults
nestedResult.artifactType == "application/vnd.cncf.notary.signature"
nestedResult.isSuccess
}
11 changes: 11 additions & 0 deletions library/sbom-validation/samples/constraint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: SbomValidation
metadata:
name: sbom-validation-constraint
spec:
enforcementAction: deny
match:
kinds:
- apiGroups: [""]
kinds: ["Pod"]
namespaces: ["default"]
58 changes: 44 additions & 14 deletions library/sbom-validation/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,20 @@ spec:
rego: |
package sbomvalidation

# This template defines policy for SBOM validation.
# It checks the following:
# - If there are any system errors
# - If there are errors for any of the images
# - There is at least one SBOM report that was verified
# - Only considers ONE SBOM report
# - The SBOM is valid (isSuccess = true)
# - The SBOM has a valid notary project signature (if require_signature = true)s

import future.keywords.if
import future.keywords.in

default require_signature := false # change to true to require notary project signature on SBOM

# Get data from Ratify
remote_data := response {
images := [img | img = input.review.object.spec.containers[_].image]
Expand All @@ -31,28 +45,44 @@ spec:
general_violation[{"result": result}] {
err := remote_data.system_error
err != ""
result := sprintf("System error calling external data provider: %s", [err])
result := sprintf("System error calling external data provider for SBOM verification: %s", [err])
}

# Check if there are errors for any of the images
general_violation[{"result": result}] {
count(remote_data.errors) > 0
result := sprintf("Error validating one or more images: %s", remote_data.errors)
result := sprintf("Error validating one or more images for SBOM verification: %s", remote_data.errors)
}

# Check if the success criteria is true
general_violation[{"result": result}] {
subject_validation := remote_data.responses[_]
subject_validation[1].isSuccess == false
result := sprintf("Subject failed verification: %s", [subject_validation[0]])
subject_result := subject_validation[1]
not process_sboms(subject_result)
result := sprintf("Subject failed SBOM verification: %s", [subject_validation[0]])
}

# Check for failed sbom validation
general_violation[{"result": result}] {
subject_results := remote_data.responses[_]
subject_result := subject_results[1]
sbom_results := [res | subject_result.verifierReports[i].name == "sbom"; res := subject_result.verifierReports[i]]
sbom_result := sbom_results[_]
sbom_result.isSuccess == false
result = sprintf("Subject %s failed sbom validation: %s", [subject_results[0], sbom_result.message])

process_sboms(subject_result) if {
# collect verifier reports from sbom verifier
sbom_results := [res | subject_result.verifierReports[i].type == "sbom"; res := subject_result.verifierReports[i]]
count(sbom_results) > 0
# validate SBOM contents for ONLY the first report found
process_sbom(sbom_results[0])
}

process_sbom(report) if {
report.isSuccess == true
valid_signatures(report)
}

valid_signatures(_) := true {
require_signature == false
}

valid_signatures(report) := true {
require_signature
count(report.nestedResults) > 0
some nestedResult in report.nestedResults
nestedResult.artifactType == "application/vnd.cncf.notary.signature"
nestedResult.isSuccess
}
8 changes: 4 additions & 4 deletions library/vulnerability-report-validation/template.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -51,26 +51,26 @@ spec:
general_violation[{"result": result}] {
err := remote_data.system_error
err != ""
result := sprintf("System error calling external data provider: %s", [err])
result := sprintf("System error calling external data provider for vulnerability report verification: %s", [err])
}

# Check if there are errors for any of the images
general_violation[{"result": result}] {
count(remote_data.errors) > 0
result := sprintf("Error validating one or more images: %s", remote_data.errors)
result := sprintf("Error validating one or more images for vulnerability report verification: %s", remote_data.errors)
}

# Check if the success criteria is true
general_violation[{"result": result}] {
subject_validation := remote_data.responses[_]
subject_result := subject_validation[1]
not process_vuln_reports(subject_result)
result := sprintf("Subject failed verification: %s", [subject_validation[0]])
result := sprintf("Subject failed vulnerability report verification: %s", [subject_validation[0]])
}

process_vuln_reports(subject_result) if {
# collect verifier reports from vulnerabilityreport verifier
vuln_results := [res | subject_result.verifierReports[i].name == "vulnerabilityreport"; res := subject_result.verifierReports[i]]
vuln_results := [res | subject_result.verifierReports[i].type == "vulnerabilityreport"; res := subject_result.verifierReports[i]]
count(vuln_results) > 0
# calculate the timestamp between current time and creation time
timestamp_diff_results_map := {diff_in_ns: i | diff_in_ns := time.now_ns() - time.parse_rfc3339_ns(vuln_results[i].extensions["createdAt"])}
Expand Down
Loading