From 92f6519b958b0fb89eaa9d39ffe3d1fa28cb21a0 Mon Sep 17 00:00:00 2001 From: "Jose I. Paris" Date: Wed, 16 Oct 2024 16:18:06 +0200 Subject: [PATCH] feat(policies): support new policy output format (#1400) Signed-off-by: Jose I. Paris --- .../frontend/attestation/v1/crafting_state.ts | 36 ++++ ...tation.v1.PolicyEvaluation.jsonschema.json | 18 ++ ...ttestation.v1.PolicyEvaluation.schema.json | 18 ++ .../api/attestation/v1/crafting_state.pb.go | 202 ++++++++++-------- .../api/attestation/v1/crafting_state.proto | 6 + pkg/attestation/crafter/crafter.go | 2 +- pkg/attestation/renderer/chainloop/v02.go | 6 +- pkg/policies/engine/engine.go | 19 +- pkg/policies/engine/rego/rego.go | 70 +++++- pkg/policies/engine/rego/rego_test.go | 126 ++++++----- .../rego/testfiles/check_qa_deprecated.rego | 26 --- .../engine/rego/testfiles/result_format.rego | 30 +++ pkg/policies/policies.go | 93 +++++--- pkg/policies/policies_test.go | 77 +++++++ .../testdata/policy_result_format.yaml | 41 ++++ .../testdata/policy_result_skipped.yaml | 31 +++ 16 files changed, 574 insertions(+), 227 deletions(-) delete mode 100644 pkg/policies/engine/rego/testfiles/check_qa_deprecated.rego create mode 100644 pkg/policies/engine/rego/testfiles/result_format.rego create mode 100644 pkg/policies/testdata/policy_result_format.yaml create mode 100644 pkg/policies/testdata/policy_result_skipped.yaml diff --git a/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts b/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts index fe5cb7978..d1a2f74ba 100644 --- a/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts +++ b/app/controlplane/api/gen/frontend/attestation/v1/crafting_state.ts @@ -137,6 +137,10 @@ export interface PolicyEvaluation { with: { [key: string]: string }; /** material type, if any, of the evaluated policy */ type: CraftingSchema_Material_MaterialType; + /** whether this evaluation was skipped or not (because of an invalid input, for example) */ + skipped: boolean; + /** Evaluation messages, intended to communicate evaluation errors (invalid input) */ + skipReasons: string[]; } export interface PolicyEvaluation_AnnotationsEntry { @@ -1300,6 +1304,8 @@ function createBasePolicyEvaluation(): PolicyEvaluation { violations: [], with: {}, type: 0, + skipped: false, + skipReasons: [], }; } @@ -1338,6 +1344,12 @@ export const PolicyEvaluation = { if (message.type !== 0) { writer.uint32(64).int32(message.type); } + if (message.skipped === true) { + writer.uint32(104).bool(message.skipped); + } + for (const v of message.skipReasons) { + writer.uint32(114).string(v!); + } return writer; }, @@ -1431,6 +1443,20 @@ export const PolicyEvaluation = { message.type = reader.int32() as any; continue; + case 13: + if (tag !== 104) { + break; + } + + message.skipped = reader.bool(); + continue; + case 14: + if (tag !== 114) { + break; + } + + message.skipReasons.push(reader.string()); + continue; } if ((tag & 7) === 4 || tag === 0) { break; @@ -1465,6 +1491,8 @@ export const PolicyEvaluation = { }, {}) : {}, type: isSet(object.type) ? craftingSchema_Material_MaterialTypeFromJSON(object.type) : 0, + skipped: isSet(object.skipped) ? Boolean(object.skipped) : false, + skipReasons: Array.isArray(object?.skipReasons) ? object.skipReasons.map((e: any) => String(e)) : [], }; }, @@ -1499,6 +1527,12 @@ export const PolicyEvaluation = { }); } message.type !== undefined && (obj.type = craftingSchema_Material_MaterialTypeToJSON(message.type)); + message.skipped !== undefined && (obj.skipped = message.skipped); + if (message.skipReasons) { + obj.skipReasons = message.skipReasons.map((e) => e); + } else { + obj.skipReasons = []; + } return obj; }, @@ -1532,6 +1566,8 @@ export const PolicyEvaluation = { return acc; }, {}); message.type = object.type ?? 0; + message.skipped = object.skipped ?? false; + message.skipReasons = object.skipReasons?.map((e) => e) || []; return message; }, }; diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json index 321625d84..d3524fc40 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json @@ -13,6 +13,13 @@ }, "^(reference_name)$": { "type": "string" + }, + "^(skip_reasons)$": { + "description": "Evaluation messages, intended to communicate evaluation errors (invalid input)", + "items": { + "type": "string" + }, + "type": "array" } }, "properties": { @@ -46,6 +53,17 @@ "referenceName": { "type": "string" }, + "skipReasons": { + "description": "Evaluation messages, intended to communicate evaluation errors (invalid input)", + "items": { + "type": "string" + }, + "type": "array" + }, + "skipped": { + "description": "whether this evaluation was skipped or not (because of an invalid input, for example)", + "type": "boolean" + }, "sources": { "description": "Base64 representation of run scripts. It might be empty if there is a FQDN reference to the policy", "items": { diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json index 9b8ca884f..a3bf31d82 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json @@ -13,6 +13,13 @@ }, "^(referenceName)$": { "type": "string" + }, + "^(skipReasons)$": { + "description": "Evaluation messages, intended to communicate evaluation errors (invalid input)", + "items": { + "type": "string" + }, + "type": "array" } }, "properties": { @@ -46,6 +53,17 @@ "reference_name": { "type": "string" }, + "skip_reasons": { + "description": "Evaluation messages, intended to communicate evaluation errors (invalid input)", + "items": { + "type": "string" + }, + "type": "array" + }, + "skipped": { + "description": "whether this evaluation was skipped or not (because of an invalid input, for example)", + "type": "boolean" + }, "sources": { "description": "Base64 representation of run scripts. It might be empty if there is a FQDN reference to the policy", "items": { diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go index 0bcfd41a9..94f753296 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.pb.go @@ -193,6 +193,10 @@ type PolicyEvaluation struct { With map[string]string `protobuf:"bytes,7,rep,name=with,proto3" json:"with,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // material type, if any, of the evaluated policy Type v1.CraftingSchema_Material_MaterialType `protobuf:"varint,8,opt,name=type,proto3,enum=workflowcontract.v1.CraftingSchema_Material_MaterialType" json:"type,omitempty"` + // whether this evaluation was skipped or not (because of an invalid input, for example) + Skipped bool `protobuf:"varint,13,opt,name=skipped,proto3" json:"skipped,omitempty"` + // Evaluation messages, intended to communicate evaluation errors (invalid input) + SkipReasons []string `protobuf:"bytes,14,rep,name=skip_reasons,json=skipReasons,proto3" json:"skip_reasons,omitempty"` } func (x *PolicyEvaluation) Reset() { @@ -305,6 +309,20 @@ func (x *PolicyEvaluation) GetType() v1.CraftingSchema_Material_MaterialType { return v1.CraftingSchema_Material_MaterialType(0) } +func (x *PolicyEvaluation) GetSkipped() bool { + if x != nil { + return x.Skipped + } + return false +} + +func (x *PolicyEvaluation) GetSkipReasons() []string { + if x != nil { + return x.SkipReasons + } + return nil +} + type Commit struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -1297,7 +1315,7 @@ var file_attestation_v1_crafting_state_proto_rawDesc = []byte{ 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, - 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xf1, 0x06, 0x0a, 0x10, 0x50, 0x6f, 0x6c, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0xae, 0x07, 0x0a, 0x10, 0x50, 0x6f, 0x6c, 0x69, 0x63, 0x79, 0x45, 0x76, 0x61, 0x6c, 0x75, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x97, 0x01, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x82, 0x01, 0xba, 0x48, 0x7f, 0xba, 0x01, 0x7c, 0x0a, 0x0d, 0x6e, 0x61, 0x6d, 0x65, 0x2e, 0x64, 0x6e, 0x73, 0x2d, @@ -1340,95 +1358,99 @@ var file_attestation_v1_crafting_state_proto_rawDesc = []byte{ 0x72, 0x61, 0x63, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x2e, 0x4d, 0x61, 0x74, 0x65, 0x72, 0x69, 0x61, 0x6c, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74, 0x79, - 0x70, 0x65, 0x1a, 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x1a, 0x37, 0x0a, 0x09, 0x57, 0x69, 0x74, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, - 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, - 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x09, 0x56, - 0x69, 0x6f, 0x6c, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, - 0x65, 0x63, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, - 0x01, 0x52, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x6d, 0x65, - 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, - 0xc8, 0x01, 0x01, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xc0, 0x02, 0x0a, - 0x06, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, - 0x68, 0x61, 0x73, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x65, - 0x6d, 0x61, 0x69, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, - 0x6f, 0x72, 0x45, 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x28, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e, 0x61, 0x6d, - 0x65, 0x12, 0x21, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6d, 0x65, 0x73, - 0x73, 0x61, 0x67, 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, - 0x64, 0x61, 0x74, 0x65, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x18, - 0x06, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2e, 0x52, 0x65, - 0x6d, 0x6f, 0x74, 0x65, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x1a, 0x40, 0x0a, - 0x06, 0x52, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x19, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, - 0xaf, 0x01, 0x0a, 0x0d, 0x43, 0x72, 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, - 0x65, 0x12, 0x46, 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, - 0x61, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, - 0x6f, 0x77, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, - 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0b, 0x69, 0x6e, - 0x70, 0x75, 0x74, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x3d, 0x0a, 0x0b, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, - 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, - 0x41, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x74, 0x74, - 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, - 0x72, 0x75, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, - 0x6e, 0x22, 0x8e, 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4d, 0x65, - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, - 0x04, 0x74, 0x65, 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, - 0x6d, 0x12, 0x28, 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, - 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, - 0x0a, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x77, - 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, - 0x6e, 0x49, 0x64, 0x12, 0x30, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x72, 0x65, - 0x76, 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, - 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x76, - 0x69, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, - 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, - 0x72, 0x02, 0x10, 0x01, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, - 0x6f, 0x6e, 0x22, 0xde, 0x02, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, - 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, - 0x03, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, - 0x46, 0x0a, 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x2e, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, - 0x2e, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, - 0x74, 0x6f, 0x72, 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, - 0x06, 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, - 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, - 0x74, 0x12, 0x2b, 0x0a, 0x11, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6c, 0x6f, - 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x6f, - 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, - 0x0a, 0x0a, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, - 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, - 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x61, 0x6e, 0x6e, - 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x44, 0x69, 0x67, 0x65, - 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, - 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, - 0x02, 0x38, 0x01, 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, - 0x6d, 0x2f, 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, - 0x63, 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x74, - 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x61, 0x66, 0x74, 0x65, - 0x72, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, - 0x6e, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x70, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x18, 0x0d, 0x20, + 0x01, 0x28, 0x08, 0x52, 0x07, 0x73, 0x6b, 0x69, 0x70, 0x70, 0x65, 0x64, 0x12, 0x21, 0x0a, 0x0c, + 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x72, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x18, 0x0e, 0x20, 0x03, + 0x28, 0x09, 0x52, 0x0b, 0x73, 0x6b, 0x69, 0x70, 0x52, 0x65, 0x61, 0x73, 0x6f, 0x6e, 0x73, 0x1a, + 0x3e, 0x0a, 0x10, 0x41, 0x6e, 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, + 0x37, 0x0a, 0x09, 0x57, 0x69, 0x74, 0x68, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x1a, 0x4f, 0x0a, 0x09, 0x56, 0x69, 0x6f, 0x6c, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x20, 0x0a, 0x07, 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x07, + 0x73, 0x75, 0x62, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x20, 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, + 0x67, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x22, 0xc0, 0x02, 0x0a, 0x06, 0x43, 0x6f, + 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x1b, 0x0a, 0x04, 0x68, 0x61, 0x73, 0x68, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x68, 0x61, 0x73, + 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x65, 0x6d, 0x61, 0x69, + 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x45, + 0x6d, 0x61, 0x69, 0x6c, 0x12, 0x28, 0x0a, 0x0b, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x5f, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0a, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x4e, 0x61, 0x6d, 0x65, 0x12, 0x21, + 0x0a, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x42, + 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x07, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, + 0x65, 0x12, 0x2e, 0x0a, 0x04, 0x64, 0x61, 0x74, 0x65, 0x18, 0x05, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x04, 0x64, 0x61, 0x74, + 0x65, 0x12, 0x37, 0x0a, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x18, 0x06, 0x20, 0x03, + 0x28, 0x0b, 0x32, 0x1d, 0x2e, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x2e, 0x52, 0x65, 0x6d, 0x6f, 0x74, + 0x65, 0x52, 0x07, 0x72, 0x65, 0x6d, 0x6f, 0x74, 0x65, 0x73, 0x1a, 0x40, 0x0a, 0x06, 0x52, 0x65, + 0x6d, 0x6f, 0x74, 0x65, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, + 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x12, 0x19, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, + 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0xaf, 0x01, 0x0a, + 0x0d, 0x43, 0x72, 0x61, 0x66, 0x74, 0x69, 0x6e, 0x67, 0x53, 0x74, 0x61, 0x74, 0x65, 0x12, 0x46, + 0x0a, 0x0c, 0x69, 0x6e, 0x70, 0x75, 0x74, 0x5f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x0b, 0x32, 0x23, 0x2e, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x63, + 0x6f, 0x6e, 0x74, 0x72, 0x61, 0x63, 0x74, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x61, 0x66, 0x74, + 0x69, 0x6e, 0x67, 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x0b, 0x69, 0x6e, 0x70, 0x75, 0x74, + 0x53, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x12, 0x3d, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x61, 0x74, + 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x41, 0x74, 0x74, + 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x17, 0x0a, 0x07, 0x64, 0x72, 0x79, 0x5f, 0x72, 0x75, 0x6e, + 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x06, 0x64, 0x72, 0x79, 0x52, 0x75, 0x6e, 0x22, 0x8e, + 0x02, 0x0a, 0x10, 0x57, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x4d, 0x65, 0x74, 0x61, 0x64, + 0x61, 0x74, 0x61, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x74, 0x65, + 0x61, 0x6d, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x74, 0x65, 0x61, 0x6d, 0x12, 0x28, + 0x0a, 0x0b, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, + 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0a, 0x77, 0x6f, + 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x49, 0x64, 0x12, 0x26, 0x0a, 0x0f, 0x77, 0x6f, 0x72, 0x6b, + 0x66, 0x6c, 0x6f, 0x77, 0x5f, 0x72, 0x75, 0x6e, 0x5f, 0x69, 0x64, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x0d, 0x77, 0x6f, 0x72, 0x6b, 0x66, 0x6c, 0x6f, 0x77, 0x52, 0x75, 0x6e, 0x49, 0x64, + 0x12, 0x30, 0x0a, 0x0f, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x5f, 0x72, 0x65, 0x76, 0x69, 0x73, + 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, + 0x10, 0x01, 0x52, 0x0e, 0x73, 0x63, 0x68, 0x65, 0x6d, 0x61, 0x52, 0x65, 0x76, 0x69, 0x73, 0x69, + 0x6f, 0x6e, 0x12, 0x2b, 0x0a, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, + 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xba, 0x48, 0x04, 0x72, 0x02, 0x10, + 0x01, 0x52, 0x0c, 0x6f, 0x72, 0x67, 0x61, 0x6e, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x22, + 0xde, 0x02, 0x0a, 0x12, 0x52, 0x65, 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, + 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, + 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x69, 0x12, 0x46, 0x0a, 0x06, + 0x64, 0x69, 0x67, 0x65, 0x73, 0x74, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x2e, 0x2e, 0x61, + 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, + 0x73, 0x6f, 0x75, 0x72, 0x63, 0x65, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x6f, 0x72, + 0x2e, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x06, 0x64, 0x69, + 0x67, 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x18, + 0x04, 0x20, 0x01, 0x28, 0x0c, 0x52, 0x07, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x12, 0x2b, + 0x0a, 0x11, 0x64, 0x6f, 0x77, 0x6e, 0x6c, 0x6f, 0x61, 0x64, 0x5f, 0x6c, 0x6f, 0x63, 0x61, 0x74, + 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x64, 0x6f, 0x77, 0x6e, 0x6c, + 0x6f, 0x61, 0x64, 0x4c, 0x6f, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, + 0x65, 0x64, 0x69, 0x61, 0x5f, 0x74, 0x79, 0x70, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x09, 0x6d, 0x65, 0x64, 0x69, 0x61, 0x54, 0x79, 0x70, 0x65, 0x12, 0x39, 0x0a, 0x0b, 0x61, 0x6e, + 0x6e, 0x6f, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x0b, 0x32, + 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, + 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, 0x0b, 0x61, 0x6e, 0x6e, 0x6f, 0x74, 0x61, + 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x1a, 0x39, 0x0a, 0x0b, 0x44, 0x69, 0x67, 0x65, 0x73, 0x74, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x42, 0x4f, 0x5a, 0x4d, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, + 0x68, 0x61, 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2d, 0x64, 0x65, 0x76, 0x2f, 0x63, 0x68, 0x61, + 0x69, 0x6e, 0x6c, 0x6f, 0x6f, 0x70, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, + 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x63, 0x72, 0x61, 0x66, 0x74, 0x65, 0x72, 0x2f, 0x61, + 0x70, 0x69, 0x2f, 0x61, 0x74, 0x74, 0x65, 0x73, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x2f, 0x76, + 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto b/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto index 899e9fa10..6bdc5baf4 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.proto @@ -140,6 +140,12 @@ message PolicyEvaluation { // material type, if any, of the evaluated policy workflowcontract.v1.CraftingSchema.Material.MaterialType type = 8; + // whether this evaluation was skipped or not (because of an invalid input, for example) + bool skipped = 13; + + // Evaluation messages, intended to communicate evaluation errors (invalid input) + repeated string skip_reasons = 14; + message Violation { string subject = 1 [(buf.validate.field).required = true]; string message = 2 [(buf.validate.field).required = true]; diff --git a/pkg/attestation/crafter/crafter.go b/pkg/attestation/crafter/crafter.go index 2e3a2c314..ed6452ddc 100644 --- a/pkg/attestation/crafter/crafter.go +++ b/pkg/attestation/crafter/crafter.go @@ -569,7 +569,7 @@ func (c *Crafter) addMaterial(ctx context.Context, m *schemaapi.CraftingSchema_M c.CraftingState.Attestation.PolicyEvaluations = append(c.CraftingState.Attestation.PolicyEvaluations, policyResults...) // log policy violations - policies.LogPolicyViolations(c.CraftingState.Attestation.PolicyEvaluations, c.Logger) + policies.LogPolicyEvaluations(c.CraftingState.Attestation.PolicyEvaluations, c.Logger) // 5 - Attach it to state if c.CraftingState.Attestation.Materials == nil { diff --git a/pkg/attestation/renderer/chainloop/v02.go b/pkg/attestation/renderer/chainloop/v02.go index 7ce5420d7..4c8734f1d 100644 --- a/pkg/attestation/renderer/chainloop/v02.go +++ b/pkg/attestation/renderer/chainloop/v02.go @@ -57,6 +57,8 @@ type PolicyEvaluation struct { Violations []*PolicyViolation `json:"violations,omitempty"` With map[string]string `json:"with,omitempty"` Type string `json:"type"` + Skipped bool `json:"skipped"` + SkipReasons []string `json:"skip_reasons,omitempty"` } type PolicyViolation struct { @@ -118,7 +120,7 @@ func (r *RendererV02) Statement(ctx context.Context) (*intoto.Statement, error) } evaluations = append(evaluations, policyResults...) // log policy violations - policies.LogPolicyViolations(evaluations, r.logger) + policies.LogPolicyEvaluations(evaluations, r.logger) // insert attestation level policy results into statement if err = addPolicyResults(statement, evaluations); err != nil { @@ -313,6 +315,8 @@ func renderEvaluation(ev *v1.PolicyEvaluation) *PolicyEvaluation { "sha256": ev.ReferenceDigest, }, }, + SkipReasons: ev.SkipReasons, + Skipped: ev.Skipped, } } diff --git a/pkg/policies/engine/engine.go b/pkg/policies/engine/engine.go index b458324fe..dd61453ee 100644 --- a/pkg/policies/engine/engine.go +++ b/pkg/policies/engine/engine.go @@ -17,11 +17,18 @@ package engine import ( "context" + "fmt" ) type PolicyEngine interface { // Verify verifies an input against a policy - Verify(ctx context.Context, policy *Policy, input []byte, args map[string]any) ([]*PolicyViolation, error) + Verify(ctx context.Context, policy *Policy, input []byte, args map[string]any) (*EvaluationResult, error) +} + +type EvaluationResult struct { + Violations []*PolicyViolation + Skipped bool + SkipReason string } // PolicyViolation represents a policy failure @@ -29,10 +36,18 @@ type PolicyViolation struct { Subject, Violation string } -// Policy represents a loaded policy in any of the supported technologies. +// Policy represents a loaded policy in any of the supported engines. type Policy struct { // the source code for this policy Source []byte `json:"module"` // The unique policy name Name string `json:"name"` } + +type ResultFormatError struct { + Field string +} + +func (e ResultFormatError) Error() string { + return fmt.Sprintf("Policy result format error: %s not found or wrong format", e.Field) +} diff --git a/pkg/policies/engine/rego/rego.go b/pkg/policies/engine/rego/rego.go index 3f4d305d3..aabc10471 100644 --- a/pkg/policies/engine/rego/rego.go +++ b/pkg/policies/engine/rego/rego.go @@ -43,8 +43,8 @@ const ( // EnvironmentModePermissive allows all operations on the compiler EnvironmentModePermissive EnvironmentMode = 1 inputArgs = "args" - mainRule = "violations" - deprecatedMainRule = "deny" + deprecatedRule = "violations" + mainRule = "result" ) // builtinFuncNotAllowed is a list of builtin functions that are not allowed in the compiler @@ -64,7 +64,7 @@ var allowedNetworkDomains = []string{ // Force interface var _ engine.PolicyEngine = (*Rego)(nil) -func (r *Rego) Verify(ctx context.Context, policy *engine.Policy, input []byte, args map[string]any) ([]*engine.PolicyViolation, error) { +func (r *Rego) Verify(ctx context.Context, policy *engine.Policy, input []byte, args map[string]any) (*engine.EvaluationResult, error) { policyString := string(policy.Source) parsedModule, err := ast.ParseModule(policy.Name, policyString) if err != nil { @@ -112,29 +112,38 @@ func (r *Rego) Verify(ctx context.Context, policy *engine.Policy, input []byte, } // If res is nil, it means that the rule hasn't been found + // TODO: Remove when this deprecated rule is not used anymore if res == nil { // Try with the deprecated main rule - if err := executeQuery(deprecatedMainRule, r.OperatingMode == EnvironmentModeRestrictive); err != nil { + if err := executeQuery(deprecatedRule, r.OperatingMode == EnvironmentModeRestrictive); err != nil { return nil, err } if res == nil { - return nil, fmt.Errorf("failed to evaluate policy: no '%s' nor '%s' rule found", mainRule, deprecatedMainRule) + return nil, fmt.Errorf("failed to evaluate policy: neither '%s' nor '%s' rule found", mainRule, deprecatedRule) } + + return parseViolationsRule(res, policy) } + return parseResultRule(res, policy) +} + +// Parse deprecated list of violations. +// TODO: Remove this path once `result` rule is consolidated +func parseViolationsRule(res rego.ResultSet, policy *engine.Policy) (*engine.EvaluationResult, error) { violations := make([]*engine.PolicyViolation, 0) for _, exp := range res { for _, val := range exp.Expressions { ruleResults, ok := val.Value.([]interface{}) if !ok { - return nil, fmt.Errorf("failed to evaluate policy expression evaluation result: %s", val.Text) + return nil, engine.ResultFormatError{Field: deprecatedRule} } for _, result := range ruleResults { reasonStr, ok := result.(string) if !ok { - return nil, fmt.Errorf("failed to evaluate rule result: %s", val.Text) + return nil, engine.ResultFormatError{Field: deprecatedRule} } violations = append(violations, &engine.PolicyViolation{ @@ -145,7 +154,52 @@ func (r *Rego) Verify(ctx context.Context, policy *engine.Policy, input []byte, } } - return violations, nil + return &engine.EvaluationResult{ + Violations: violations, + Skipped: false, // best effort + SkipReason: "", + }, nil +} + +// parse `result` rule +func parseResultRule(res rego.ResultSet, policy *engine.Policy) (*engine.EvaluationResult, error) { + result := &engine.EvaluationResult{Violations: make([]*engine.PolicyViolation, 0)} + for _, exp := range res { + for _, val := range exp.Expressions { + ruleResult, ok := val.Value.(map[string]any) + if !ok { + return nil, engine.ResultFormatError{Field: mainRule} + } + + skipped, ok := ruleResult["skipped"].(bool) + if !ok { + return nil, engine.ResultFormatError{Field: "skipped"} + } + + reason, ok := ruleResult["skip_reason"].(string) + if !ok { + return nil, engine.ResultFormatError{Field: "skip_reason"} + } + + violations, ok := ruleResult["violations"].([]any) + if !ok { + return nil, engine.ResultFormatError{Field: "violations"} + } + + result.Skipped = skipped + result.SkipReason = reason + + for _, violation := range violations { + vs, ok := violation.(string) + if !ok { + return nil, fmt.Errorf("failed to evaluate violation in policy evaluation result: %s", val.Text) + } + result.Violations = append(result.Violations, &engine.PolicyViolation{Subject: policy.Name, Violation: vs}) + } + } + } + + return result, nil } func queryRego(ctx context.Context, ruleName string, parsedModule *ast.Module, options ...func(r *rego.Rego)) (rego.ResultSet, error) { diff --git a/pkg/policies/engine/rego/rego_test.go b/pkg/policies/engine/rego/rego_test.go index 6321108ba..ffcfc7b2e 100644 --- a/pkg/policies/engine/rego/rego_test.go +++ b/pkg/policies/engine/rego/rego_test.go @@ -36,21 +36,22 @@ func TestRego_VerifyWithValidPolicy(t *testing.T) { } t.Run("invalid input", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) + result, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) require.NoError(t, err) - assert.Len(t, violations, 2) - assert.Contains(t, violations, &engine.PolicyViolation{ + assert.False(t, result.Skipped) + assert.Len(t, result.Violations, 2) + assert.Contains(t, result.Violations, &engine.PolicyViolation{ Subject: policy.Name, Violation: "Container image is not approved", }) - assert.Contains(t, violations, &engine.PolicyViolation{ + assert.Contains(t, result.Violations, &engine.PolicyViolation{ Subject: policy.Name, Violation: "Container image is not released", }) }) t.Run("valid input", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` + result, err := r.Verify(context.TODO(), policy, []byte(` { "kind": "CONTAINER_IMAGE", "references": [{ @@ -61,47 +62,8 @@ func TestRego_VerifyWithValidPolicy(t *testing.T) { }] }`), nil) require.NoError(t, err) - assert.Len(t, violations, 0) - }) -} - -func TestRego_VerifyWithDeprecatedPolicy(t *testing.T) { - regoContent, err := os.ReadFile("testfiles/check_qa_deprecated.rego") - require.NoError(t, err) - - r := &Rego{} - policy := &engine.Policy{ - Name: "check approval", - Source: regoContent, - } - - t.Run("invalid input", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) - require.NoError(t, err) - assert.Len(t, violations, 2) - assert.Contains(t, violations, &engine.PolicyViolation{ - Subject: policy.Name, - Violation: "Container image is not approved", - }) - assert.Contains(t, violations, &engine.PolicyViolation{ - Subject: policy.Name, - Violation: "Container image is not released", - }) - }) - - t.Run("valid input", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` - { - "kind": "CONTAINER_IMAGE", - "references": [{ - "metadata": {"name": "chainloop-platform-qa-approval"}, - "annotations": {"approval": "true"} - }, { - "metadata": {"name": "chainloop-platform-release-production"} - }] - }`), nil) - require.NoError(t, err) - assert.Len(t, violations, 0) + assert.False(t, result.Skipped) + assert.Len(t, result.Violations, 0) }) } @@ -116,26 +78,26 @@ func TestRego_VerifyWithArguments(t *testing.T) { } t.Run("no violations", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` + result, err := r.Verify(context.TODO(), policy, []byte(` { "kind": "CONTAINER_IMAGE" }`), map[string]interface{}{"foo": "hello"}, ) require.NoError(t, err) - assert.Len(t, violations, 0) + assert.Len(t, result.Violations, 0) }) t.Run("with violations", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` + result, err := r.Verify(context.TODO(), policy, []byte(` { "kind": "CONTAINER_IMAGE" }`), map[string]interface{}{"foo": "bar"}, ) require.NoError(t, err) - assert.Len(t, violations, 1) - assert.Contains(t, violations, &engine.PolicyViolation{ + assert.Len(t, result.Violations, 1) + assert.Contains(t, result.Violations, &engine.PolicyViolation{ Subject: "foobar", Violation: "foo is bar"}, ) }) @@ -151,28 +113,28 @@ func TestRego_VerifyWithComplexArguments(t *testing.T) { } t.Run("violation with array args", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` + result, err := r.Verify(context.TODO(), policy, []byte(` { "kind": "CONTAINER_IMAGE" }`), map[string]interface{}{"foo": []string{"hello", "bar"}}, ) require.NoError(t, err) - assert.Len(t, violations, 1) - assert.Contains(t, violations, &engine.PolicyViolation{ + assert.Len(t, result.Violations, 1) + assert.Contains(t, result.Violations, &engine.PolicyViolation{ Subject: "foobar", Violation: "foo has bar"}, ) }) t.Run("with array args", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(` + result, err := r.Verify(context.TODO(), policy, []byte(` { "kind": "CONTAINER_IMAGE" }`), map[string]interface{}{"foo": []string{"hello", "world"}}, ) require.NoError(t, err) - assert.Len(t, violations, 0) + assert.Len(t, result.Violations, 0) }) } @@ -188,10 +150,47 @@ func TestRego_VerifyInvalidPolicy(t *testing.T) { } t.Run("doesn't eval a main rule", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) + _, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) + assert.Error(t, err) + assert.Contains(t, err.Error(), "neither 'result' nor 'violations' rule found") + }) +} + +func TestRego_ResultFormat(t *testing.T) { + regoContent, err := os.ReadFile("testfiles/result_format.rego") + require.NoError(t, err) + + r := &Rego{} + policy := &engine.Policy{ + Name: "result-output", + Source: regoContent, + } + + t.Run("empty input", func(t *testing.T) { + _, err := r.Verify(context.TODO(), policy, []byte{}, nil) assert.Error(t, err) - assert.Contains(t, err.Error(), "no 'violations' nor 'deny' rule found") - assert.Len(t, violations, 0) + }) + + t.Run("invalid input", func(t *testing.T) { + result, err := r.Verify(context.TODO(), policy, []byte("{\"foo\": \"bar\"}"), nil) + require.NoError(t, err) + assert.True(t, result.Skipped) + assert.Equal(t, "invalid input", result.SkipReason) + }) + + t.Run("valid input, no violations", func(t *testing.T) { + result, err := r.Verify(context.TODO(), policy, []byte("{\"specVersion\": \"1.5\"}"), nil) + require.NoError(t, err) + assert.False(t, result.Skipped) + assert.Len(t, result.Violations, 0) + }) + + t.Run("valid input, violations", func(t *testing.T) { + result, err := r.Verify(context.TODO(), policy, []byte("{\"specVersion\": \"1.4\"}"), nil) + require.NoError(t, err) + assert.False(t, result.Skipped) + assert.Len(t, result.Violations, 1) + assert.Equal(t, "wrong CycloneDX version. Expected 1.5, but it was 1.4", result.Violations[0].Violation) }) } @@ -206,12 +205,11 @@ func TestRego_WithRestrictiveMode(t *testing.T) { Source: regoContent, } - violations, err := r.Verify(context.TODO(), policy, []byte(`{}`), nil) + _, err = r.Verify(context.TODO(), policy, []byte(`{}`), nil) assert.Error(t, err) assert.Contains(t, err.Error(), "rego_type_error: undefined function opa.runtime") assert.Contains(t, err.Error(), "rego_type_error: undefined function trace") assert.Contains(t, err.Error(), "rego_type_error: undefined function rego.parse_module") - assert.Len(t, violations, 0) }) t.Run("forbidden network requests", func(t *testing.T) { @@ -224,10 +222,9 @@ func TestRego_WithRestrictiveMode(t *testing.T) { Source: regoContent, } - violations, err := r.Verify(context.TODO(), policy, []byte(`{}`), nil) + _, err = r.Verify(context.TODO(), policy, []byte(`{}`), nil) assert.Error(t, err) assert.Contains(t, err.Error(), "eval_builtin_error: http.send: unallowed host: example.com") - assert.Len(t, violations, 0) }) t.Run("allowed network requests", func(t *testing.T) { @@ -258,11 +255,10 @@ func TestRego_WithPermissiveMode(t *testing.T) { } t.Run("allowed functions", func(t *testing.T) { - violations, err := r.Verify(context.TODO(), policy, []byte(`{}`), nil) + _, err = r.Verify(context.TODO(), policy, []byte(`{}`), nil) assert.Error(t, err) assert.NotContains(t, err.Error(), "rego_type_error: undefined function opa.runtime") assert.NotContains(t, err.Error(), "rego_type_error: undefined function trace") assert.NotContains(t, err.Error(), "rego_type_error: undefined function rego.parse_module") - assert.Len(t, violations, 0) }) } diff --git a/pkg/policies/engine/rego/testfiles/check_qa_deprecated.rego b/pkg/policies/engine/rego/testfiles/check_qa_deprecated.rego deleted file mode 100644 index 6ba9ca6e9..000000000 --- a/pkg/policies/engine/rego/testfiles/check_qa_deprecated.rego +++ /dev/null @@ -1,26 +0,0 @@ -package main - -deny[msg] { - not is_released - - msg:= "Container image is not released" -} - -deny[msg] { - not is_approved - - msg:= "Container image is not approved" -} - -is_approved { - input.kind == "CONTAINER_IMAGE" - - input.references[i].metadata.name == "chainloop-platform-qa-approval" - input.references[i].annotations.approval == "true" -} - -is_released { - input.kind == "CONTAINER_IMAGE" - - input.references[i].metadata.name == "chainloop-platform-release-production" -} diff --git a/pkg/policies/engine/rego/testfiles/result_format.rego b/pkg/policies/engine/rego/testfiles/result_format.rego new file mode 100644 index 000000000..89dd3cbd7 --- /dev/null +++ b/pkg/policies/engine/rego/testfiles/result_format.rego @@ -0,0 +1,30 @@ +package main + +import rego.v1 + +result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, +} + +default skip_reason := "" + +skip_reason := m if { + not valid_input + m := "invalid input" +} + +default skipped := true + +skipped := false if valid_input + +violations contains msg if { + valid_input + input.specVersion != "1.5" + msg := sprintf("wrong CycloneDX version. Expected 1.5, but it was %s", [input.specVersion]) +} + +valid_input if { + input.specVersion +} diff --git a/pkg/policies/policies.go b/pkg/policies/policies.go index 9883e28d2..f5baac056 100644 --- a/pkg/policies/policies.go +++ b/pkg/policies/policies.go @@ -126,9 +126,26 @@ func (pv *PolicyVerifier) evaluatePolicyAttachment(ctx context.Context, attachme pv.logger.Info().Msgf("evaluating policy %s against attestation", policy.Metadata.Name) } - violations, sources, err := pv.executeScripts(ctx, policy, scripts, material, attachment) - if err != nil { - return nil, NewPolicyError(err) + sources := make([]string, 0) + evalResults := make([]*engine.EvaluationResult, 0) + skipped := true + reasons := make([]string, 0) + for _, script := range scripts { + r, err := pv.executeScript(ctx, policy, script, material, attachment) + if err != nil { + return nil, NewPolicyError(err) + } + + // Gather merged results + evalResults = append(evalResults, r) + + if r.SkipReason != "" { + reasons = append(reasons, r.SkipReason) + } + + // Skipped = false if any of the evaluations was not skipped + skipped = skipped && r.Skipped + sources = append(sources, base64.StdEncoding.EncodeToString(script.Source)) } var evaluationSources []string @@ -136,17 +153,28 @@ func (pv *PolicyVerifier) evaluatePolicyAttachment(ctx context.Context, attachme evaluationSources = sources } + // Only inform skip reasons if it's skipped + if !skipped { + reasons = []string{} + } + + // Merge multi-kind results return &v12.PolicyEvaluation{ - Name: policy.GetMetadata().GetName(), - MaterialName: opts.name, - Sources: evaluationSources, - Violations: engineViolationsToAPIViolations(violations), + Name: policy.GetMetadata().GetName(), + MaterialName: opts.name, + Sources: evaluationSources, + // merge all violations + Violations: engineEvaluationsToAPIViolations(evalResults), Annotations: policy.GetMetadata().GetAnnotations(), Description: policy.GetMetadata().GetDescription(), With: attachment.GetWith(), Type: opts.kind, ReferenceName: ref.Name, ReferenceDigest: ref.Digest["sha256"], + // Merged "skipped" + Skipped: skipped, + // Merged "skip_reason" + SkipReasons: reasons, }, nil } @@ -171,24 +199,15 @@ func (pv *PolicyVerifier) VerifyStatement(ctx context.Context, statement *intoto return result, nil } -func (pv *PolicyVerifier) executeScripts(ctx context.Context, policy *v1.Policy, scripts []*engine.Policy, material []byte, att *v1.PolicyAttachment) ([]*engine.PolicyViolation, []string, error) { - violations := make([]*engine.PolicyViolation, 0) - sources := make([]string, 0) - - for _, script := range scripts { - // verify the policy - ng := getPolicyEngine(policy) - res, err := ng.Verify(ctx, script, material, getInputArguments(att.GetWith())) - if err != nil { - return nil, nil, NewPolicyError(err) - } - - sources = append(sources, base64.StdEncoding.EncodeToString(script.Source)) - - violations = append(violations, res...) +func (pv *PolicyVerifier) executeScript(ctx context.Context, policy *v1.Policy, script *engine.Policy, material []byte, att *v1.PolicyAttachment) (*engine.EvaluationResult, error) { + // verify the policy + ng := getPolicyEngine(policy) + res, err := ng.Verify(ctx, script, material, getInputArguments(att.GetWith())) + if err != nil { + return nil, fmt.Errorf("failed to execute policy : %w", err) } - return violations, sources, nil + return res, nil } // LoadPolicySpec loads and validates a policy spec from a contract @@ -305,13 +324,15 @@ func getValue(values []string) any { return lines[0] } -func engineViolationsToAPIViolations(input []*engine.PolicyViolation) []*v12.PolicyEvaluation_Violation { +func engineEvaluationsToAPIViolations(results []*engine.EvaluationResult) []*v12.PolicyEvaluation_Violation { res := make([]*v12.PolicyEvaluation_Violation, 0) - for _, v := range input { - res = append(res, &v12.PolicyEvaluation_Violation{ - Subject: v.Subject, - Message: v.Violation, - }) + for _, r := range results { + for _, v := range r.Violations { + res = append(res, &v12.PolicyEvaluation_Violation{ + Subject: v.Subject, + Message: v.Violation, + }) + } } return res @@ -491,13 +512,17 @@ func loadLegacyPolicyScript(spec *v1.PolicySpec) ([]byte, error) { return content, nil } -func LogPolicyViolations(evaluations []*v12.PolicyEvaluation, logger *zerolog.Logger) { +func LogPolicyEvaluations(evaluations []*v12.PolicyEvaluation, logger *zerolog.Logger) { for _, policyEval := range evaluations { + subject := policyEval.MaterialName + if subject == "" { + subject = "statement" + } + + if policyEval.Skipped { + logger.Warn().Msgf("policy evaluation skipped (%s) for %s. Reasons: %s", policyEval.Name, subject, policyEval.SkipReasons) + } if len(policyEval.Violations) > 0 { - subject := policyEval.MaterialName - if subject == "" { - subject = "statement" - } logger.Warn().Msgf("found policy violations (%s) for %s", policyEval.Name, subject) for _, v := range policyEval.Violations { logger.Warn().Msgf(" - %s", v.Message) diff --git a/pkg/policies/policies_test.go b/pkg/policies/policies_test.go index ff224a3da..acda2ca0c 100644 --- a/pkg/policies/policies_test.go +++ b/pkg/policies/policies_test.go @@ -798,6 +798,83 @@ func (s *testSuite) TestInputArguments() { } } +func (s *testSuite) TestNewResultFormat() { + cases := []struct { + name string + policy string + material string + expectErr bool + expectViolations int + expectSkipped bool + expectReasons []string + }{ + { + name: "result.violations", + policy: "file://testdata/policy_result_format.yaml", + material: "{\"specVersion\": \"1.4\"}", + expectViolations: 1, + }, + { + name: "skip", + policy: "file://testdata/policy_result_format.yaml", + material: "{\"invalid\": \"1.4\"}", + expectSkipped: true, + expectReasons: []string{"invalid input"}, + }, + { + name: "skip multiple", + policy: "file://testdata/policy_result_skipped.yaml", + material: "{}", + expectSkipped: true, + expectReasons: []string{"this one is skipped", "this is also skipped"}, + }, + } + + for _, tc := range cases { + s.Run(tc.name, func() { + schema := &v12.CraftingSchema{ + Materials: []*v12.CraftingSchema_Material{ + { + Name: "sbom", + Type: v12.CraftingSchema_Material_SBOM_CYCLONEDX_JSON, + }, + }, + Policies: &v12.Policies{ + Materials: []*v12.PolicyAttachment{ + { + Policy: &v12.PolicyAttachment_Ref{Ref: tc.policy}, + }, + }, + Attestation: nil, + }, + } + material := &v1.Attestation_Material{ + M: &v1.Attestation_Material_Artifact_{Artifact: &v1.Attestation_Material_Artifact{ + Content: []byte(tc.material), + }}, + MaterialType: v12.CraftingSchema_Material_SBOM_CYCLONEDX_JSON, + InlineCas: true, + } + + verifier := NewPolicyVerifier(schema, nil, &s.logger) + res, err := verifier.VerifyMaterial(context.TODO(), material, "") + + if tc.expectErr { + s.Error(err) + return + } + + s.Require().NoError(err) + s.Len(res, 1) + s.Len(res[0].Violations, tc.expectViolations) + s.Equal(tc.expectSkipped, res[0].Skipped) + if len(res[0].SkipReasons) > 0 { + s.Equal(res[0].SkipReasons, tc.expectReasons) + } + }) + } +} + type testSuite struct { suite.Suite diff --git a/pkg/policies/testdata/policy_result_format.yaml b/pkg/policies/testdata/policy_result_format.yaml new file mode 100644 index 000000000..830b439ab --- /dev/null +++ b/pkg/policies/testdata/policy_result_format.yaml @@ -0,0 +1,41 @@ +apiVersion: workflowcontract.chainloop.dev/v1 +kind: Policy +metadata: + name: policy-result-format + description: Policy with new result format + annotations: + category: SBOM +spec: + policies: + - kind: SBOM_CYCLONEDX_JSON + embedded: | + package main + + import rego.v1 + + result := { + "skipped": skipped, + "violations": violations, + "skip_reason": skip_reason, + } + + default skip_reason := "" + + skip_reason := m if { + not valid_input + m := "invalid input" + } + + default skipped := true + + skipped := false if valid_input + + violations contains msg if { + valid_input + input.specVersion != "1.5" + msg := sprintf("wrong CycloneDX version. Expected 1.5, but it was %s", [input.specVersion]) + } + + valid_input if { + input.specVersion + } diff --git a/pkg/policies/testdata/policy_result_skipped.yaml b/pkg/policies/testdata/policy_result_skipped.yaml new file mode 100644 index 000000000..1f71881ae --- /dev/null +++ b/pkg/policies/testdata/policy_result_skipped.yaml @@ -0,0 +1,31 @@ +apiVersion: workflowcontract.chainloop.dev/v1 +kind: Policy +metadata: + name: policy-result-skipped + description: Policy with new result format + annotations: + category: SBOM +spec: + policies: + - kind: SBOM_CYCLONEDX_JSON + embedded: | + package main + + import rego.v1 + + result := { + "skipped": true, + "violations": [], + "skip_reason": "this one is skipped", + } + - kind: SBOM_CYCLONEDX_JSON + embedded: | + package main + + import rego.v1 + + result := { + "skipped": true, + "violations": [], + "skip_reason": "this is also skipped", + } \ No newline at end of file