From dfac4a4fd7f5633002a781c89f27c3202b01e243 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 28 Oct 2026 09:38:00 +0100 Subject: [PATCH 1/3] feat(materials): add BUILD_LOG material type for compiler build logs Add a BUILD_LOG material for the logs a build writes, so that a policy can check the compiler warnings of a build. The material reads an MSBuild binary log (format 18 or later), an MSBuild text log (one file can hold several builds), a Visual C++ BuildLog.htm (UTF-16), or a zip or tar archive of any of them. The file is uploaded as is. Policies get one normalized report: each log with its archive entry, solution, result and the warning counts it states and that could be read, and the diagnostics of all logs, with repeats folded into one entry that lists the logs it was seen in. BUILD_LOG is not auto-detected: a contract or --kind must name it. Signed-off-by: Javier Rodriguez Chainloop-Trace-Sessions: 22fceba7-c836-4440-b83a-be86dfa26a91 --- app/cli/documentation/cli-reference.md | 4 +- .../workflowcontract/v1/crafting_schema.ts | 11 + ...on.v1.Attestation.Material.jsonschema.json | 6 +- ...tation.v1.Attestation.Material.schema.json | 6 +- ...tation.v1.PolicyEvaluation.jsonschema.json | 3 +- ...ttestation.v1.PolicyEvaluation.schema.json | 3 +- ...v1.CraftingSchema.Material.jsonschema.json | 3 +- ...act.v1.CraftingSchema.Material.schema.json | 3 +- ...ct.v1.PolicyGroup.Material.jsonschema.json | 3 +- ...ntract.v1.PolicyGroup.Material.schema.json | 3 +- ...flowcontract.v1.PolicySpec.jsonschema.json | 3 +- ...workflowcontract.v1.PolicySpec.schema.json | 3 +- ...owcontract.v1.PolicySpecV2.jsonschema.json | 3 +- ...rkflowcontract.v1.PolicySpecV2.schema.json | 3 +- .../workflowcontract/v1/crafting_schema.pb.go | 15 +- .../workflowcontract/v1/crafting_schema.proto | 4 + .../v1/crafting_schema_test.go | 4 + .../v1/crafting_schema_validations.go | 3 + .../api/attestation/v1/crafting_state.go | 11 + .../api/attestation/v1/crafting_state_test.go | 29 ++ .../crafter/materials/build_log.go | 95 ++++ .../crafter/materials/build_log_test.go | 185 +++++++ .../crafter/materials/buildlog/binlog.go | 453 +++++++++++++++++ .../crafter/materials/buildlog/binlog_test.go | 240 +++++++++ .../crafter/materials/buildlog/buildlog.go | 463 ++++++++++++++++++ .../materials/buildlog/buildlog_test.go | 419 ++++++++++++++++ .../materials/buildlog/fidelity_test.go | 187 +++++++ .../materials/buildlog/localized_test.go | 172 +++++++ .../crafter/materials/buildlog/text.go | 338 +++++++++++++ .../crafter/materials/materials.go | 2 + .../materials/testdata/buildlog/lib.binlog | Bin 0 -> 110333 bytes .../materials/testdata/buildlog/msbuild.txt | 179 +++++++ .../materials/testdata/buildlog/sample.binlog | Bin 0 -> 163968 bytes .../materials/testdata/buildlog/vcbuild.htm | Bin 0 -> 4188 bytes 34 files changed, 2836 insertions(+), 20 deletions(-) create mode 100644 pkg/attestation/crafter/materials/build_log.go create mode 100644 pkg/attestation/crafter/materials/build_log_test.go create mode 100644 pkg/attestation/crafter/materials/buildlog/binlog.go create mode 100644 pkg/attestation/crafter/materials/buildlog/binlog_test.go create mode 100644 pkg/attestation/crafter/materials/buildlog/buildlog.go create mode 100644 pkg/attestation/crafter/materials/buildlog/buildlog_test.go create mode 100644 pkg/attestation/crafter/materials/buildlog/fidelity_test.go create mode 100644 pkg/attestation/crafter/materials/buildlog/localized_test.go create mode 100644 pkg/attestation/crafter/materials/buildlog/text.go create mode 100644 pkg/attestation/crafter/materials/testdata/buildlog/lib.binlog create mode 100644 pkg/attestation/crafter/materials/testdata/buildlog/msbuild.txt create mode 100644 pkg/attestation/crafter/materials/testdata/buildlog/sample.binlog create mode 100644 pkg/attestation/crafter/materials/testdata/buildlog/vcbuild.htm diff --git a/app/cli/documentation/cli-reference.md b/app/cli/documentation/cli-reference.md index b6d08f84a..4c9addc02 100644 --- a/app/cli/documentation/cli-reference.md +++ b/app/cli/documentation/cli-reference.md @@ -264,7 +264,7 @@ Options --append reserved for a future release: will control whether --policy-input and --policy-input-from-file append to (rather than replace) the contract-declared value; has no effect yet --attestation-id string Unique identifier of the in-progress attestation -h, --help help for add ---kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "BUILD_LOG" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --max-extract-entries int max number of files to extract when --value is an archive (default 10000) --max-extract-size string max total uncompressed size to extract when --value is an archive (default "1GiB") --name string name of the material as shown in the contract @@ -2876,7 +2876,7 @@ Options --annotation strings Key-value pairs of material annotations (key=value) -h, --help help for eval --input stringArray Key-value pairs of policy inputs (key=value) ---kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "BUILD_LOG" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --material string Path to material or attestation file -p, --policy string Policy reference (./my-policy.yaml, https://my-domain.com/my-policy.yaml, chainloop://my-stored-policy) (default "policy.yaml") --project string Project name to use as engine context for chainloop.* built-ins diff --git a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts index 6e924df09..9a7b98f9e 100644 --- a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts +++ b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts @@ -342,6 +342,12 @@ export enum CraftingSchema_Material_MaterialType { * and byte-verifiable evidence anchors backing each claim */ CHAINLOOP_AI_SECURITY_CONTEXT = 45, + /** + * BUILD_LOG - Build log of Microsoft build tools: an MSBuild binary log (-bl), an + * MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive + * of them. Its compiler and build warnings and errors are evaluated. + */ + BUILD_LOG = 46, UNRECOGNIZED = -1, } @@ -485,6 +491,9 @@ export function craftingSchema_Material_MaterialTypeFromJSON(object: any): Craft case 45: case "CHAINLOOP_AI_SECURITY_CONTEXT": return CraftingSchema_Material_MaterialType.CHAINLOOP_AI_SECURITY_CONTEXT; + case 46: + case "BUILD_LOG": + return CraftingSchema_Material_MaterialType.BUILD_LOG; case -1: case "UNRECOGNIZED": default: @@ -586,6 +595,8 @@ export function craftingSchema_Material_MaterialTypeToJSON(object: CraftingSchem return "PITEST_XML"; case CraftingSchema_Material_MaterialType.CHAINLOOP_AI_SECURITY_CONTEXT: return "CHAINLOOP_AI_SECURITY_CONTEXT"; + case CraftingSchema_Material_MaterialType.BUILD_LOG: + return "BUILD_LOG"; case CraftingSchema_Material_MaterialType.UNRECOGNIZED: default: return "UNRECOGNIZED"; diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json index 1e9131d91..63364b108 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json @@ -62,7 +62,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" @@ -160,7 +161,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json index 3661c9bf4..51c5d3dca 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json @@ -62,7 +62,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" @@ -160,7 +161,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" 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 78a2c9514..315ce7eb7 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json @@ -173,7 +173,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" 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 ed6228d36..c3e2a8be6 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json @@ -173,7 +173,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json index 3203a4ea4..3858a8f0e 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json @@ -83,7 +83,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json index 0c0d8190c..73a83a27a 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json @@ -83,7 +83,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json index aaac50622..dce614f15 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json @@ -107,7 +107,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json index 3ead8e336..66c3acf3b 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json @@ -107,7 +107,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json index a96f72d35..f439de8b5 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json @@ -81,7 +81,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json index cb2fa59ff..5f4823951 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json @@ -81,7 +81,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json index a14cea087..baca32956 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json @@ -102,7 +102,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json index 15dbc37f3..3f6e96ba8 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json @@ -102,7 +102,8 @@ "CHECKMARX_JSON", "OVERSECURED_JSON", "PITEST_XML", - "CHAINLOOP_AI_SECURITY_CONTEXT" + "CHAINLOOP_AI_SECURITY_CONTEXT", + "BUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go index d5e6b58ce..2e31a7365 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go @@ -260,6 +260,10 @@ const ( // repository's fix history, the attack surfaces they share, ranked risks, // and byte-verifiable evidence anchors backing each claim CraftingSchema_Material_CHAINLOOP_AI_SECURITY_CONTEXT CraftingSchema_Material_MaterialType = 45 + // Build log of Microsoft build tools: an MSBuild binary log (-bl), an + // MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive + // of them. Its compiler and build warnings and errors are evaluated. + CraftingSchema_Material_BUILD_LOG CraftingSchema_Material_MaterialType = 46 ) // Enum value maps for CraftingSchema_Material_MaterialType. @@ -311,6 +315,7 @@ var ( 43: "OVERSECURED_JSON", 44: "PITEST_XML", 45: "CHAINLOOP_AI_SECURITY_CONTEXT", + 46: "BUILD_LOG", } CraftingSchema_Material_MaterialType_value = map[string]int32{ "MATERIAL_TYPE_UNSPECIFIED": 0, @@ -359,6 +364,7 @@ var ( "OVERSECURED_JSON": 43, "PITEST_XML": 44, "CHAINLOOP_AI_SECURITY_CONTEXT": 45, + "BUILD_LOG": 46, } ) @@ -2105,7 +2111,7 @@ var File_workflowcontract_v1_crafting_schema_proto protoreflect.FileDescriptor const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\n" + - ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\x99\x13\n" + + ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\xa8\x13\n" + "\x0eCraftingSchema\x122\n" + "\x0eschema_version\x18\x01 \x01(\tB\v\xbaH\x06r\x04\n" + "\x02v1\x18\x01R\rschemaVersion\x12N\n" + @@ -2128,7 +2134,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x0fDAGGER_PIPELINE\x10\x06\x12\x15\n" + "\x11TEAMCITY_PIPELINE\x10\a\x12\x13\n" + "\x0fTEKTON_PIPELINE\x10\b\x12\x15\n" + - "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\xe4\f\n" + + "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\xf3\f\n" + "\bMaterial\x12[\n" + "\x04type\x18\x01 \x01(\x0e29.workflowcontract.v1.CraftingSchema.Material.MaterialTypeB\f\xbaH\a\x82\x01\x04\x10\x01 \x00\x18\x01R\x04type\x12\x99\x01\n" + "\x04name\x18\x02 \x01(\tB\x84\x01\xbaH\x7f\xba\x01|\n" + @@ -2139,7 +2145,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\vskip_upload\x18\x06 \x01(\bR\n" + "skipUpload\x12\xaa\x01\n" + "\x05group\x18\a \x01(\tB\x93\x01\xbaH\x8f\x01\xba\x01\x8b\x01\n" + - "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\x89\b\n" + + "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\x98\b\n" + "\fMaterialType\x12\x1d\n" + "\x19MATERIAL_TYPE_UNSPECIFIED\x10\x00\x12\n" + "\n" + @@ -2191,7 +2197,8 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x10OVERSECURED_JSON\x10+\x12\x0e\n" + "\n" + "PITEST_XML\x10,\x12!\n" + - "\x1dCHAINLOOP_AI_SECURITY_CONTEXT\x10-:\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + + "\x1dCHAINLOOP_AI_SECURITY_CONTEXT\x10-\x12\r\n" + + "\tBUILD_LOG\x10.:\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + "\x10CraftingSchemaV2\x128\n" + "\vapi_version\x18\x01 \x01(\tB\x17\xbaH\x14r\x12\n" + "\x10chainloop.dev/v1R\n" + diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto index 46f879052..8c0bd395e 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto @@ -209,6 +209,10 @@ message CraftingSchema { // repository's fix history, the attack surfaces they share, ranked risks, // and byte-verifiable evidence anchors backing each claim CHAINLOOP_AI_SECURITY_CONTEXT = 45; + // Build log of Microsoft build tools: an MSBuild binary log (-bl), an + // MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive + // of them. Its compiler and build warnings and errors are evaluated. + BUILD_LOG = 46; } } } diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go b/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go index e1d3809d9..66c656332 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go @@ -359,6 +359,10 @@ func TestMaterialKindsExcludedFromAutoDetection(t *testing.T) { name: "radamsa crashes accept almost any non-empty file", kind: v1.CraftingSchema_Material_RADAMSA_CRASHES, }, + { + name: "build log probing parses whole logs of hundreds of megabytes", + kind: v1.CraftingSchema_Material_BUILD_LOG, + }, } for _, tc := range testCases { diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go index 99db20ae6..d87741386 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go @@ -42,6 +42,9 @@ import ( // an app platform), but the export envelope is not published by the vendor, // so pinning it here would bet every other JSON kind on a shape inferred // from a sample. +// - BUILD_LOG: recognizing a log means parsing it, and diagnostic-verbosity +// text logs reach hundreds of megabytes; probing every file for it would +// slow down every auto-detected material. // // TestMaterialKindsExcludedFromAutoDetection pins these exclusions. var CraftingMaterialInValidationOrder = []CraftingSchema_Material_MaterialType{ diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state.go index 07ea4766e..413b2e97f 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.go @@ -27,6 +27,7 @@ import ( v1 "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/accesschk" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/attestation" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/buildlog" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/cobertura" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/dranzer" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/jacoco" @@ -350,6 +351,16 @@ func (m *Attestation_Material) ingestMaterialToJSON(rawMaterial []byte, value st return nil, fmt.Errorf("invalid dranzer material: %w", err) } return json.Marshal(bundle) + case v1.CraftingSchema_Material_BUILD_LOG: + // Build logs are binary (MSBuild -bl), plain text or HTML, possibly + // several in an archive. They project to the normalized diagnostics + // only, with no raw text: diagnostic-verbosity text logs reach hundreds + // of megabytes and the policy engine holds its whole input in memory. + report, err := buildlog.Parse(rawMaterial) + if err != nil { + return nil, fmt.Errorf("invalid build log material: %w", err) + } + return json.Marshal(report) } return rawMaterial, nil diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go index b0495130a..e2159bb27 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go @@ -372,6 +372,35 @@ func TestDranzerBundleIsEvaluable(t *testing.T) { assert.Len(t, decoded["reports"], 4) } +// TestBuildLogIsEvaluable guards that a BUILD_LOG material, which is stored as +// the raw (often gzip-compressed binary) log, projects to the normalized +// diagnostics policies evaluate. Handing the engine the raw bytes would fail +// to parse, or for a text log yield nothing to evaluate. +func TestBuildLogIsEvaluable(t *testing.T) { + m := &Attestation_Material{ + MaterialType: schemaapi.CraftingSchema_Material_BUILD_LOG, + M: &Attestation_Material_Artifact_{ + Artifact: &Attestation_Material_Artifact{Name: "build-log", Digest: "sha256:deadbeef"}, + }, + } + + for _, name := range []string{"sample.binlog", "msbuild.txt", "vcbuild.htm"} { + t.Run(name, func(t *testing.T) { + content, err := m.GetEvaluableContent("../../../materials/testdata/buildlog/"+name, nil) + require.NoError(t, err) + + var decoded map[string]any + require.NoError(t, json.NewDecoder(bytes.NewReader(content)).Decode(&decoded)) + + assert.NotEmpty(t, decoded["tool"].(map[string]any)["name"]) + assert.EqualValues(t, 4, decoded["summary"].(map[string]any)["warnings"]) + assert.Len(t, decoded["diagnostics"], 4) + assert.Len(t, decoded["logs"], 1) + assert.Contains(t, decoded, "chainloop_metadata") + }) + } +} + // TestRadamsaReportArchiveIsEvaluable guards that a RADAMSA_REPORT material whose // value is an archive of per-run -M logs projects to input.elements holding the // records merged across every entry. Recording the archive whole without merging diff --git a/pkg/attestation/crafter/materials/build_log.go b/pkg/attestation/crafter/materials/build_log.go new file mode 100644 index 000000000..cd9385852 --- /dev/null +++ b/pkg/attestation/crafter/materials/build_log.go @@ -0,0 +1,95 @@ +// +// Copyright 2026 The Chainloop 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 materials + +import ( + "context" + "errors" + "fmt" + "strconv" + + schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" + api "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/api/attestation/v1" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/buildlog" + "github.com/chainloop-dev/chainloop/pkg/casclient" + "github.com/rs/zerolog" +) + +const ( + // AnnotationBuildLogLogsCount is the number of build logs in a BUILD_LOG + // material: 1 for a single log, or the number of logs found in an archive. + AnnotationBuildLogLogsCount = "chainloop.material.build_log.logs.count" + // AnnotationBuildLogWarningsCount and AnnotationBuildLogErrorsCount are the + // number of distinct warnings and errors in a BUILD_LOG material. + AnnotationBuildLogWarningsCount = "chainloop.material.build_log.warnings.count" + AnnotationBuildLogErrorsCount = "chainloop.material.build_log.errors.count" +) + +// BuildLogCrafter stores the log of a Microsoft build (MSBuild binary or text +// log, or a Visual C++ BuildLog.htm) as evidence of the warnings and errors the +// build produced. The log is stored as-is; its projection to the JSON the +// policy engine consumes happens later, at evaluation time. +// +// The value may also be an archive of logs, for builds that produce one binary +// log per solution. The archive is recorded whole and the projection +// aggregates its entries. +type BuildLogCrafter struct { + *crafterCommon + backend *casclient.CASBackend +} + +func NewBuildLogCrafter(schema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*BuildLogCrafter, error) { + if schema.Type != schemaapi.CraftingSchema_Material_BUILD_LOG { + return nil, fmt.Errorf("material type is not a build log") + } + craftCommon := &crafterCommon{logger: l, input: schema} + return &BuildLogCrafter{backend: backend, crafterCommon: craftCommon}, nil +} + +func (i *BuildLogCrafter) Craft(ctx context.Context, filePath string) (*CraftResult, error) { + // Parse with the same code the policy projection uses, so a material + // accepted here is one the projection can read. Were the two to disagree, a + // log would be attested and then fail, or worse pass, the policy gate. + report, err := buildlog.ParseFile(filePath) + if err != nil { + if errors.Is(err, buildlog.ErrNoBuildLog) { + return nil, fmt.Errorf("input does not look like a build log: %w", ErrInvalidMaterialType) + } + return nil, err + } + + m, err := uploadAndCraft(ctx, i.input, i.backend, filePath, i.logger) + if err != nil { + return nil, err + } + + i.injectAnnotations(m, report) + + return craftResult(m, nil) +} + +func (i *BuildLogCrafter) injectAnnotations(m *api.Attestation_Material, report *buildlog.Report) { + if m.Annotations == nil { + m.Annotations = make(map[string]string) + } + m.Annotations[AnnotationToolNameKey] = report.Tool.Name + if report.Tool.Version != "" { + m.Annotations[AnnotationToolVersionKey] = report.Tool.Version + } + m.Annotations[AnnotationBuildLogLogsCount] = strconv.Itoa(len(report.Logs)) + m.Annotations[AnnotationBuildLogWarningsCount] = strconv.Itoa(report.Summary.Warnings) + m.Annotations[AnnotationBuildLogErrorsCount] = strconv.Itoa(report.Summary.Errors) +} diff --git a/pkg/attestation/crafter/materials/build_log_test.go b/pkg/attestation/crafter/materials/build_log_test.go new file mode 100644 index 000000000..40c1f645a --- /dev/null +++ b/pkg/attestation/crafter/materials/build_log_test.go @@ -0,0 +1,185 @@ +// +// Copyright 2026 The Chainloop 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 materials_test + +import ( + "bytes" + "compress/gzip" + "context" + "os" + "path/filepath" + "testing" + + contractAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials" + "github.com/chainloop-dev/chainloop/pkg/casclient" + mUploader "github.com/chainloop-dev/chainloop/pkg/casclient/mocks" + "github.com/rs/zerolog" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestNewBuildLogCrafter(t *testing.T) { + testCases := []struct { + name string + input *contractAPI.CraftingSchema_Material + wantErr bool + }{ + { + name: "happy path", + input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_BUILD_LOG}, + }, + { + name: "wrong type", + input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_SARIF}, + wantErr: true, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := materials.NewBuildLogCrafter(tc.input, nil, nil) + if tc.wantErr { + assert.Error(t, err) + return + } + assert.NoError(t, err) + }) + } +} + +// writeBuildLogZip zips the binary logs of two solutions, the form used when a +// build produces one binary log per solution. +func writeBuildLogZip(t *testing.T) string { + t.Helper() + entries := map[string][]byte{} + for _, n := range []string{"sample.binlog", "lib.binlog"} { + content, err := os.ReadFile(filepath.Join("./testdata/buildlog", n)) + require.NoError(t, err) + entries["logs/"+n] = content + } + p := filepath.Join(t.TempDir(), "logs.zip") + writeZip(t, p, entries) + return p +} + +// writeOldBinlog writes a binary log header in a format version that predates +// length-prefixed records. +func writeOldBinlog(t *testing.T) string { + t.Helper() + var buf bytes.Buffer + zw := gzip.NewWriter(&buf) + _, err := zw.Write([]byte{14, 0, 0, 0, 14, 0, 0, 0}) + require.NoError(t, err) + require.NoError(t, zw.Close()) + p := filepath.Join(t.TempDir(), "old.binlog") + require.NoError(t, os.WriteFile(p, buf.Bytes(), 0o600)) + return p +} + +func TestBuildLogCrafter_Craft(t *testing.T) { + testCases := []struct { + name string + filePath func(t *testing.T) string + wantErr string + annotations map[string]string + }{ + { + name: "invalid path", + filePath: func(*testing.T) string { return "./testdata/non-existing.binlog" }, + wantErr: "no such file or directory", + }, + { + name: "wrong content", + filePath: func(*testing.T) string { return "./testdata/sbom-spdx.json" }, + wantErr: "does not look like a build log", + }, + { + name: "unsupported binary log version", + filePath: writeOldBinlog, + wantErr: "unsupported MSBuild binary log version 14", + }, + { + name: "msbuild binary log", + filePath: func(*testing.T) string { return "./testdata/buildlog/sample.binlog" }, + annotations: map[string]string{ + "chainloop.material.tool.name": "msbuild", + "chainloop.material.build_log.logs.count": "1", + "chainloop.material.build_log.warnings.count": "4", + "chainloop.material.build_log.errors.count": "0", + }, + }, + { + name: "msbuild text log", + filePath: func(*testing.T) string { return "./testdata/buildlog/msbuild.txt" }, + annotations: map[string]string{ + "chainloop.material.tool.name": "msbuild", + "chainloop.material.build_log.warnings.count": "4", + }, + }, + { + name: "visual c++ build log", + filePath: func(*testing.T) string { return "./testdata/buildlog/vcbuild.htm" }, + annotations: map[string]string{ + "chainloop.material.tool.name": "vcbuild", + "chainloop.material.build_log.warnings.count": "4", + }, + }, + { + name: "archive of binary logs", + filePath: writeBuildLogZip, + annotations: map[string]string{ + "chainloop.material.tool.name": "msbuild", + "chainloop.material.build_log.logs.count": "2", + "chainloop.material.build_log.warnings.count": "4", + }, + }, + } + + schema := &contractAPI.CraftingSchema_Material{ + Name: "build-log", + Type: contractAPI.CraftingSchema_Material_BUILD_LOG, + } + + l := zerolog.Nop() + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + uploader := mUploader.NewUploader(t) + if tc.wantErr == "" { + uploader.On("Upload", context.TODO(), mock.Anything, mock.Anything, mock.Anything). + Return(&casclient.UpDownStatus{}, nil) + } + + backend := &casclient.CASBackend{Uploader: uploader} + crafter, err := materials.NewBuildLogCrafter(schema, backend, &l) + require.NoError(t, err) + + got, err := craftedMaterial(crafter.Craft(context.TODO(), tc.filePath(t))) + if tc.wantErr != "" { + assert.ErrorContains(t, err, tc.wantErr) + return + } + + require.NoError(t, err) + assert.Equal(t, contractAPI.CraftingSchema_Material_BUILD_LOG.String(), got.MaterialType.String()) + assert.True(t, got.UploadedToCas) + for k, v := range tc.annotations { + assert.Equal(t, v, got.Annotations[k], k) + } + }) + } +} diff --git a/pkg/attestation/crafter/materials/buildlog/binlog.go b/pkg/attestation/crafter/materials/buildlog/binlog.go new file mode 100644 index 000000000..f5290aed4 --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/binlog.go @@ -0,0 +1,453 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +import ( + "bufio" + "bytes" + "compress/gzip" + "encoding/binary" + "errors" + "fmt" + "io" + "regexp" + "strconv" +) + +// An MSBuild binary log (-bl) is a gzip stream: a header of two little-endian +// int32 (the file format version and the minimum reader version), then a +// sequence of records, each a 7-bit encoded kind followed by a 7-bit encoded +// length and the record body. Strings are written once as String records and +// referenced by index afterwards. +// +// The layout is MSBuild's BuildEventArgsWriter/BuildEventArgsReader. Only the +// records that carry diagnostics are decoded; every other record is skipped by +// its length, which is why logs older than the length-prefixed format are not +// supported. +const ( + recordEndOfFile = 0 + recordBuildFinished = 2 + recordProjectStarted = 3 + recordError = 9 + recordWarning = 10 + recordString = 24 + + // minBinlogVersion is the first format version whose records are all + // length-prefixed (MSBuild's ForwardCompatibilityMinimalVersion). + minBinlogVersion = 18 + // knownBinlogVersion is the newest format version whose diagnostic record + // layout this reader was written against. Newer versions may append fields + // to a record, which are skipped; older ones must match it exactly. + knownBinlogVersion = 25 + + // stringStartIndex is the index MSBuild assigns to the first String record; + // 0 is null, 1 is the empty string and the rest are reserved. + stringStartIndex = 10 +) + +// BuildEventArgsFieldFlags: which optional fields a record carries. +const ( + flagBuildEventContext = 1 << 0 + flagHelpKeyword = 1 << 1 + flagMessage = 1 << 2 + flagSenderName = 1 << 3 + flagThreadID = 1 << 4 + flagTimestamp = 1 << 5 + flagSubcategory = 1 << 6 + flagCode = 1 << 7 + flagFile = 1 << 8 + flagProjectFile = 1 << 9 + flagLineNumber = 1 << 10 + flagColumnNumber = 1 << 11 + flagEndLineNumber = 1 << 12 + flagEndColumnNumber = 1 << 13 + flagArguments = 1 << 14 + flagImportance = 1 << 15 + flagExtended = 1 << 16 +) + +// ErrUnsupportedBinlog is returned for a binary log whose format version this +// reader cannot decode. +var ErrUnsupportedBinlog = errors.New("unsupported MSBuild binary log version") + +// maxBinlogRecord bounds a single record body. Diagnostic records are a few +// hundred bytes; the bound only stops a corrupt length from allocating +// gigabytes. +const maxBinlogRecord = 64 << 20 + +// formatArgRe matches a .NET composite-format placeholder such as {0} or {1,-8:N2}. +var formatArgRe = regexp.MustCompile(`\{(\d+)(?:,[^}:]*)?(?::[^}]*)?\}`) + +// isBinlog reports whether peeked bytes are the start of an MSBuild binary log: +// a gzip stream whose first decompressed int32 is a plausible format version. +// A tar.gz, the other gzip content a material may hold, starts with an entry +// name instead. +func isBinlog(peek []byte) bool { + if !bytes.HasPrefix(peek, []byte{0x1f, 0x8b}) { + return false + } + zr, err := gzip.NewReader(bytes.NewReader(peek)) + if err != nil { + return false + } + var version int32 + if err := binary.Read(zr, binary.LittleEndian, &version); err != nil { + return false + } + return version > 0 && version < 1<<16 +} + +type binlogReader struct { + r *bufio.Reader + version int32 + strings []string +} + +// parseBinlog reads the diagnostics of an MSBuild binary log. +func parseBinlog(r io.Reader) (*parsedLog, error) { + zr, err := gzip.NewReader(r) + if err != nil { + return nil, fmt.Errorf("opening binary log: %w", err) + } + defer zr.Close() + + br := &binlogReader{r: bufio.NewReader(zr)} + var header [2]int32 + if err := binary.Read(br.r, binary.LittleEndian, &header); err != nil { + return nil, fmt.Errorf("reading binary log header: %w", err) + } + br.version = header[0] + if br.version < minBinlogVersion { + return nil, fmt.Errorf("%w %d: produce the log with a newer MSBuild (format %d or later)", ErrUnsupportedBinlog, br.version, minBinlogVersion) + } + if header[1] > knownBinlogVersion { + return nil, fmt.Errorf("%w %d: it requires a reader for format %d or later", ErrUnsupportedBinlog, br.version, header[1]) + } + + log := &parsedLog{format: FormatMSBuildBinlog, tool: Tool{Name: ToolMSBuild}} + for n := 0; ; n++ { + kind, err := br.varint(br.r) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + if kind == recordEndOfFile { + return log, nil + } + length, err := br.varint(br.r) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + if length < 0 { + return nil, fmt.Errorf("binary log record %d has an invalid length %d", n, length) + } + // Only records read into memory are bounded; the rest are skipped + // whatever their size (an embedded project-imports archive can be large). + if kind != recordString && kind != recordWarning && kind != recordError && + kind != recordProjectStarted && kind != recordBuildFinished { + if _, err := br.r.Discard(length); err != nil { + return nil, fmt.Errorf("binary log is truncated: skipping record %d: %w", n, err) + } + continue + } + if length > maxBinlogRecord { + return nil, fmt.Errorf("binary log record %d has an invalid length %d", n, length) + } + + switch kind { + case recordBuildFinished: + body, err := br.body(length) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + if log.result, err = br.buildResult(body); err != nil { + return nil, fmt.Errorf("decoding binary log record %d: %w", n, err) + } + case recordProjectStarted: + body, err := br.body(length) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + if log.solution == "" { + // The first project started is the one the build was run for. + // Only a label, so a record this reader cannot decode is no error. + log.solution = br.projectFile(body) + } + case recordString: + // A String record's length is the string's own length prefix. + body, err := br.body(length) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + br.strings = append(br.strings, string(body)) + case recordWarning, recordError: + body, err := br.body(length) + if err != nil { + return nil, fmt.Errorf("binary log is truncated: reading record %d: %w", n, err) + } + d, err := br.diagnostic(body) + if err != nil { + return nil, fmt.Errorf("decoding binary log record %d: %w", n, err) + } + if kind == recordError { + d.Severity = SeverityError + } else { + d.Severity = SeverityWarning + } + log.diagnostics = append(log.diagnostics, d) + } + } +} + +func (br *binlogReader) body(length int) ([]byte, error) { + body := make([]byte, length) + if _, err := io.ReadFull(br.r, body); err != nil { + return nil, err + } + return body, nil +} + +// diagnostic decodes a Warning or Error record body: the shared event fields +// (driven by the flags), then the diagnostic fields that are always present. +func (br *binlogReader) diagnostic(body []byte) (Diagnostic, error) { + r := bytes.NewReader(body) + message, arguments, err := br.eventFields(r) + if err != nil { + return Diagnostic{}, err + } + d, err := br.diagnosticFields(r) + if err != nil { + return Diagnostic{}, err + } + + // Unread bytes mean the record does not have the layout this reader + // expects. That is fatal for the versions it knows, where it would mean a + // misread; newer versions may append fields, which are safe to skip. + if r.Len() != 0 && br.version <= knownBinlogVersion { + return Diagnostic{}, fmt.Errorf("record has %d unread bytes", r.Len()) + } + + d.Message = formatMessage(message, arguments) + return d, nil +} + +// eventFields reads the optional fields every build event may carry, in the +// order of BuildEventArgsReader.ReadBuildEventArgsFields, and returns the +// message with its format arguments. +func (br *binlogReader) eventFields(r *bytes.Reader) (string, []string, error) { + flags, err := br.varint(r) + if err != nil { + return "", nil, err + } + + var message string + var arguments []string + skipVarint := func() error { return br.skipVarints(r, 1) } + skipString := func() error { _, err := br.str(r); return err } + + fields := []struct { + flag int + read func() error + }{ + {flagMessage, func() (err error) { message, err = br.str(r); return err }}, + // nodeId, projectContextId, targetId, taskId, submissionId, + // projectInstanceId, evaluationId + {flagBuildEventContext, func() error { return br.skipVarints(r, 7) }}, + {flagThreadID, skipVarint}, + {flagHelpKeyword, skipString}, + {flagSenderName, skipString}, + // ticks, then the DateTimeKind + {flagTimestamp, func() error { + if _, err := r.Seek(8, io.SeekCurrent); err != nil { + return err + } + return skipVarint() + }}, + // type, metadata (an index into a name-value list), data + {flagExtended, func() error { + if err := skipString(); err != nil { + return err + } + if err := skipVarint(); err != nil { + return err + } + return skipString() + }}, + // Diagnostic events never set these, but the reader honors them. + {flagSubcategory, skipString}, + {flagCode, skipString}, + {flagFile, skipString}, + {flagProjectFile, skipString}, + {flagLineNumber, skipVarint}, + {flagColumnNumber, skipVarint}, + {flagEndLineNumber, skipVarint}, + {flagEndColumnNumber, skipVarint}, + {flagArguments, func() (err error) { arguments, err = br.arguments(r); return err }}, + {flagImportance, skipVarint}, + } + for _, f := range fields { + if flags&f.flag == 0 { + continue + } + if err := f.read(); err != nil { + return "", nil, err + } + } + return message, arguments, nil +} + +func (br *binlogReader) arguments(r *bytes.Reader) ([]string, error) { + count, err := br.varint(r) + if err != nil { + return nil, err + } + if count < 0 || count > r.Len() { + return nil, fmt.Errorf("invalid argument count %d", count) + } + arguments := make([]string, 0, count) + for range count { + a, err := br.str(r) + if err != nil { + return nil, err + } + arguments = append(arguments, a) + } + return arguments, nil +} + +// diagnosticFields reads what BuildEventArgsReader.ReadDiagnosticFields does: +// subcategory, code, file, project file, line, column, end line, end column. +func (br *binlogReader) diagnosticFields(r *bytes.Reader) (Diagnostic, error) { + var d Diagnostic + var err error + if _, err = br.str(r); err != nil { + return d, err + } + if d.Code, err = br.str(r); err != nil { + return d, err + } + if d.File, err = br.str(r); err != nil { + return d, err + } + if d.Project, err = br.str(r); err != nil { + return d, err + } + if d.Line, err = br.varint(r); err != nil { + return d, err + } + if d.Column, err = br.varint(r); err != nil { + return d, err + } + return d, br.skipVarints(r, 2) +} + +// buildResult reads a BuildFinished record body: the event fields, then +// whether the build succeeded. +func (br *binlogReader) buildResult(body []byte) (string, error) { + r := bytes.NewReader(body) + if _, _, err := br.eventFields(r); err != nil { + return "", err + } + succeeded, err := r.ReadByte() + if err != nil { + return "", err + } + if succeeded != 0 { + return ResultSucceeded, nil + } + return ResultFailed, nil +} + +// projectFile returns the project file of a ProjectStarted record body: the +// event fields, an optional parent build context, then the project file. It +// returns the empty string when the body cannot be decoded. +func (br *binlogReader) projectFile(body []byte) string { + r := bytes.NewReader(body) + if _, _, err := br.eventFields(r); err != nil { + return "" + } + hasParent, err := r.ReadByte() + if err != nil { + return "" + } + if hasParent != 0 { + if err := br.skipVarints(r, 7); err != nil { + return "" + } + } + project, err := br.str(r) + if err != nil { + return "" + } + return project +} + +// formatMessage expands a .NET composite format string with its arguments, +// which is how MSBuild stores lazily formatted task messages. +func formatMessage(format string, arguments []string) string { + if len(arguments) == 0 { + return format + } + return formatArgRe.ReplaceAllStringFunc(format, func(m string) string { + i, err := strconv.Atoi(formatArgRe.FindStringSubmatch(m)[1]) + if err != nil || i >= len(arguments) { + return m + } + return arguments[i] + }) +} + +// str reads a deduplicated string reference. +func (br *binlogReader) str(r io.ByteReader) (string, error) { + i, err := br.varint(r) + if err != nil { + return "", err + } + switch { + case i == 0, i == 1: + return "", nil + case i < stringStartIndex || i-stringStartIndex >= len(br.strings): + return "", fmt.Errorf("invalid string reference %d", i) + } + return br.strings[i-stringStartIndex], nil +} + +func (br *binlogReader) skipVarints(r io.ByteReader, n int) error { + for range n { + if _, err := br.varint(r); err != nil { + return err + } + } + return nil +} + +// varint reads a .NET 7-bit encoded int32. +func (br *binlogReader) varint(r io.ByteReader) (int, error) { + var v uint32 + for shift := 0; shift < 35; shift += 7 { + b, err := r.ReadByte() + if err != nil { + if errors.Is(err, io.EOF) && shift > 0 { + err = io.ErrUnexpectedEOF + } + return 0, err + } + v |= uint32(b&0x7f) << shift + if b&0x80 == 0 { + return int(int32(v)), nil + } + } + return 0, errors.New("invalid 7-bit encoded integer") +} diff --git a/pkg/attestation/crafter/materials/buildlog/binlog_test.go b/pkg/attestation/crafter/materials/buildlog/binlog_test.go new file mode 100644 index 000000000..5c1bf9325 --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/binlog_test.go @@ -0,0 +1,240 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +import ( + "bytes" + "compress/gzip" + "encoding/binary" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// binlogWriter builds minimal binary logs for the record shapes the synthetic +// fixtures do not contain, following MSBuild's BuildEventArgsWriter layout. +type binlogWriter struct { + out bytes.Buffer + strings map[string]int +} + +func newBinlogWriter(version, minReaderVersion int32) *binlogWriter { + w := &binlogWriter{strings: map[string]int{}} + _ = binary.Write(&w.out, binary.LittleEndian, version) + _ = binary.Write(&w.out, binary.LittleEndian, minReaderVersion) + return w +} + +func putVarint(buf *bytes.Buffer, v int) { + u := uint32(int32(v)) + for u >= 0x80 { + buf.WriteByte(byte(u) | 0x80) + u >>= 7 + } + buf.WriteByte(byte(u)) +} + +// str returns the deduplicated index of s, writing its String record first +// when it is new. Index 0 is null, as in MSBuild. +func (w *binlogWriter) str(s *string) int { + if s == nil { + return 0 + } + if i, ok := w.strings[*s]; ok { + return i + } + putVarint(&w.out, recordString) + putVarint(&w.out, len(*s)) + w.out.WriteString(*s) + i := len(w.strings) + stringStartIndex + w.strings[*s] = i + return i +} + +type diagnosticRecord struct { + kind int + message string + arguments []string + extendedType string + code string + file string + project string + line, column int +} + +func ptr(s string) *string { return &s } + +const ( + codeVariadicClass = "C4840" + codeUnresolvedRef = "MSB3245" + sourceFile = `C:\src\a.cpp` + sourceProject = `C:\src\a.vcxproj` +) + +func (w *binlogWriter) diagnostic(d diagnosticRecord) { + // String records go out first, as the writer flushes them before the + // record that references them. + msg := w.str(ptr(d.message)) + sender := w.str(ptr("Task")) + var extType, extData int + if d.extendedType != "" { + extType = w.str(ptr(d.extendedType)) + extData = w.str(ptr(`{"ruleId":"` + d.code + `"}`)) + } + args := make([]int, 0, len(d.arguments)) + for _, a := range d.arguments { + args = append(args, w.str(ptr(a))) + } + code, file, project := w.str(ptr(d.code)), w.str(ptr(d.file)), w.str(ptr(d.project)) + + var body bytes.Buffer + flags := flagMessage | flagBuildEventContext | flagSenderName | flagTimestamp + if d.extendedType != "" { + flags |= flagExtended + } + if len(args) > 0 { + flags |= flagArguments + } + putVarint(&body, flags) + putVarint(&body, msg) + for _, v := range []int{1, 2, 3, 4, 5, 6, 7} { + putVarint(&body, v) + } + putVarint(&body, sender) + _ = binary.Write(&body, binary.LittleEndian, int64(638000000000000000)) + putVarint(&body, 1) + if d.extendedType != "" { + putVarint(&body, extType) + putVarint(&body, 0) // no metadata + putVarint(&body, extData) + } + if len(args) > 0 { + putVarint(&body, len(args)) + for _, a := range args { + putVarint(&body, a) + } + } + putVarint(&body, 0) // subcategory + putVarint(&body, code) + putVarint(&body, file) + putVarint(&body, project) + putVarint(&body, d.line) + putVarint(&body, d.column) + putVarint(&body, 0) + putVarint(&body, 0) + + putVarint(&w.out, d.kind) + putVarint(&w.out, body.Len()) + w.out.Write(body.Bytes()) +} + +func (w *binlogWriter) bytes(t *testing.T) []byte { + t.Helper() + putVarint(&w.out, recordEndOfFile) + return gzipOf(t, w.out.Bytes()) +} + +func gzipOf(t *testing.T, raw []byte) []byte { + t.Helper() + var gz bytes.Buffer + zw := gzip.NewWriter(&gz) + _, err := zw.Write(raw) + require.NoError(t, err) + require.NoError(t, zw.Close()) + return gz.Bytes() +} + +func TestParseBinlogRecordShapes(t *testing.T) { + w := newBinlogWriter(25, 18) + // MSVC reports its diagnostics as extended events carrying a SARIF result. + w.diagnostic(diagnosticRecord{ + kind: recordWarning, message: "non-portable use of class as an argument to a variadic function", + extendedType: "SarifBuildEvent", code: codeVariadicClass, file: sourceFile, project: sourceProject, line: 10, column: 4, + }) + // Task warnings may keep their message as a format string plus arguments. + w.diagnostic(diagnosticRecord{ + kind: recordWarning, message: "Reference {0} could not be resolved: {1}", arguments: []string{"Foo", "not found"}, + code: codeUnresolvedRef, file: `C:\sdk\Microsoft.Common.targets`, project: `C:\src\b.csproj`, line: 2400, column: 5, + }) + w.diagnostic(diagnosticRecord{ + kind: recordError, message: msgUndeclared, + code: codeUndeclared, file: sourceFile, project: sourceProject, line: 41, column: 7, + }) + + report, err := Parse(w.bytes(t)) + require.NoError(t, err) + + assert.Equal(t, Summary{ + Warnings: 2, + Errors: 1, + ByCode: map[string]int{codeUndeclared: 1, codeVariadicClass: 1, codeUnresolvedRef: 1}, + }, report.Summary) + assert.Equal(t, inLogs([]Diagnostic{ + {Severity: SeverityError, Code: codeUndeclared, Message: msgUndeclared, File: sourceFile, Line: 41, Column: 7, Project: sourceProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: codeVariadicClass, Message: "non-portable use of class as an argument to a variadic function", File: sourceFile, Line: 10, Column: 4, Project: sourceProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: codeUnresolvedRef, Message: "Reference Foo could not be resolved: not found", File: `C:\sdk\Microsoft.Common.targets`, Line: 2400, Column: 5, Project: `C:\src\b.csproj`, Occurrences: 1}, + }, 0), report.Diagnostics) +} + +func TestParseBinlogUnsupportedVersions(t *testing.T) { + testCases := []struct { + name string + version, minRead int32 + }{ + {name: "older than length-prefixed records", version: 17, minRead: 17}, + {name: "requires a newer reader", version: 40, minRead: 40}, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := Parse(newBinlogWriter(tc.version, tc.minRead).bytes(t)) + assert.ErrorIs(t, err, ErrUnsupportedBinlog) + }) + } +} + +func TestParseBinlogTruncatedFails(t *testing.T) { + w := newBinlogWriter(25, 18) + w.diagnostic(diagnosticRecord{kind: recordWarning, message: "m", code: codeUnusedLocal, file: "a.cs", project: csharpProject, line: 1, column: 1}) + complete := w.out.Bytes() + + // A log cut short, whether inside a record or before the end-of-file + // record, is incomplete evidence: the parse must fail rather than report + // only the warnings it got to. + for name, raw := range map[string][]byte{ + "inside a record": complete[:len(complete)-3], + "missing end-of-file": complete, + "inside the file header": complete[:5], + } { + t.Run(name, func(t *testing.T) { + _, err := Parse(gzipOf(t, raw)) + assert.Error(t, err) + assert.NotErrorIs(t, err, ErrNoBuildLog) + }) + } +} + +func TestParseBinlogMalformedRecordFails(t *testing.T) { + w := newBinlogWriter(25, 18) + // A record whose declared length leaves fields unread means the layout is + // not the one this reader knows. + putVarint(&w.out, recordWarning) + putVarint(&w.out, 40) + w.out.Write(make([]byte, 40)) + + _, err := Parse(w.bytes(t)) + assert.Error(t, err) +} diff --git a/pkg/attestation/crafter/materials/buildlog/buildlog.go b/pkg/attestation/crafter/materials/buildlog/buildlog.go new file mode 100644 index 000000000..a0e84b8f6 --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/buildlog.go @@ -0,0 +1,463 @@ +// +// Copyright 2026 The Chainloop 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 buildlog extracts the compiler and build diagnostics (warnings and +// errors) from the logs Microsoft build tools write, and normalizes them into +// one Chainloop-defined structure that policies evaluate: +// +// - MSBuild binary logs (-bl), format version 18 or later +// - MSBuild text logs (console or file logger, any verbosity that keeps +// warnings), possibly several invocations appended to one file +// - Visual C++ build logs (BuildLog.htm, written by vcbuild), UTF-16 HTML +// - a zip or tar archive holding any mix of the above, in any folders, for +// builds that produce one log per solution or per project. Archives inside +// it (such as the binary logs zipped per solution) are read one level deep; +// entries that are not logs are skipped. +// +// The original log is what a material stores; this projection is recomputed +// from it whenever policies run. It keeps no raw log text: diagnostic-verbosity +// logs reach hundreds of megabytes, and the policy engine holds its whole +// input in memory. +package buildlog + +import ( + "bufio" + "bytes" + "errors" + "fmt" + "io" + "os" + "slices" + "sort" + "strings" + + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/archiveio" +) + +const ( + FormatMSBuildBinlog = "msbuild-binlog" + FormatMSBuildText = "msbuild-text" + FormatVCBuildHTML = "vcbuild-html" + + ToolMSBuild = "msbuild" + ToolVCBuild = "vcbuild" + + SeverityWarning = "warning" + SeverityError = "error" + + ResultSucceeded = "succeeded" + ResultFailed = "failed" +) + +// ErrNoBuildLog is returned when a material holds no recognizable build log: a +// single file in another format, or an archive with no build log entry. +var ErrNoBuildLog = errors.New("no build log found") + +// Tool identifies the build tool that wrote a log. +type Tool struct { + Name string `json:"name"` + Version string `json:"version,omitempty"` +} + +// Diagnostic is one distinct warning or error. Repeats of the same diagnostic +// (a header warning reported once per translation unit including it, or a +// project built by several solutions) are folded into one entry: Occurrences +// counts them, and Logs lists the indexes in Report.Logs of the logs they were +// seen in, so a folded diagnostic can still be traced to each build. +type Diagnostic struct { + Severity string `json:"severity"` + Code string `json:"code,omitempty"` + Message string `json:"message"` + File string `json:"file,omitempty"` + Line int `json:"line,omitempty"` + Column int `json:"column,omitempty"` + Project string `json:"project,omitempty"` + Occurrences int `json:"occurrences"` + Logs []int `json:"logs"` +} + +// Summary counts distinct diagnostics. +type Summary struct { + Warnings int `json:"warnings"` + Errors int `json:"errors"` + ByCode map[string]int `json:"by_code"` +} + +// Counts are the totals a log states about itself, such as MSBuild's +// "N Warning(s)" summary. They let a policy check that every diagnostic the +// tool reported was parsed. +type Counts struct { + Warnings int `json:"warnings"` + Errors int `json:"errors"` +} + +// Log describes one build recorded in the material: a binary log, one MSBuild +// run of a text log (a text log can hold several, one per solution, which then +// share a Source), or a Visual C++ build log. Source names the archive entry it +// was read from, and is empty for a single-file material. Solution is the +// project the build was run for (usually a .sln), when the log records it. +// +// Result is how the build ended, empty when the log does not say (a log cut +// short). Parsed counts the occurrences read from the log, comparable with the +// totals it Reported about itself: fewer parsed than reported means warnings +// the parser could not read. +type Log struct { + Source string `json:"source,omitempty"` + Solution string `json:"solution,omitempty"` + Format string `json:"format"` + Tool Tool `json:"tool"` + Result string `json:"result,omitempty"` + Summary Summary `json:"summary"` + Parsed Counts `json:"parsed"` + Reported *Counts `json:"reported,omitempty"` +} + +// Report is the policy projection of a BUILD_LOG material. Skipped lists the +// archive entries that were not read as logs, so a policy can tell a log that +// could not be read from a file that is not a log. +type Report struct { + Tool Tool `json:"tool"` + Logs []Log `json:"logs"` + Skipped []string `json:"skipped,omitempty"` + Summary Summary `json:"summary"` + Diagnostics []Diagnostic `json:"diagnostics"` +} + +// parsedLog is one log as read, before its diagnostics are folded. +type parsedLog struct { + source string + solution string + result string + format string + tool Tool + diagnostics []Diagnostic + reported *Counts +} + +// archiveLimits bounds archive expansion. Logs are streamed and only their +// diagnostics are kept, so the bound is on the work done, not on memory. +func archiveLimits() archiveio.Limits { + return archiveio.Limits{MaxEntries: 1024, MaxTotalSize: 4 << 30} +} + +// Parse projects a BUILD_LOG material held in memory. +func Parse(data []byte) (*Report, error) { + if isBinlog(data) { + return parseSingle(bytes.NewReader(data)) + } + if format := archiveio.DetectBytes(data); format != archiveio.None { + return parseArchive(func(yield func(string, io.Reader) error) error { + return archiveio.WalkBytes(data, format, archiveLimits(), yield) + }) + } + return parseSingle(bytes.NewReader(data)) +} + +// ParseFile projects a BUILD_LOG material on disk, streaming it. +func ParseFile(p string) (*Report, error) { + f, err := os.Open(p) + if err != nil { + return nil, fmt.Errorf("can't open the file: %w", err) + } + defer f.Close() + + br := bufio.NewReader(f) + peek, _ := br.Peek(512) + if isBinlog(peek) { + return parseSingle(br) + } + format, err := archiveio.DetectFile(p) + if err != nil { + return nil, fmt.Errorf("inspecting %q: %w", p, err) + } + if format != archiveio.None { + return parseArchive(func(yield func(string, io.Reader) error) error { + return archiveio.WalkPath(p, format, archiveLimits(), yield) + }) + } + return parseSingle(br) +} + +func parseSingle(r io.Reader) (*Report, error) { + logs, err := parseLog(r) + if err != nil { + return nil, err + } + if len(logs) == 0 { + return nil, ErrNoBuildLog + } + return newReport(logs), nil +} + +// walker yields the entries of an archive. +type walker func(yield func(name string, r io.Reader) error) error + +// maxNestedArchive bounds an archive inside the material's archive, which has +// to be read into memory to be opened. +const maxNestedArchive = 512 << 20 + +// nestedArchiveExts are the entry names opened as archives. Packages such as +// .nupkg or .vsix are zips too, but not bundles of logs, so they are skipped +// like any other entry that is not a log. +var nestedArchiveExts = []string{".zip", ".tar", ".tar.gz", ".tgz"} + +func isArchiveName(name string) bool { + lower := strings.ToLower(name) + for _, ext := range nestedArchiveExts { + if strings.HasSuffix(lower, ext) { + return true + } + } + return false +} + +// parseArchive parses every archive entry that is a build log, including those +// of an archive one level down: a bundle of everything a release produced may +// hold the binary logs already zipped per solution. Other entries are skipped, +// but at least one log must be found. +func parseArchive(walk walker) (*Report, error) { + var c collected + if err := c.walk(walk, "", 0); err != nil { + return nil, fmt.Errorf("reading build log archive: %w", err) + } + if len(c.logs) == 0 { + return nil, ErrNoBuildLog + } + // Deterministic order, so the projection does not depend on archive order. + // Stable, so the runs of one text log keep their order. + sort.SliceStable(c.logs, func(i, j int) bool { return c.logs[i].source < c.logs[j].source }) + sort.Strings(c.skipped) + report := newReport(c.logs) + report.Skipped = c.skipped + return report, nil +} + +// collected gathers the logs of an archive and the entries that are not logs. +type collected struct { + logs []*parsedLog + skipped []string +} + +// projectImportsSuffix names the archive of project files that +// -bl:ProjectImports=ZipFile writes next to a binary log. It never holds logs. +const projectImportsSuffix = ".projectimports.zip" + +// walk adds the build logs of an archive, naming each by its path from the +// material's archive. depth counts the archives the walk is in beyond the +// material's own. +func (c *collected) walk(walk walker, prefix string, depth int) error { + return walk(func(name string, r io.Reader) error { + source := prefix + name + switch { + case strings.HasSuffix(strings.ToLower(name), projectImportsSuffix): + c.skipped = append(c.skipped, source) + return nil + case isArchiveName(name): + return c.nested(r, source, depth) + } + return c.add(r, source) + }) +} + +// add parses r and adds its logs, or records it as skipped when it holds none. +func (c *collected) add(r io.Reader, source string) error { + parsed, err := parseLog(r) + if err != nil { + return fmt.Errorf("parsing %q: %w", source, err) + } + if len(parsed) == 0 { + c.skipped = append(c.skipped, source) + return nil + } + for _, l := range parsed { + l.source = source + } + c.logs = append(c.logs, parsed...) + return nil +} + +// nested opens an archive found inside the material's archive. Skipping it +// would drop its logs from the evaluation without a trace, so anything that +// cannot be read fails instead. +func (c *collected) nested(r io.Reader, source string, depth int) error { + if depth > 0 { + return fmt.Errorf("archive %q is inside another archive within the material: move its logs up one level", source) + } + data, err := io.ReadAll(io.LimitReader(r, maxNestedArchive+1)) + if err != nil { + return fmt.Errorf("reading %q: %w", source, err) + } + if len(data) > maxNestedArchive { + return fmt.Errorf("archive %q is larger than %d MiB: add its logs to the material's archive directly", source, maxNestedArchive>>20) + } + + format := archiveio.DetectBytes(data) + if format == archiveio.None || isBinlog(data) { + // Named like an archive but is not one; it may still be a log. + return c.add(bytes.NewReader(data), source) + } + return c.walk(func(yield func(string, io.Reader) error) error { + return archiveio.WalkBytes(data, format, archiveLimits(), yield) + }, source+"/", depth+1) +} + +// sniffSize is how much of a file is read to recognize it. A CI console capture +// can hold the output of earlier steps before MSBuild starts. +const sniffSize = 64 << 10 + +// parseLog parses the builds recorded in one log file, or returns none when r +// is not a build log. +func parseLog(r io.Reader) ([]*parsedLog, error) { + br := bufio.NewReaderSize(r, sniffSize) + peek, err := br.Peek(sniffSize) + if err != nil && !errors.Is(err, io.EOF) && !errors.Is(err, bufio.ErrBufferFull) { + return nil, err + } + if isBinlog(peek) { + log, err := parseBinlog(br) + if err != nil { + return nil, err + } + return []*parsedLog{log}, nil + } + if format := sniffText(peek); format != "" { + return parseText(br, format) + } + return nil, nil +} + +// diagnosticKey identifies a diagnostic for folding repeats. Paths are compared +// without regard to case, as the Windows tools that write these logs spell the +// same file differently from one report to the next. +type diagnosticKey struct { + severity, code, message, file, project string + line, column int +} + +func keyOf(d Diagnostic) diagnosticKey { + return diagnosticKey{ + severity: d.Severity, + code: d.Code, + message: d.Message, + file: strings.ToLower(d.File), + project: strings.ToLower(d.Project), + line: d.Line, + column: d.Column, + } +} + +// fold merges repeated diagnostics, keeping the first spelling of each, and +// sorts the result. +func fold(diagnostics []Diagnostic) []Diagnostic { + index := make(map[diagnosticKey]int, len(diagnostics)) + out := make([]Diagnostic, 0, len(diagnostics)) + for _, d := range diagnostics { + k := keyOf(d) + if i, ok := index[k]; ok { + out[i].Occurrences += max(d.Occurrences, 1) + out[i].Logs = mergeIndexes(out[i].Logs, d.Logs) + continue + } + d.Occurrences = max(d.Occurrences, 1) + index[k] = len(out) + out = append(out, d) + } + sort.Slice(out, func(i, j int) bool { + a, b := out[i], out[j] + if a.Code != b.Code { + return a.Code < b.Code + } + if a.File != b.File { + return a.File < b.File + } + if a.Line != b.Line { + return a.Line < b.Line + } + if a.Column != b.Column { + return a.Column < b.Column + } + if a.Project != b.Project { + return a.Project < b.Project + } + if a.Severity != b.Severity { + return a.Severity < b.Severity + } + return a.Message < b.Message + }) + return out +} + +// occurrences counts the diagnostics read from a log, before folding. +func occurrences(diagnostics []Diagnostic) Counts { + var c Counts + for _, d := range diagnostics { + if d.Severity == SeverityError { + c.Errors += max(d.Occurrences, 1) + } else { + c.Warnings += max(d.Occurrences, 1) + } + } + return c +} + +// mergeIndexes returns the sorted union of two index lists. +func mergeIndexes(a, b []int) []int { + out := append(slices.Clone(a), b...) + slices.Sort(out) + return slices.Compact(out) +} + +func summarize(diagnostics []Diagnostic) Summary { + s := Summary{ByCode: map[string]int{}} + for _, d := range diagnostics { + if d.Severity == SeverityError { + s.Errors++ + } else { + s.Warnings++ + } + if d.Code != "" { + s.ByCode[d.Code]++ + } + } + return s +} + +func newReport(logs []*parsedLog) *Report { + report := &Report{Logs: make([]Log, 0, len(logs))} + var all []Diagnostic + for i, l := range logs { + folded := fold(l.diagnostics) + for j := range folded { + folded[j].Logs = []int{i} + } + if report.Tool.Name == "" { + report.Tool = l.tool + } + report.Logs = append(report.Logs, Log{ + Source: l.source, + Solution: l.solution, + Result: l.result, + Parsed: occurrences(l.diagnostics), + Format: l.format, + Tool: l.tool, + Summary: summarize(folded), + Reported: l.reported, + }) + all = append(all, folded...) + } + report.Diagnostics = fold(all) + report.Summary = summarize(report.Diagnostics) + return report +} diff --git a/pkg/attestation/crafter/materials/buildlog/buildlog_test.go b/pkg/attestation/crafter/materials/buildlog/buildlog_test.go new file mode 100644 index 000000000..8e23f15f7 --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/buildlog_test.go @@ -0,0 +1,419 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +import ( + "archive/zip" + "bytes" + "encoding/json" + "fmt" + "os" + "path/filepath" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +const ( + testdataDir = "../testdata/buildlog/" + sampleBinlog = "sample.binlog" + msbuildText = "msbuild.txt" + vcbuildHTML = "vcbuild.htm" + readmeEntry = "logs/README.txt" + csharpProject = "a.csproj" +) + +// Codes and names shared by several assertions. +const ( + codeBrokenCrypto = "CA5351" + codeConversion = "C4244" + codeUnsafeCRT = "C4996" + codeUnusedLocal = "CS0168" + codeNullConvert = "CS8600" + codeNullDeref = "CS8602" + codeUndeclared = "C2065" + msgUndeclared = "'buf': undeclared identifier" + appProject = "/src/App/App.csproj" + appSource = "/src/App/Program.cs" + vcProject = "demo" +) + +// sampleDiagnostics are the warnings the synthetic two-project solution in +// testdata produces, whichever log format recorded the build. +var sampleDiagnostics = []Diagnostic{ + {Severity: SeverityWarning, Code: codeBrokenCrypto, Message: "Weak uses a broken cryptographic algorithm MD5 (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5351)", File: "/src/Lib/Hashing.cs", Line: 9, Column: 25, Project: "/src/Lib/Lib.csproj", Occurrences: 1}, + {Severity: SeverityWarning, Code: codeUnusedLocal, Message: "The variable 'unused' is declared but never used", File: appSource, Line: 3, Column: 5, Project: appProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: codeNullConvert, Message: "Converting null literal or possible null value to non-nullable type.", File: appSource, Line: 4, Column: 12, Project: appProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: codeNullDeref, Message: "Dereference of a possibly null reference.", File: appSource, Line: 5, Column: 19, Project: appProject, Occurrences: 1}, +} + +var sampleSummary = Summary{ + Warnings: 4, + ByCode: map[string]int{codeBrokenCrypto: 1, codeUnusedLocal: 1, codeNullConvert: 1, codeNullDeref: 1}, +} + +// inLogs returns diagnostics as seen in the given logs of a report. +func inLogs(ds []Diagnostic, logs ...int) []Diagnostic { + out := make([]Diagnostic, len(ds)) + for i, d := range ds { + d.Logs = logs + out[i] = d + } + return out +} + +func readTestdata(t *testing.T, name string) []byte { + t.Helper() + data, err := os.ReadFile(testdataDir + name) + require.NoError(t, err) + return data +} + +func zipOf(t *testing.T, files map[string][]byte) []byte { + t.Helper() + var buf bytes.Buffer + zw := zip.NewWriter(&buf) + for name, content := range files { + w, err := zw.Create(name) + require.NoError(t, err) + _, err = w.Write(content) + require.NoError(t, err) + } + require.NoError(t, zw.Close()) + return buf.Bytes() +} + +func TestParseMSBuildBinlog(t *testing.T) { + report, err := Parse(readTestdata(t, sampleBinlog)) + require.NoError(t, err) + + assert.Equal(t, Tool{Name: ToolMSBuild}, report.Tool) + require.Len(t, report.Logs, 1) + assert.Equal(t, FormatMSBuildBinlog, report.Logs[0].Format) + // The build's root project: the solution the binary log was produced for. + assert.Equal(t, "/src/Sample.sln", report.Logs[0].Solution) + assert.Equal(t, sampleSummary, report.Summary) + assert.Equal(t, inLogs(sampleDiagnostics, 0), report.Diagnostics) +} + +func TestParseMSBuildTextLog(t *testing.T) { + report, err := Parse(readTestdata(t, msbuildText)) + require.NoError(t, err) + + require.Len(t, report.Logs, 1) + assert.Equal(t, FormatMSBuildText, report.Logs[0].Format) + assert.Equal(t, "/src/Sample.sln", report.Logs[0].Solution) + // The warnings MSBuild repeats in its end-of-build summary must not be + // counted twice. + assert.Equal(t, sampleSummary, report.Summary) + assert.Equal(t, inLogs(sampleDiagnostics, 0), report.Diagnostics) + assert.Equal(t, &Counts{Warnings: 4}, report.Logs[0].Reported) +} + +func TestParseMSBuildTextLogOfSeveralSolutions(t *testing.T) { + // One text log can hold several MSBuild invocations, one per solution, + // appended to each other. Each repeats its warnings in its own summary; + // a project both solutions build reports its warning once per solution. + invocation := func(solution string, warnings ...string) string { + s := "MSBuild version 17.9.8+b34f75857 for .NET Framework\r\n" + + "Build started 1/2/2026 3:04:05 PM.\r\n" + + "Project \"" + solution + "\" on node 1 (default targets).\r\n" + for _, w := range warnings { + s += " 1>" + w + "\r\n" + } + s += "Build succeeded.\r\n\r\n" + for _, w := range warnings { + s += " " + w + "\r\n" + } + return s + fmt.Sprintf("\r\n %d Warning(s)\r\n 0 Error(s)\r\n\r\n", len(warnings)) + } + shared := "/src/Lib/Hashing.cs(9,25): warning CA5351: Weak uses a broken cryptographic algorithm MD5 (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5351) [/src/Lib/Lib.csproj]" + app := "/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]" + log := invocation("/src/A.sln", shared, app) + invocation("/src/B.sln", shared) + + report, err := Parse([]byte(log)) + require.NoError(t, err) + + // Each MSBuild run is a log of its own, so the warnings stay attributed to + // the solution that produced them. + require.Len(t, report.Logs, 2) + assert.Equal(t, "/src/A.sln", report.Logs[0].Solution) + assert.Equal(t, &Counts{Warnings: 2}, report.Logs[0].Reported) + assert.Equal(t, "/src/B.sln", report.Logs[1].Solution) + assert.Equal(t, &Counts{Warnings: 1}, report.Logs[1].Reported) + assert.Equal(t, report.Logs[0].Source, report.Logs[1].Source) + + assert.Equal(t, Summary{Warnings: 2, ByCode: map[string]int{codeBrokenCrypto: 1, codeUnusedLocal: 1}}, report.Summary) + want := []Diagnostic{sampleDiagnostics[0], sampleDiagnostics[1]} + want[0].Occurrences = 2 + want[0].Logs = []int{0, 1} + want[1].Logs = []int{0} + assert.Equal(t, want, report.Diagnostics) +} + +func TestParseArchiveOfVCBuildLogs(t *testing.T) { + // Visual C++ writes one BuildLog.htm per project, so a solution's logs come + // zipped. Each entry is a log of its own. + htm := readTestdata(t, vcbuildHTML) + report, err := Parse(zipOf(t, map[string][]byte{ + "demo/Release/BuildLog.htm": htm, + "other/Release/BuildLog.htm": htm, + })) + require.NoError(t, err) + + require.Len(t, report.Logs, 2) + for i, source := range []string{"demo/Release/BuildLog.htm", "other/Release/BuildLog.htm"} { + assert.Equal(t, source, report.Logs[i].Source) + assert.Equal(t, FormatVCBuildHTML, report.Logs[i].Format) + assert.Equal(t, &Counts{Warnings: 5}, report.Logs[i].Reported) + } + assert.Equal(t, Tool{Name: ToolVCBuild}, report.Tool) + assert.Equal(t, 4, report.Summary.Warnings) + for _, d := range report.Diagnostics { + assert.Equal(t, vcProject, d.Project) + assert.Equal(t, []int{0, 1}, d.Logs) + } +} + +func TestParseMSBuildTextLogSummaryOnly(t *testing.T) { + // A console log captured with -clp:Summary only carries the warnings in + // the end-of-build summary; they must still be picked up. + log := "Build started 1/2/2026 3:04:05 PM.\r\n" + + "Build succeeded.\r\n" + + "\r\n" + + " \"/src/App/App.csproj\" (default target) (1) ->\r\n" + + " /src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]\r\n" + + "\r\n" + + " 1 Warning(s)\r\n" + + " 0 Error(s)\r\n" + + report, err := Parse([]byte(log)) + require.NoError(t, err) + assert.Equal(t, inLogs([]Diagnostic{sampleDiagnostics[1]}, 0), report.Diagnostics) +} + +func TestParseMSBuildTextLogWithBanner(t *testing.T) { + log := "MSBuild version 17.9.8+b34f75857 for .NET Framework\r\n" + + "Build started 1/2/2026 3:04:05 PM.\r\n" + + " 1>C:\\src\\lib\\io.cpp(41,7): error C2065: 'buf': undeclared identifier [C:\\src\\lib\\lib.vcxproj]\r\n" + + "Build FAILED.\r\n" + + " 0 Warning(s)\r\n" + + " 1 Error(s)\r\n" + + report, err := Parse([]byte(log)) + require.NoError(t, err) + + assert.Equal(t, Tool{Name: ToolMSBuild, Version: "17.9.8+b34f75857"}, report.Tool) + assert.Equal(t, Summary{Errors: 1, ByCode: map[string]int{codeUndeclared: 1}}, report.Summary) + assert.Equal(t, []Diagnostic{{ + Severity: SeverityError, Code: codeUndeclared, Message: msgUndeclared, + File: "C:\\src\\lib\\io.cpp", Line: 41, Column: 7, Project: "C:\\src\\lib\\lib.vcxproj", Occurrences: 1, Logs: []int{0}, + }}, report.Diagnostics) + assert.Equal(t, &Counts{Errors: 1}, report.Logs[0].Reported) +} + +func TestParseVCBuildHTMLLog(t *testing.T) { + report, err := Parse(readTestdata(t, vcbuildHTML)) + require.NoError(t, err) + + assert.Equal(t, Tool{Name: ToolVCBuild}, report.Tool) + require.Len(t, report.Logs, 1) + assert.Equal(t, FormatVCBuildHTML, report.Logs[0].Format) + assert.Equal(t, &Counts{Warnings: 5}, report.Logs[0].Reported) + + assert.Equal(t, Summary{ + Warnings: 4, + ByCode: map[string]int{codeConversion: 1, "C4706": 1, codeUnsafeCRT: 1, "LNK4099": 1}, + }, report.Summary) + assert.Equal(t, inLogs([]Diagnostic{ + // The header warning is reported once per translation unit including it, + // with the path spelled in different case; it is one diagnostic. + {Severity: SeverityWarning, Code: codeConversion, Message: "'initializing' : conversion from 'int' to 'char', possible loss of data", File: "C:\\SDK\\include\\vendor.h", Line: 7, Project: vcProject, Occurrences: 2}, + {Severity: SeverityWarning, Code: "C4706", Message: "assignment within conditional expression", File: ".\\util.cpp", Line: 30, Project: vcProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: codeUnsafeCRT, Message: "'strcpy': This function or variable may be unsafe. Consider using strcpy_s instead.", File: ".\\main.cpp", Line: 12, Project: vcProject, Occurrences: 1}, + {Severity: SeverityWarning, Code: "LNK4099", Message: "PDB 'vc90.pdb' was not found with 'vendor.lib'; linking object as if no debug info", File: "vendor.lib(io.obj)", Project: vcProject, Occurrences: 1}, + }, 0), report.Diagnostics) +} + +func TestParseArchiveOfBinlogs(t *testing.T) { + // One binlog per solution, zipped together. The library is built by both + // solutions, so its warning is one diagnostic seen twice. + bundle := zipOf(t, map[string][]byte{ + "logs/sample.binlog": readTestdata(t, sampleBinlog), + "logs/lib.binlog": readTestdata(t, "lib.binlog"), + readmeEntry: []byte("not a build log"), + }) + + report, err := Parse(bundle) + require.NoError(t, err) + + require.Len(t, report.Logs, 2) + assert.Equal(t, "logs/lib.binlog", report.Logs[0].Source) + assert.Equal(t, "/src/Lib/Lib.csproj", report.Logs[0].Solution) + assert.Equal(t, Summary{Warnings: 1, ByCode: map[string]int{codeBrokenCrypto: 1}}, report.Logs[0].Summary) + assert.Equal(t, "logs/sample.binlog", report.Logs[1].Source) + assert.Equal(t, "/src/Sample.sln", report.Logs[1].Solution) + assert.Equal(t, sampleSummary, report.Logs[1].Summary) + + assert.Equal(t, sampleSummary, report.Summary) + // Each diagnostic names the logs it was seen in: the library's warning both. + want := inLogs(sampleDiagnostics, 1) + want[0].Occurrences = 2 + want[0].Logs = []int{0, 1} + assert.Equal(t, want, report.Diagnostics) +} + +// everythingZip is one archive with every log of a release in it, in folders, +// next to files that are not logs: the binary logs zipped per solution as +// their own archive, a text log of the same build, and per-project Visual C++ +// logs. +func everythingZip(t *testing.T) []byte { + t.Helper() + binlogs := zipOf(t, map[string][]byte{ + sampleBinlog: readTestdata(t, sampleBinlog), + "lib.binlog": readTestdata(t, "lib.binlog"), + }) + return zipOf(t, map[string][]byte{ + "release/binlogs.zip": binlogs, + "release/msbuild/build.log": readTestdata(t, msbuildText), + "release/ce/demo/Release/BuildLog.htm": readTestdata(t, vcbuildHTML), + "release/README.txt": []byte("build logs of the release"), + // A package is a zip too, but not a bundle of logs. + "release/packages/tool.nupkg": zipOf(t, map[string][]byte{"tool.nuspec": []byte("")}), + }) +} + +func TestParseArchiveOfEverything(t *testing.T) { + report, err := Parse(everythingZip(t)) + require.NoError(t, err) + + sources := make([]string, 0, len(report.Logs)) + formats := map[string]string{} + for _, l := range report.Logs { + sources = append(sources, l.Source) + formats[l.Source] = l.Format + } + assert.Equal(t, []string{ + "release/binlogs.zip/lib.binlog", + "release/binlogs.zip/sample.binlog", + "release/ce/demo/Release/BuildLog.htm", + "release/msbuild/build.log", + }, sources) + assert.Equal(t, FormatMSBuildBinlog, formats["release/binlogs.zip/lib.binlog"]) + assert.Equal(t, FormatVCBuildHTML, formats["release/ce/demo/Release/BuildLog.htm"]) + assert.Equal(t, FormatMSBuildText, formats["release/msbuild/build.log"]) + + // The C# warnings and the Visual C++ ones, each once: the text log and the + // binary log record the same build. + assert.Equal(t, 8, report.Summary.Warnings) + occurrences := map[string]int{} + for _, d := range report.Diagnostics { + occurrences[d.Code] += d.Occurrences + } + // Built by both solutions, and in the text log too. + assert.Equal(t, 3, occurrences[codeBrokenCrypto]) + assert.Equal(t, 2, occurrences[codeUnusedLocal]) +} + +func TestParseFileOfEverythingMatchesParse(t *testing.T) { + data := everythingZip(t) + p := filepath.Join(t.TempDir(), "release.zip") + require.NoError(t, os.WriteFile(p, data, 0o600)) + + fromFile, err := ParseFile(p) + require.NoError(t, err) + fromBytes, err := Parse(data) + require.NoError(t, err) + assert.Equal(t, fromBytes, fromFile) +} + +func TestParseArchiveNestedTooDeep(t *testing.T) { + // Logs two archives down are not read. That must fail, not pass as a + // bundle without them. + inner := zipOf(t, map[string][]byte{sampleBinlog: readTestdata(t, sampleBinlog)}) + middle := zipOf(t, map[string][]byte{"inner.zip": inner}) + outer := zipOf(t, map[string][]byte{ + "middle.zip": middle, + "vcbuild/demo.htm": readTestdata(t, vcbuildHTML), + }) + + _, err := Parse(outer) + require.Error(t, err) + assert.NotErrorIs(t, err, ErrNoBuildLog) + assert.ErrorContains(t, err, "middle.zip/inner.zip") +} + +func TestParseArchiveWithoutBuildLogs(t *testing.T) { + _, err := Parse(zipOf(t, map[string][]byte{"README.txt": []byte("hello")})) + assert.ErrorIs(t, err, ErrNoBuildLog) +} + +func TestParseRejectsOtherContent(t *testing.T) { + for name, data := range map[string][]byte{ + "empty": nil, + "plain text": []byte("hello world\n"), + "json": []byte(`{"runs": []}`), + } { + t.Run(name, func(t *testing.T) { + _, err := Parse(data) + assert.ErrorIs(t, err, ErrNoBuildLog) + }) + } +} + +func TestParseFileMatchesParse(t *testing.T) { + for _, name := range []string{sampleBinlog, msbuildText, vcbuildHTML} { + t.Run(name, func(t *testing.T) { + fromFile, err := ParseFile(testdataDir + name) + require.NoError(t, err) + fromBytes, err := Parse(readTestdata(t, name)) + require.NoError(t, err) + assert.Equal(t, fromBytes, fromFile) + }) + } +} + +func TestReportJSONShape(t *testing.T) { + report, err := Parse(readTestdata(t, sampleBinlog)) + require.NoError(t, err) + + raw, err := json.Marshal(report) + require.NoError(t, err) + + var doc map[string]any + require.NoError(t, json.Unmarshal(raw, &doc)) + assert.Equal(t, map[string]any{"name": "msbuild"}, doc["tool"]) + assert.Equal(t, map[string]any{ + "warnings": float64(4), + "errors": float64(0), + "by_code": map[string]any{codeBrokenCrypto: float64(1), codeUnusedLocal: float64(1), codeNullConvert: float64(1), codeNullDeref: float64(1)}, + }, doc["summary"]) + diagnostics := doc["diagnostics"].([]any) + assert.Equal(t, map[string]any{ + "severity": "warning", + "code": codeBrokenCrypto, + "message": sampleDiagnostics[0].Message, + "file": "/src/Lib/Hashing.cs", + "line": float64(9), + "column": float64(25), + "project": "/src/Lib/Lib.csproj", + "occurrences": float64(1), + "logs": []any{float64(0)}, + }, diagnostics[0]) + logs := doc["logs"].([]any) + assert.Equal(t, "/src/Sample.sln", logs[0].(map[string]any)["solution"]) +} diff --git a/pkg/attestation/crafter/materials/buildlog/fidelity_test.go b/pkg/attestation/crafter/materials/buildlog/fidelity_test.go new file mode 100644 index 000000000..26a46bdb7 --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/fidelity_test.go @@ -0,0 +1,187 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +// The projection feeds a compliance gate, so what it cannot read must show: +// these tests guard the facts a policy needs to fail rather than pass a log +// whose warnings were not all read. + +import ( + "bytes" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestParseRecordsParsedCounts(t *testing.T) { + // Parsed counts occurrences, like the totals a log states about itself, + // so a policy can compare the two. + for name, want := range map[string]Counts{ + sampleBinlog: {Warnings: 4}, + msbuildText: {Warnings: 4}, + vcbuildHTML: {Warnings: 5}, + } { + t.Run(name, func(t *testing.T) { + report, err := Parse(readTestdata(t, name)) + require.NoError(t, err) + assert.Equal(t, want, report.Logs[0].Parsed) + }) + } +} + +func TestParseRecordsBuildResult(t *testing.T) { + for name, want := range map[string]string{ + sampleBinlog: ResultSucceeded, + msbuildText: ResultSucceeded, + vcbuildHTML: ResultSucceeded, + } { + t.Run(name, func(t *testing.T) { + report, err := Parse(readTestdata(t, name)) + require.NoError(t, err) + assert.Equal(t, want, report.Logs[0].Result) + }) + } + + t.Run("failed msbuild run", func(t *testing.T) { + report, err := Parse([]byte("Build started 1/2/2026 3:04:05 PM.\r\n" + + " 1>C:\\src\\io.cpp(41,7): error C2065: 'buf': undeclared identifier [C:\\src\\lib.vcxproj]\r\n" + + "Build FAILED.\r\n 0 Warning(s)\r\n 1 Error(s)\r\n")) + require.NoError(t, err) + assert.Equal(t, ResultFailed, report.Logs[0].Result) + }) + + t.Run("text log cut short", func(t *testing.T) { + report, err := Parse([]byte("Build started 1/2/2026 3:04:05 PM.\r\n" + + " 1>C:\\src\\io.cpp(41,7): warning C4996: 'strcpy': unsafe [C:\\src\\lib.vcxproj]\r\n")) + require.NoError(t, err) + assert.Empty(t, report.Logs[0].Result) + }) + + t.Run("failed visual c++ build", func(t *testing.T) { + htm := bytes.ReplaceAll(readTestdata(t, vcbuildHTML), utf16le("0 error(s)"), utf16le("2 error(s)")) + report, err := Parse(htm) + require.NoError(t, err) + assert.Equal(t, ResultFailed, report.Logs[0].Result) + }) +} + +func utf16le(s string) []byte { + out := make([]byte, 0, 2*len(s)) + for _, c := range []byte(s) { + out = append(out, c, 0) + } + return out +} + +func TestParseMSBuildRunsWithoutBuildStarted(t *testing.T) { + // At minimal verbosity MSBuild writes no "Build started" line. Each run + // still ends with its counts, and starts with a banner unless -nologo. + run := func(banner bool, warning string) string { + s := "" + if banner { + s += "MSBuild version 17.9.8+b34f75857 for .NET Framework\r\n" + } + return s + " " + warning + "\r\n" + + "Build succeeded.\r\n\r\n " + warning + "\r\n\r\n 1 Warning(s)\r\n 0 Error(s)\r\n\r\nTime Elapsed 00:00:01.00\r\n" + } + first := "C:\\src\\a.cpp(1,1): warning C4244: conversion [C:\\src\\a.vcxproj]" + second := "C:\\src\\b.cpp(2,2): warning C4996: 'strcpy': unsafe [C:\\src\\b.vcxproj]" + + for name, log := range map[string]string{ + "each run with a banner": run(true, first) + run(true, second), + "second run without its banner": "Build started 1/2/2026 3:04:05 PM.\r\n" + run(false, first) + run(false, second), + "first run with a banner only": run(true, first) + run(false, second), + } { + t.Run(name, func(t *testing.T) { + report, err := Parse([]byte(log)) + require.NoError(t, err) + require.Len(t, report.Logs, 2) + assert.Equal(t, map[string]int{codeConversion: 1, codeUnsafeCRT: 1}, report.Summary.ByCode) + for _, l := range report.Logs { + assert.Equal(t, &Counts{Warnings: 1}, l.Reported) + assert.Equal(t, Counts{Warnings: 1}, l.Parsed) + } + }) + } +} + +func TestParseArchiveRecordsSkippedEntries(t *testing.T) { + report, err := Parse(zipOf(t, map[string][]byte{ + "logs/sample.binlog": readTestdata(t, sampleBinlog), + readmeEntry: []byte("build logs"), + "logs/broken.binlog": []byte("not gzip"), + })) + require.NoError(t, err) + assert.Equal(t, []string{readmeEntry, "logs/broken.binlog"}, report.Skipped) +} + +func TestParseArchiveSkipsProjectImports(t *testing.T) { + // -bl:ProjectImports=ZipFile writes the project files next to each binary + // log as .ProjectImports.zip: never logs, at any depth. + binlogs := zipOf(t, map[string][]byte{ + "sample.binlog": readTestdata(t, sampleBinlog), + "sample.ProjectImports.zip": zipOf(t, map[string][]byte{csharpProject: []byte("")}), + }) + report, err := Parse(zipOf(t, map[string][]byte{"release/binlogs.zip": binlogs})) + require.NoError(t, err) + require.Len(t, report.Logs, 1) + assert.Equal(t, []string{"release/binlogs.zip/sample.ProjectImports.zip"}, report.Skipped) +} + +func TestParseBinlogSkipsLargeRecordsItDoesNotRead(t *testing.T) { + // A large embedded project-imports archive is skipped, not read, so its + // size does not matter. + w := newBinlogWriter(25, 18) + const recordProjectImportArchive = 17 + putVarint(&w.out, recordProjectImportArchive) + putVarint(&w.out, maxBinlogRecord+1) + w.out.Write(make([]byte, maxBinlogRecord+1)) + w.diagnostic(diagnosticRecord{kind: recordWarning, message: "m", code: codeUnusedLocal, file: "a.cs", project: csharpProject, line: 1, column: 1}) + + report, err := Parse(w.bytes(t)) + require.NoError(t, err) + assert.Equal(t, 1, report.Summary.Warnings) +} + +func TestParseDiagnosticLineEdgeCases(t *testing.T) { + testCases := []struct { + line string + want Diagnostic + }{ + { + // A column range. + line: `C:\src\a.cpp(10,5-12): warning C4996: 'strcpy': unsafe [C:\src\a.vcxproj]`, + want: Diagnostic{Severity: SeverityWarning, Code: codeUnsafeCRT, Message: "'strcpy': unsafe", File: `C:\src\a.cpp`, Line: 10, Column: 5, Project: `C:\src\a.vcxproj`}, + }, + { + // clang-cl names the warning flag in brackets; it is not a project. + line: `C:\src\a.cpp(3,1): warning : unused variable 'x' [-Wunused-variable]`, + want: Diagnostic{Severity: SeverityWarning, Message: "unused variable 'x' [-Wunused-variable]", File: `C:\src\a.cpp`, Line: 3, Column: 1}, + }, + } + for _, tc := range testCases { + t.Run(tc.line, func(t *testing.T) { + got, ok := parseDiagnosticLine(tc.line) + require.True(t, ok) + assert.Equal(t, tc.want, got) + }) + } +} + +func TestSniffOldMSBuildBanner(t *testing.T) { + assert.Equal(t, FormatMSBuildText, sniffText([]byte("Microsoft (R) Build Engine Version 3.5.30729.1\r\n[Microsoft .NET Framework, Version 2.0.50727.8806]\r\n"))) +} diff --git a/pkg/attestation/crafter/materials/buildlog/localized_test.go b/pkg/attestation/crafter/materials/buildlog/localized_test.go new file mode 100644 index 000000000..52325015a --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/localized_test.go @@ -0,0 +1,172 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +import ( + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +// A localized Visual Studio translates MSBuild's own lines (build started, +// result, counts, project) but the compiler diagnostics keep the canonical +// English "warning CODE:" form. +func TestParseLocalizedMSBuildTextLogs(t *testing.T) { + const warning = `C:\src\a.cpp(5,3): warning C4996: 'strcpy': unsafe [C:\src\a.vcxproj]` + testCases := []struct { + lang, banner, start, project, result, warnings, errors string + wantResult string + wantReported *Counts + }{ + { + lang: "zh-CN", + banner: "适用于 .NET Framework MSBuild 版本 17.14.40+3e7442088", start: "生成启动时间为 2026/09/20 11:38:29。", + project: ` 1>项目“C:\src\a.sln”在节点 1 上(默认目标)。`, + result: "已成功生成。", warnings: " 1 个警告", errors: " 0 个错误", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + { + lang: "zh-CN failed", + start: "生成启动时间为 2018/3/13 星期二 20:57:50。", project: `1>项目“C:\src\a.sln”在节点 2 上(Build 个目标)。`, + result: "生成失败。", warnings: " 1 个警告", errors: " 1 个错误", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "zh-TW", + start: "已經開始建置於 2014/10/18 下午 02:46:20。", project: `1>節點 2 (Build 目標) 上的專案 "C:\src\a.sln"。`, + result: "建置成功。", + wantResult: ResultSucceeded, + }, + { + lang: "ja", + start: "2020/05/16 15:14:01 にビルドを開始しました。", project: `1>ノード 2 上のプロジェクト "C:\src\a.sln" (Build ターゲット)。`, + result: "ビルドに成功しました。", warnings: " 1 個の警告", errors: " 0 エラー", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + { + lang: "ko", + start: "빌드 시작: 2017-10-12 오전 2:11:06", project: `1>2 노드의 "C:\src\a.sln" 프로젝트(Build 대상)입니다.`, + result: "빌드하지 못했습니다.", + wantResult: ResultFailed, + }, + { + lang: "de", + start: "Der Buildvorgang wurde am 26.01.2015 01:09:39 gestartet.", project: `1>Projekt "C:\src\a.sln" auf Knoten "2", Rebuild Ziel(e).`, + result: "Build erfolgreich.", + wantResult: ResultSucceeded, + }, + { + lang: "fr", + start: "La génération a démarré 09/04/2015 11:37:23.", project: `1>Projet "C:\src\a.sln" sur le nœud 2 (Build cible(s)).`, + result: "ÉCHEC de la build.", warnings: " 1 Avertissement(s)", errors: " 1 Erreur(s)", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "ru", + start: "Сборка начата 04.04.2015 16:03:05.", project: `1>Проект "C:\src\a.sln" в узле 5 (целевые объекты Build).`, + result: "Сборка успешно завершена.", warnings: "Предупреждений: 1", errors: "Ошибок: 0", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + } + for _, tc := range testCases { + t.Run(tc.lang, func(t *testing.T) { + lines := []string{} + if tc.banner != "" { + lines = append(lines, tc.banner) + } + lines = append(lines, tc.start, tc.project, " 1>"+warning, tc.result) + if tc.warnings != "" { + lines = append(lines, "", " "+warning, "", tc.warnings, tc.errors) + } + report, err := Parse([]byte("\ufeff" + strings.Join(lines, "\r\n") + "\r\n")) + require.NoError(t, err) + + require.Len(t, report.Logs, 1) + l := report.Logs[0] + assert.Equal(t, FormatMSBuildText, l.Format) + assert.Equal(t, `C:\src\a.sln`, l.Solution) + assert.Equal(t, tc.wantResult, l.Result) + assert.Equal(t, tc.wantReported, l.Reported) + assert.Equal(t, Counts{Warnings: 1}, l.Parsed, "the summary repeats the warning: it is read once") + assert.Equal(t, map[string]int{codeUnsafeCRT: 1}, report.Summary.ByCode) + }) + } +} + +func TestParseCIPrefixedMSBuildLogs(t *testing.T) { + // CI systems prefix every console line, and the build starts after the + // output of earlier steps. + steps := strings.Repeat("##[group]Run some earlier step with a long output line to push MSBuild past the first kilobytes\n", 200) + body := []string{ + "MSBuild version 17.14.60+43b635718 for .NET Framework", + "Build started 10/20/2026 9:31:59 PM.", + ` 1>Project "C:\src\a.sln" on node 1 (default targets).`, + ` 1>C:\src\a.cpp(5,3): warning C4996: 'strcpy': unsafe [C:\src\a.vcxproj]`, + "Build succeeded.", + ` C:\src\a.cpp(5,3): warning C4996: 'strcpy': unsafe [C:\src\a.vcxproj]`, + " 1 Warning(s)", + " 0 Error(s)", + } + for name, prefix := range map[string]string{ + "GitHub Actions or Azure DevOps": "2026-10-20T21:31:58.4328243Z ", + "AppVeyor": "[00:01:12] ", + } { + t.Run(name, func(t *testing.T) { + var b strings.Builder + for _, l := range strings.Split(strings.TrimSuffix(steps, "\n"), "\n") { + b.WriteString(prefix + l + "\n") + } + for _, l := range body { + b.WriteString(prefix + l + "\n") + } + + report, err := Parse([]byte(b.String())) + require.NoError(t, err) + require.Len(t, report.Logs, 1) + l := report.Logs[0] + assert.Equal(t, `C:\src\a.sln`, l.Solution) + assert.Equal(t, ResultSucceeded, l.Result) + assert.Equal(t, &Counts{Warnings: 1}, l.Reported) + assert.Equal(t, Counts{Warnings: 1}, l.Parsed) + assert.Equal(t, `C:\src\a.cpp`, report.Diagnostics[0].File) + }) + } +} + +func TestParseDotnetBuildConsoleLog(t *testing.T) { + // `dotnet build` prints neither a banner nor "Build started": its result + // line is what identifies the log. + for lang, log := range map[string]string{ + "en": " Determining projects to restore...\n" + + `/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]` + "\n" + + "Build succeeded.\n" + `/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]` + "\n" + + " 1 Warning(s)\n 0 Error(s)\n", + "ru": `/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]` + "\n" + + "Сборка успешно завершена.\n" + `/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj]` + "\n" + + "Предупреждений: 1\nОшибок: 0\n", + } { + t.Run(lang, func(t *testing.T) { + report, err := Parse([]byte(log)) + require.NoError(t, err) + require.Len(t, report.Logs, 1) + assert.Equal(t, ResultSucceeded, report.Logs[0].Result) + assert.Equal(t, &Counts{Warnings: 1}, report.Logs[0].Reported) + assert.Equal(t, Counts{Warnings: 1}, report.Logs[0].Parsed) + }) + } +} diff --git a/pkg/attestation/crafter/materials/buildlog/text.go b/pkg/attestation/crafter/materials/buildlog/text.go new file mode 100644 index 000000000..becde225c --- /dev/null +++ b/pkg/attestation/crafter/materials/buildlog/text.go @@ -0,0 +1,338 @@ +// +// Copyright 2026 The Chainloop 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 buildlog + +import ( + "bufio" + "bytes" + "fmt" + "html" + "regexp" + "strconv" + "strings" + + "golang.org/x/text/encoding/unicode" + "golang.org/x/text/transform" +) + +var ( + // diagnosticRe matches MSBuild's canonical error format, which both + // MSBuild and the Visual C++ tools write: + // origin(location) : [subcategory] category code : text [project] + // The origin is a file, or a tool name such as "LINK" or "lib.lib(obj.obj)"; + // the location is "(line)", "(line,col)", "(line-line)", "(line,col-col)" or + // "(line,col,line,col)". MSBuild prefixes a node id ("3>") when it builds in + // parallel and appends the project in brackets. A bracket starting with "-" + // is a clang-cl warning flag, not a project. + diagnosticRe = regexp.MustCompile(`^\s*(?:\d+(?::\d+)?>)?(.*?)(?:\((\d+)(?:([,-])(\d+))?(?:[,-]\d+)*\))?\s*:\s*(?:[^:]*?\s)?(fatal error|error|warning)(?:\s+([A-Za-z]+[0-9]+))?\s*:\s*(.*?)(?:\s+\[([^\]\-][^\]]*)\])?\s*$`) + // MSBuild's own lines are translated when Visual Studio runs in another + // language, while compiler diagnostics keep the canonical English form. The + // patterns below hold the English line and the translations seen in real + // logs (zh-CN, zh-TW, ja, ko, de, fr, ru). A translation not listed leaves + // the fact unknown, which the policy treats as incomplete evidence. + + // msbuildBannerRe extracts the MSBuild version from the console banner. + msbuildBannerRe = regexp.MustCompile(`^(?:MSBuild (?i:version)|Microsoft \(R\) Build Engine (?i:version)|.*MSBuild 版本) ([0-9][^\s]*)`) + // msbuildStartRe and the result patterns delimit one MSBuild invocation: the + // diagnostics logged while it runs, then the end-of-build summary that + // repeats them. + msbuildStartRe = regexp.MustCompile(`^(?:Build started\b|生成启动时间为|已經開始建置於|빌드 시작:|Der Buildvorgang wurde am .* gestartet\.|La génération a démarré|Сборка начата)|にビルドを開始しました。\s*$`) + msbuildSucceededRe = regexp.MustCompile(`^(?:Build succeeded\.|已成功生成。|生成成功。|建置成功。|ビルドに成功しました。|Build erfolgreich\.|Сборка успешно завершена\.)\s*$`) + msbuildFailedRe = regexp.MustCompile(`^(?:Build FAILED\.|生成失败。|빌드하지 못했습니다\.|ÉCHEC de la build\.)\s*$`) + // msbuildProjectRe matches the start of a project build, + // `Project "path" on node N`, which may follow other text on its line. + msbuildProjectRe = regexp.MustCompile(`\bProject "([^"]+)" on node \d+|项目“([^”]+)”在节点 \d+ 上|節點 \d+ .*?上的專案 "([^"]+)"|ノード \d+ 上のプロジェクト "([^"]+)"|\d+ 노드의 "([^"]+)" 프로젝트|Projekt "([^"]+)" auf Knoten|Projet "([^"]+)" sur le nœud|Проект "([^"]+)" в узле`) + // msbuildWarningsRe and msbuildErrorsRe match the summary counters + // "N Warning(s)" and "N Error(s)". + msbuildWarningsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Warning\(s\)|个警告|個の警告|Avertissement\(s\))|Предупреждений: (\d+))\s*$`) + msbuildErrorsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Error\(s\)|个错误|エラー|Erreur\(s\))|Ошибок: (\d+))\s*$`) + // ciPrefixRe matches what CI systems put before each console line: an ISO + // timestamp (GitHub Actions, Azure DevOps) or "[hh:mm:ss] " (AppVeyor). + ciPrefixRe = regexp.MustCompile(`^(?:\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z |\[\d{2}:\d{2}:\d{2}\] )`) + + // vcbuildProjectRe matches the per-project heading of a Visual C++ build log. + vcbuildProjectRe = regexp.MustCompile(`Build started: Project: ([^,]+),`) + // vcbuildResultRe matches the per-project result line "name - N error(s), M warning(s)". + vcbuildResultRe = regexp.MustCompile(`^.+ - (\d+) error\(s\), (\d+) warning\(s\)`) + // htmlTagRe matches the markup a Visual C++ build log wraps its text in. + htmlTagRe = regexp.MustCompile(`<[^>]*>`) +) + +// maxLineLength bounds one log line. Diagnostic-verbosity logs echo whole +// compiler command lines and item lists, so lines of tens of kilobytes are +// normal. +const maxLineLength = 64 << 20 + +// sniffText decides whether peeked bytes start a text build log, and which +// kind. It returns the empty string for anything else. An MSBuild log is +// recognized by its banner, "Build started" or result line anywhere in the +// peek: a CI console capture shows the output of earlier steps first, and +// `dotnet build` prints neither a banner nor "Build started". +func sniffText(peek []byte) string { + head, err := decodeText(peek) + if err != nil { + return "" + } + if strings.Contains(strings.ToLower(head), " 0 { + log.result = ResultFailed + } else { + log.result = ResultSucceeded + } + continue + } + if d, ok := parseDiagnosticLine(line); ok { + if d.Project == "" { + d.Project = project + } + log.diagnostics = append(log.diagnostics, d) + } + } + return log, scanner.Err() +} + +// scanMSBuild reads an MSBuild text log. Several invocations may be appended to +// one file (one per solution); each is returned as a log of its own, so that +// their warnings stay attributed to the solution that produced them. A run +// starts at its banner or "Build started" line and ends with the counts of its +// summary; at minimal verbosity there is no "Build started", and with -nologo +// no banner, so any of them delimits it. +func scanMSBuild(scanner *bufio.Scanner) ([]*parsedLog, error) { + var logs []*parsedLog + cur := &run{} + next := func() { + if !cur.empty() { + logs = append(logs, cur.log()) + } + cur = &run{} + } + for scanner.Scan() { + line := ciPrefixRe.ReplaceAllString(strings.TrimRight(scanner.Text(), "\r"), "") + + switch { + case msbuildBannerRe.MatchString(line): + next() + cur.version, cur.opened = msbuildBannerRe.FindStringSubmatch(line)[1], true + continue + case msbuildStartRe.MatchString(line): + // Keep what came before it too: MSBuild reports some command-line + // errors before it starts building. + if cur.inSummary || cur.result != "" { + next() + } + cur.opened = true + continue + case msbuildSucceededRe.MatchString(line): + cur.inSummary, cur.result = true, ResultSucceeded + continue + case msbuildFailedRe.MatchString(line): + cur.inSummary, cur.result = true, ResultFailed + continue + } + if cur.solution == "" { + // The first project a run builds is the one it was started for. + if m := msbuildProjectRe.FindStringSubmatch(line); m != nil { + cur.solution = firstMatch(m) + } + } + if cur.inSummary { + if m := msbuildWarningsRe.FindStringSubmatch(line); m != nil { + n, _ := strconv.Atoi(firstMatch(m)) + cur.count().Warnings += n + continue + } + if m := msbuildErrorsRe.FindStringSubmatch(line); m != nil { + n, _ := strconv.Atoi(firstMatch(m)) + cur.count().Errors += n + // The error count closes the run's summary. + next() + continue + } + } + if d, ok := parseDiagnosticLine(line); ok { + if cur.inSummary { + cur.summary = append(cur.summary, d) + } else { + cur.inline = append(cur.inline, d) + } + } + } + next() + if len(logs) == 0 { + // A recognized log with nothing in it is still one build. + logs = append(logs, (&run{}).log()) + } + return logs, scanner.Err() +} + +// parseDiagnosticLine parses one line in canonical error format. +func parseDiagnosticLine(line string) (Diagnostic, bool) { + m := diagnosticRe.FindStringSubmatch(line) + if m == nil { + return Diagnostic{}, false + } + d := Diagnostic{ + File: strings.TrimSpace(m[1]), + Severity: SeverityWarning, + Code: m[6], + Message: m[7], + Project: m[8], + } + if m[5] != "warning" { + d.Severity = SeverityError + } + d.Line, _ = strconv.Atoi(m[2]) + // "(line,col)" carries a column; "(line-line)" is a range of lines. + if m[3] == "," { + d.Column, _ = strconv.Atoi(m[4]) + } + return d, true +} diff --git a/pkg/attestation/crafter/materials/materials.go b/pkg/attestation/crafter/materials/materials.go index d897cb4fd..fb8cc78e5 100644 --- a/pkg/attestation/crafter/materials/materials.go +++ b/pkg/attestation/crafter/materials/materials.go @@ -491,6 +491,8 @@ func Craft(ctx context.Context, materialSchema *schemaapi.CraftingSchema_Materia crafter, err = NewCheckmarxCrafter(materialSchema, casBackend, logger) case schemaapi.CraftingSchema_Material_OVERSECURED_JSON: crafter, err = NewOversecuredCrafter(materialSchema, casBackend, logger) + case schemaapi.CraftingSchema_Material_BUILD_LOG: + crafter, err = NewBuildLogCrafter(materialSchema, casBackend, logger) default: return nil, fmt.Errorf("material of type %q not supported yet", materialSchema.Type) } diff --git a/pkg/attestation/crafter/materials/testdata/buildlog/lib.binlog b/pkg/attestation/crafter/materials/testdata/buildlog/lib.binlog new file mode 100644 index 0000000000000000000000000000000000000000..5c6c38053d1c1530e015ee2e3930223618ababd4 GIT binary patch literal 110333 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

jrtY=>~0>wS05wg$?A*!%qCGiH9* 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

8GNGahxb-(WKs;}4JI z*IsG!y>(+e1|}z@-?lKmS4QW-pT>Z7`r3x!Hv&&S3EudbFKP5(_+||F58qqxHxd!~ z zVLT$gYWVBmyKe*VD_6=khf)`j_Zq$*{NO_X-g8y!k8Nzqvj~6G@Z%u(sIhAu!)?Mx z6p>Fp`ZV~P&j9$O=8MB0k3qQB&aUC7{BM8vd*CWPue}Za6(WCV_{ZSC{0V?RFe5aH z!IPOvisM$2Qhphqu<&>13J2=ibU7zBl|EA%D1@CNj@ zc!jVA!rfpY><)vV8|>ju^Yn9QI>J3+A?yW%peO7NUA1g7z|pcVM7R%Re}kY8?B~uC z20FX}5aEe14^DzXFcB8J^F9yr21A4=!#p?z2Ek-F)t%Qp%o_?3o(A*abQlEFV2L~L zR+u*&B0K};!I>}!X24nQywQU!-V7MW-^$`FTFo5#!H47h^vmcy0q zyzj!iIy_zlXTa5rw+gOt=N+?od2l2|crDC>>tGP9h3ld3HJAD0yP*zm6hveL%!3kfmJmme)lG#6pMVG8Nf-nt;3;?B z4>m6kPJ{@bhI#M|41&|}th>Pvgn82-BIjTpJP(5x;6(_)Ip|AtSx+_{<*X+!LAId0 z1TRBheV586*^EojgUA(l30{T4Yw&FVu0UU^3-MT(_y{7`;U#zj25-V!5P<9MX83-z z!+Z?kGI$BzhCxsU??7KYw<&&%MJt2x2;YTe@Yg|b7v6KPY|vPT^(w;mVHtb?gWx`V z2y6a0rYCnFXOZs1I(#liHQ?HZOT+NGh_vDE!}o)2xpo`?Z8+CHJR2rHgGe5CAGU$P z_FO&(KpyAXta{^}W~Crpz?H!cTo4p+9o;L;wP^+1tB7>s3b+R_*qLLV)rqV5a?B2S zZl=R~i|@vD=OA!(;lf!Cz9WZ753Ydg$pw2E74>j$*`(P{%U(mcH&+Jxa6!+4SI zKF6Z<=3YU#AJ?1f&jmq0ZUE;pV?D9JA^jUakQ>Ae=0MGanDA*~ct^+=aYMMFuJ}h6 zI{2=TAI1&mMsUE@KHuAnGS~=_kz5%Z#RW%mY*3AKZ~vSy^Ie3;aAj~T7X)LtaqhG~ zFS2N3I3L23xG`KY7X*{I$?mjKQM4%*ZAw+NLs7J;7Hw)(v^t9;Y11s)w5n*0qiEAD z+VrYu{i0|k7OkWz+U6+Q42w3SDjHrANt!T@j-t)7XtS!KWkk_tTeR6#(WXYx z=2)~jRnhK7(dJsTxmD4EOCxFXEZV%PXq}^I^DWx^s%YDyXbUXb0!DKislAq4wCP+; zL@vYW+(It6h+E77aM}IF$*pjhaVB#P3@+i8R?Yl$rM+ln%w^mR#$3*=-~d?WuI-30 z^T&v+g%xy7hbfX=&Y4Z%)JT4gAS`FHo^%iZuL7UG7 zgWD{c+dY5S28*_U(YA97xL|Mxx03^4ySu+Vw$YiWKSp>LR|a=;L9mP4!?~`0e!SVC zeTwj2t_*$=1bews(|WO3FKu;Lg%II=Tp8TY1;IY<0OxYwnzh|w&4>I!?htp_^*;CN z4hO#k@<+I%+%XQgcEVqGIZMDFA$**>1W#~5aGX2o-U+YmwrIz>c!W=J$GKkz!71*v zJF8EaRTtqi+;Q&xAUMOF zI(O6EAs5@k>l{Y-7I&R15~hnhQZ5%15qk zlaJX?VCXS~3s4?nPbdYb19F`!4<5Hyy{HBvjS5hA80?5TApjb=8eKi<@M1x-ap1E$4lz+BZ~Oi1CUv;PW^`;MB&VHBqzQi2N53>2J+ zW+4Dd+^KiN)F~;5%ti%h4hqgi^AG^D-Kp<*9qP0cMCPLcv;YMcqD2UR`R>&7Ve0G@ zL>8k0v;+m0qGbqx#qQLgFKpfvM3%D|0tHv1RS1CP?$qKib#V$Jt5E@3gMw?(It0LK zcj~`Whq^Kak@ctmZ9u_|XcGcpy*qVjn7S?nk@uhBSQ8BQh(o=cf^aD+K>JV-l%oCa zRW5(b;+3NLDk29^DLROPhtOdJzyWvamvK&&S5puf9djcEksGK0-9*7#s0;yc!(3#x$>XZn!%Jpo=?q@sAvC2gH*e_zc$*&xO5kZwx>WcW&164!0xXNgsTM>x<{Y zez-pdpbvH(EBkEjaqd}!2jJt}$3ZXv55%q>|J4f?>k@nckwN$p9E`2q^g-Cw1FTq845e$MQc&VF}N7$q#*vn7AlkhUvw)`o@spDmSIbMNRy23Aq;R*aI zyc(}@@2UC@Jdt0E*WvXTxcol0BGrL^#Badt_c6Ernedte{}+Te;v&2W2f;?X89$mZ z`tdC|25tT84(-qUR{Uqa$gMxO{nSB!&Tqq8@OJF>h?4U&2VMyI9e5|+<%*vk#&>7< zU3j-EzW2`^{94HG!FzG3s|BWn@!KH355xVg;x~rjdm(=SAH;`T@jv@TSbf4D#z*i` zSA4BE9sDqfKZcLv6WIO7{EJ~m7X{&yxCo!ZL2weEcE9tz_)Cj-68jLD1yAB)E_eo? z#Q@B5|7mw-m{}X)bND1)0fXQiKJPwS8#Zugbr8OQPhxfo-~ztreqYbCX%{d;_!7Q= zgF$c!Uv{r7Dd<%81j1MFNqiLt!4-VXo%X_84(&;Vuj7-rCkle=_=ejE7;TfTW0k*& zZ{admk6Rpud-&V<4!&E(>=^g9qtsIf-@_;IeH;Y$@B=rchT5cihEiqtp{r7B!*Hh5 zU7X7USF^m*&`~NL;Wqq9{OcfS!?)#KDw$?;+VD)LhqxW@rqi}CoavOy=ke}C;kkF5 zq0m)9xIJHl^Z6iX&lm8nyw@5zylx7@9rz;Lkq?3nd?&ZobhTL>_(%E9d?DYZ3iZDG zRd_B?5bnws;ck2obmhCdtGD7^hu1?vxCdW^d-6fhgYV_eYxbVQ>!~2zn=itB_#o)b z_jTvd_Z?m@1>t^t5$?|iK|g+gJFjP$*IPk&AYX(B@j)<Guj?$s4KG~8D~cm!XBNAf{1f*-}ZE=ON&Y_DJW zHV~1~{0M#w9~{e%;{h1$PW|E&r^|N|@STLAaPN!jt(R zDCVcQ`R0?qu~@}?ke|v=wmg8*FcwI$S~c5MP83^FeTkKjQ8m#ecGRhxk?qALS46$M_&P${*)l7h83j zShSG6Q z4)bdB_%45oHx51DQ6@SdL6eH0pmoJnB=cH!bjr<)#$|! zAOp!DG7SwT(@+r^LWYuFa2SEZ$p|u%j3T4S7&4ZOBjd>gGLcLo#bh#>LZ*^wWE7fC zO2`Z{lguK4*<=oxOXiX3Xd0SNrlF_e|J=l@`Dl%rk39MqjiWlP^?2<%Pdxc_-Dm1N zOP~AE^Dn$ua{*aM7LgIe(q=K~OO_D2l+a~lIaxvINSGY_HU}xAyXe+c6v^;^f7ihjvAaoEq!;XU1NzghATA`qI5wvj#b``X4 zg4SKodI+?q&`anoXnh3QSD^ib{=xuZpfE@nEEEYtgrUMPVYqOV8zCI!MhbOD38RHE z!dPLPFkYA-OcW*wFBA)tg(<>RVVW>im@a4~!VF=iFiV&%j3RS{xxzeQFPSfB3xtKj zB4M$xM4(HBWx{e{g|JeftAy3U8ey%lPN3@rx8b!`-Bp*Unn65gc5R4C?SW0%Osc64hu(wqrx%axNt%^ zDV!2c3rD##0_?`Yvx0U`(9R3=f9H-ft=4B-ZH3-C2TyDrqd zA>0&h31z}<;f`=up!WprzCa%c^r1j=#WrGFk+u_Qo=Dq^G+(3zVh6FKsC5!Mi-lqr zv8&ik>@M~Ydx}~wvA1}TJIwVF4|7MjV_aXcpV(hK#0?O&fg&6v(!nAv66p|;4i%4c z!^AV(aFLD>=}3`|66t7hj5t;tCyo~!W z66s;_DjAOG2&5emhjK^75^_u|A;-m0=!7_voD@%qr$u;1JS(0P&x;qti{d5mym(oJ zSHvf;ir2*JBE2Er6mN-T;%)Jccvrk9-WMN;55-)mjTC4rwUhFsc2aw(os=)NlM1AE zQU|G>)KTgrb(UzMM7u~@SBZ9$wCh8YhjHCP)*dNs?A9Z4@R;8~G^`oGML| zrc1O$nj!7MrFf>aMm&HI;aSoeakjKZoFlCf=Spy%q|KKWNDHM!(qd_ev{YIqEtghE zE2Tx!DrvQ}Mp`Sali+#@Zjj(cX_K^BqFbb`(l!ZBz{~J@dxOGl)m(lP0{bV52QX~lR7J|&%&&PedA1kXw5r3=zU>5@b*OWG9) z9>z!URq2{^U7|Ono6;>wn}~{0nM7|(+8v1=!zb`v>7I07f)AvJQm#zf$XZ*uot!6Y z?PYB;nu_w}0=a|SQSKze&T^r=5AVlaWY|^iCU=*6$UWs=a&Nhh+*j@=_m|-S84i>O z$%Ex0d5Byj50&9CdAK}69x0EK3;EGkT=TL&?b4ayhYwBZ=Qs;I()?&QrAZO1`oIYXwRNrK8eG>8ungU6ig$H-&ardMG`WUP^DJkJ4A^r}S3_ zCxry8Hw{adnUs<3mR2C_Vl_knjWtp;E zfh!caQh}=!x>{MItX0-2>y-`4MrD(-S=pj&Rp>T_Zdd3Ih3-`7E`{z^=pKdcRcNU~ z_bGJ0LJuhPph6ER^sqvYDD%HzP%bK$l*`H$ zg5pZ<$>~0$yM8^ZPj*ao=V%RG+)&U)DCJ# zwUgReEmXUxC8Vo*N}S41=S%pR{49PB-%TCLbys_+JyqCC?XC7v`>Oraqg;P=nJ_>d zs18z(a)Z^oT#-tLsM=61?%+GDn@O&Qs^B3)F?`B2`V}N%hcuS3U#Hb ztx|u#S{=^i5xPcQtFBY&dUb=kQQaoCNh9WMae&Z!n&Wrh@eFQG{iTMOoRaN@lV5KY=MnG4WF?HLs;M_K#Txn z(U6tT+V|*URp6|uSyZ-YSo0U{ssp8H2t(o8<<)D{$QA|l=c*N?OV_PXT4utQa5%P* z9v4+&hNNOiDs&55epLU|+={4+6;cHGTyr@WFmls)e zp@F{fz6n14)Aw693`NC73kPjWNl0RmsgtS>+7?Lq{EI+pa!OjWKuWz(AXyK%hajs^ zsgarOmsbDBx5{YUJiTT7e?Ugo{d z@x{JMNUv-OlSaUi@Pp~i9!{U#_uXNSc6hbl?_RlF?9)Fs6|i#q{?~L{6`4JwYmCv} zn|s5mPxejm{o6+u-@bip?3;5_hxhzx-!H^spZ*(DkmdVEhLZmh^{Yr0TCvs`O{j4^ z+=QvVX})y>`gi%Q_xBBl{Os`$z8`z(MzK$S{Ij&ImTzZfu`uQl&j`#BDvyyj{Guh3 zo9>(BvjW~>t8j;1*cJ2B*&v+c(>+lSAJ(Y032m-A8kQKNy2J-`<;{$YaslXUeCjm^ zKZDI=Gweyg28?_1o;3*=^^<4H2b#S4j14fthO#>KCz~`3<-D1Z5gl66&Mlfw!4U3uUgY$e~t`F#SBLu{^vLvurs#&It zgoFg;iQ+tNzAr-3EMNVfJF*M|J4Gk#0%)|(YCW+^3*NL!cguFfMbm>E#K~XYt30xz zgZNv+dle%qK2<)BX@rw}mX`5VIT^R!aby(v-~uD6VmU~iU)_t0rC1Dy`hXr+?k*KQ z3>zACu>1iO)!%OPpsxSx;LW%j@MxyFmbaXD>&(KI|# z7x@HVn97cCJfF^l%Zx~In*T>Nv;%|R1dVTz(D&|jn(a)Z8qD8%tZ!5Gw z7P!^sQ;yZavW1ytW0uHHtYN)LjFD{?h#10Qz>tp7{!zEQ^`Gq&;U5d^ z2dwC6D6`P1MSm)mW>HT*&eVFkze8Oc_2lCqo*Gc8NupN`iw8!V#JY1TR&nMatN2>D z3!japXgET1$i6k%ay}mBXuB>lB(nqPJS?g^Ej3iDw9#;@^j`TJxQ*C8Jk|}|Mm#Yh z#!v}EJ_CMT3Bt>f?}f=5 zd~l-=Z14dceV_P)@>Fpi+~kXZuJ?Vl*6HPq#$7C2IM^4cii>W5t_7D5vu25UHL2Yc%SnSLtW}|IH&Fz&e^`EX%t8!9)6_!%ugIgjd z69+P5d#uUCfgIf)bEvae*W=j5=o>)eobNJ1d|f7AiM#{;ala*hE9>TMEEz`yt}XZfWA8tpqd3wvV7RKQ z!_4%IdTPd)CKyDrgl!2)BZ=U!#tdL=jBUX7uGf26Aq_CL&|r!_f-Ph-uYm(u~64F6>vUy(0e;aI=pT7o`$DUB0^7xaM zl@aFgNCy-=6jH1Si5lv6=n&vkkNvB(P58HZ^`t$k#@U`UA-{LKw5(6F8-Cw0Ez=(x zB3i=Md@DL7KA~pK==xb1(XlbrW8EIVw{4b?j8K%S+gSZHJ38k5*iXEEAveZXzU=QZ zeQ6nj$8r zqf+-lsi#E@FWU?4SL{~<<4cWj<4c1M z9l{MUHf_ggFH5{84>3#NAB@wIhoX0)V_flx0fI2|G)>ED+ff|@8oc?YDlCvaHDq#a zlJ#W+d2dxaMAhWKC{5Ul!dD4UvwFbWSt-V=d!rgFM;%=&1?$`_M;x$KtmNNA_bja+~q`i<|%9|q((|m=P z_hW-2MfO?TSq_r_og#k?dDmWOzh`eGihr$1miW~|z;5B-BrW|rMIv8Qc2PDUSf`NF zI@_D!@$*B&bfQmuS>Esd*aA=~Rao^q=J(#22_FYXoS3o;H5AS|FcXkirSFLZaCI*e-&G zhJd2*a|!goe%`{j`eV6~eZEY8tt?MY-S~u{;-JHOrpiQKFu^XVuP>_~2`RB&p4fTk zC%)|9DkxV~tb2tw&?)g;;He!xwu7g3V5(R}3mH>UfX7Ds6P$aWrlw9MwfAvGB zn=0~vFFbWrkCpnp?4A4L=AJfT?AP|Cn*4VqOgPe5T3Y*rc}QoLR#~{-^=)OjmDiKc z?9c5l>@NccWR+Vr6QV;%(yp&Jd4_oq`hXfVQ~cjQorYN>D7E9u{vAsMO^f~c4?U`? z%LJVPB{fgYXt{avh|c)Ay$kM&#aSj!nJQ)9!cL4*-oLf zpD$DJMciBVxAaAxXab$auC?H*EWH7h;R2pv*)eB3@H>`!6lp)XUuJz*b zt-3tODcNAdw3E8*(n%}VS+=M@K5r2Z2f9Iw<%Nk{+MMc-UF*pwc4_^iAQpECUH|aT zF#3aueY+~e0a$oRegKvhP71`{TdoXIIbR{r$8?VjZr_ht!ZwE*O*;4f*++J7AjW*T zfPm&5vvR!cJ*A*8Qs{#)j!=Ws2g{h}cC`i_^83ef>35XaUQfgXzuEqr@N;rYpEMV# z0a-sQEwj@XToo|{!!9N;1OwhFE?XO9`~3d4o~$IHw*BSe^~Clg6yi`E{AJS6P=C&; z`11_Bz<~1%;A@nsUk8iwj8sGo6Kx)b0iRHsrV)mnNR6^jl{#E(l;Ifgj?lp`nD_Tl z3U364M`x4s%8v^&sZ=9hjf=iDrTTy|3D0QUbO!!PiuKc zphbq{$D4XGKUN`+#k$Yu#$v!@QH*AE@|R20^SvrRABXu?FF(}Y0+ljIuy;^s>t7lh zIu!G-Hut1ubZY5q-ZAS7zenlD!Qx}{74A4e{J2o@|5hPSEJAEQu>ihWDdzQHK^s*; zfM)NPPVc9pamN+Pod1Bw%uUrZ=nOPjPA4?MsVGO)eF(eMf)h z?}h(VN^r?IpT9Jy>~@~a_|#GWbG-lXWW^`PC&VWRK6x@rg-{DY>=Rm*q__b-1eV-zWd4-w7{eWq0V1 zo|>HLDFNFi+v92N&nSfvGP3mkWqbYUKm499`MU|47UiAdCn(Y&C2#cErQhA&Y)^WQ zFS`?8Cv*$DgD^SOia4f;+uKP=e3Vu}lMJ2|#{06>%+oQPfoEdj!k$^U>TEm*&&9W` zcdhrV^Y9$}$XaCOdorr@`u+~mp?jD?Gq^BfS2P0Z0we-3bj*qXj5uPJj1P_!L&onA zuJ4c~L4Uu|rK{#riVioJ8CnrMo*9C}4{3&!&~YIGJp9m50{C~i8rt7WZl6EG(GC-D zm)!qDd#E%7$en$@+SwQ2xwxzCuC=>u9$tVSS+838DBY?mWOeqzHDz@+O<0{RfLE;x z;X*9z^JyP7)U9Osh(fskBxqgEQ=*%^MfH|PKL1ZV`q7)kTeTAhZ}s~8esBAZnQ1w` zY~GghgZ5XZb++$EzbBg~e@ydx`N}0&Qm(n)sTnsErxsycfWcx6mSFYXg{2rQ!@~U& z&!WI`47zY&1qLsoz)B2OVbIqGRxA5VvdAN{NGNX6@LY3`-|Fk$g8y)M%5ryNg~Foy-p{jgY(yB5Q7ELe*H9~+R{J}SzX6)HyAtKi=ZXp~yx_qlLW6}TS5F)UcGJZDStbo&i5 zZRul$wgHE~NoRvv7t%?@@2HEoCyM-y7*1fp#?ZQ$qhgfjIVo^uNG}pp4g{_kVZ7rD zzkC8w!p7!uN@5bfVjKZ;UyBLsRqM)cnq=*+_2hFq2zN4f&@5rZ*Afbq*An=*11EFJ zP#CC2@d@5996tCpLr?Ib3ST9rdaQKxDGeOrv030$rcuYt%oyGtBaEt=zl5{yPZrs_ zd~y>mv~R{+0_TRY%DLgE!uNlk9FNq}nO?!T;L{I>wpx0~Lbqa9J|*nW+X8Qr3SW_X zv)b~tqho5swo+uY^o7+CFTeSyoD-`@LOOkJR?>&I;q7>X^nR2M@;4a*UHQ~j`BcHF zh4^Ghq5O7wOr)2th2tu3R!7gLS>n5~YV_y?j+6xwJ21yr4mqL_m$>4GQsPJ4$`Gp) z(%UWz*@@@ar6Jb&ic~sFF1-n*DPYziw>8*>VIBi^DP2y%{HVb& za5n>XDcj`=W`>IC%|H(HR>orm@|#8s1@};37ZoDr8>fO_;g04zaD=D#1gbMA#YoNP+!| zmYF9r4+tT$7ZZH>@Ea zPJ?AM2*xZ_F+CWese3RX7+gcaETUln4HnUWw`#p8S5438`Hswh0jc|O4~Bm|xO)hr zSXz!(I1APY<71onONR_L^z(NWwFiU^)`R!}4m7#~=`2H_SZRE@Rv)H?XcN1d+E;b0 zazPeA9Kw)(J*4Fe-qXa)0Xvb$0H-vFl+ohwpCsH~!H#`)LTcbLugYW9`x4-g`iWJ* zUj9v}ATMPe-PxlM=CIkzc=2m;69sY*J z&#|HdpJQoYJMBaxrIvq_s#hs(AeTvBWI1%7jf)>U@zoR4>ZB&Q5wf&WQfBU7UD$^0J=sSuhhbmBRo3z)mJH}w&}jP!yur@Dp%K*F)4}KW3R@|HDA~%A zMA|=AMV=HyR?vtH+_vN|Nk$!wj8lS)Y8n}h(lRq!i<{V)8J_GCWo2q)omQr29c0qS znwC0-0~cuow}bAcg(_hHL6<9t%~n>c=7p^0jL>XnF~L`CWu&BfLK{~`LRv<8T-6L}S8AJ-x-M5@QbOGnzOp2ueny5@c$X?ozLrL+Yra+_Ku|?W*O>d2 z&R!CstOZ@b@FIqnFqFEA&fm{Jk-N%Cnjy|%cpeuhT}6j1_(anHX9B=L1@c}!TinkGUJh$k4&}6!^06%NxKMG|Fu}hXS|R#XgO0zj zxja8zS62!dDOfm3%8#RvT~Ij>0$7zO4VqZCWX%y9`XD zR=iFtQSaW0G{wD}_!hp6YrJnC@a+1L`tx4h-sa#(7n0}N`KVTb7air{p_=kac?WNN zR>!uUoOoY$+gd-kYI(CVJU_*M>-S~xHXSFpR);-@^5lI7!@C&V!*?+6loV;_BmUJ| z**;%Rt$-qNJ5DI7#XCZ=!CGuD6fO5Le1O42d>`|#N?L-(CcaGAKL^L0Tb20;LkNoT zBUNOex`nNqz+E(zb+(I5qH7pgXAm#gkMC8Tk1>3L!BhNLTJk8E(J}^#9g2P)u{URioGs&!QUW!rtGVm6L$O&%KAnp^D+u0$sX=jJ@WT-%4>L%dmZcdwc?tbdcFvN&D=gBvLd-H_yzq$yUR%4r;31C9Qj< zyDmj1)lc!OexsnpW$$9;`V0j!P=*Aq&ycP{lVdw;ae2?(YgD&^Ir-^x#d7ETC5Zaw;bf_ z>-_0=ausP9F4V;#-H76zNCs0H^pfQX4}V=8>OFql006IqxfF%35aA{xVSx}%#Y~-w zuBB0-T>=L$oC4!uI7OFrin^6XQPs$bPHV}Fp0 z<0EAmJsr~6pATOvFO@Q*q|&1l)zhIa05yBZ^l%8HwAKb014T#p*IOBexO@w7x!8#n zgX?TqfvoN_#SpzkjR4TqLjvUCVoeNn6LI0XPRm0|yeY9Mdt`i8xN?KjZWg%S! z_qsR&J4E@gQU!SyTqXe?(8@i1lzRD|2HAXt(iVLj@N3Y=0r+xKZ3Svadv~AIk_!8W zUzNt_E3miN`$}!0KsL&dz~z17M-19-BkbSE@A(J0`Ryh-Ui&!;=m?-^r2&1hSqtJ{ z4YEO6`bQv7Qpo)sN0|tqL!|+kw`f6p_2jyVsh-v;8F8&$by~+IC%F>h(h}2?;}Tk@ z*GMKK>BwKn_+0Q#d4#xdFe4BSlXG#8MzMT4g@fgO>zpl5xWp-MY-<#w8!Sk6nO90D` zR1fQ?JeSERtM0jp3tE7d&8a%j0S7tYU6cUw9bijP#>n>*3 zBnK3-UQKp9z*8Jg0vn=1k$G>Y+>ysK;)dt|#}Z+er)J35(`>h9oa^c!gjJ~k=4%?Q z@bvpM>S9oSe366K>+y?wDM6!V**)s0In^;uoX5qzlvpFB+C7ADWAPx3)C2bErCG+5 zX(gTKNqPcbc7+puTO+*CJ|jrDGo0|U`m;j$@LAjo%7>M;tq{9cl0H+I0cJP|{`Cmt z2QpiqN3e4Ge5C=8qrrUZ-IctU~h#&C{9J|EOTE}tQS&j*9* z^PO{=UNX}G`0`ToMELeEA4Y5Gt7ZCJxp`k}70Oi8D@iLCos2CPWj_uQ3^C6ke3Brq zTy(%zmo&D{6>|@|sk5?6Ooi zSP4t1EfvU~E7~Y5uxg@E!F+ThViYQvqMMPTP=Oq|6)_4GOw^r7QJ5Mm?yw5ojp0H~ z6e^HM_rgS>uKL#9*R2T+<-oZ#zs%<$$2b7Z#1uW8n*S_#|(Ny4JwW(_YQVzcU` zrpr=^0;O52R+1~}p;UNT9c>(;+N26oXd@qLn|iT!z)>*!9!6|x1%n<%YH9_N^eAFe zE0`INA~kiLU~vaj=xz*`XqsAqG%60$)VeBNpdegDXpyjpub+|eS$4;)(2uE=D2Pz} zhI&e;`y5+<3*U(c-=PvT@ilrZyR)8L>R9Gj?&wCycc|pUYFeY)%g|-P(G}jbh|#5! zI$D-WtWct>T4nhu_lf)*kCf;t6IPqlObWC46K#O4)CQP>>HZ{QfGL=TPa*}F0y+L9 zVt^@_cb-NHu)4wG+NjXo7_QO;m;yQSG)#c$YPHrgO@J+v_eiR1A&q6o452xvJBWZ7 z9j2vL%nS$MD}0vi=^!5p`?6bT9?OJ#lB&lB{t{>$;bBpCPf}3qjgh6hMo_CA(;aIZ zfiLpkFj^*t9{J|O8oI}AS?YAbueA<>uN?BqjvA!;4H%suKSEMuu2VN)YD7z=J>=4@ zCTg9u0TXC+1yYwnAtz_}vowwQ;!}DM>#)jN?|{M&IpP#ZWmCoEEkD3Ehz z2?9mqbafEiQ4Q+gE1O&g8#HxLMJoMHEhV+aIsRP*?8S)hu(Hj}U+GTG< zHR3I@;a4i+k)ihwo*YYO&c+YL`m^S0p~uUj6zw0>txjFt?vkBC8y&woHaWz7LVX)4 z&jkie^uAMWqRpBnsw%$_AeY`SQJeKmBtz=G60wP7$hB7@HjxauT}EyqQ4L>3btcRz z+Q6UAyzI{wjX%|-(gAYm6BD&X?~e@WR4$@FGGu4DP=A6ke=VwX`LLG*4mXGm*lCNke1v!B|SNzE>AScDEkU^1iQ95imXTRbK42aLFT2c zbCj|(BMxPOtOM2bYL`VL&5_Sm$5OaGaIU4n6v`MVRsmmKt?Td$J%j4DRhO%Kho-u# zOQkjB(mXS@Ltou8q-l+a)h$CRy&f@qWXQ)cvP(OHT?)UtFmUNeP1&WL8kcIwkArgQ z5Hqz??~)8Dcq5`qGQ{*|M3-d90~rEEHGK8-gmjlDF(o6;m64DVm)zRZIxa2URXZ-Z zP2I$V*2#4|37!=G)u3JP&$4~O?eKgVd5bGhEbSHY<%6$CQm<~~%joamk}e43Ee)DB zmp0q@%F@+gK}c_wH0YK6lVh`m+wFkDCpqVt3(OJh5)#nwalpWq*Ivf}0`7A_A(^|7 zgnGXNUSZ%x<`Q$j0iSb$mz09qXH&dfcjL|C_SG)OGGY5FF=Q_9Sxc@$afw38+gC!j z5J-XP|8-sC%apZ3rB8DZ0kJwv#RQGmwUNZeXemiDWty~!9}ug<{9P+{S{Sk70xMRx zlOt2Q2x0*pkg9`>)k^IWDpfdS4q90KmZ*#&W&c%UpaV{|yZ&eQErsK2{odttyGz)< zEL+JGIOGl<3Z(<@vQ5ftws2GJu2ROBKnoXV!r8fS(TaM&YkE&JkdngL`2fdqxYe-& z?sBYD&(URgOBo(GN2e2lb4PqoU(*f)>>(2QYNfcNl&>YMFCv$VPfEIHw%Cvyb02;n zJC;WrbR-{gJc8n~xthG&8Rm8gPo}56Cnvj;m=dF=)~&9xwBH=AJR%wKY|BOpyetJT zaYs$!hLxT+zJY8+SI7{D0IWb$za8)*vr5PqfI~L#%aZUC2g8WCrikbk5K-t@4FL#) zbh%|@ZUFg!6Og>+3%~HeT05UVCk(5N!s;Gi9TDY=K0Tzv5C$rQ9+Db*ji5&O#HfCD zj<-!(dX97m8HT-7Vdn+dM@4mfIfW{$_M7_B#lcy41v-q7Q;LwD0U<>JAOIwrLD6FYBi_=6`a$DNjwR<529 zYYKS%N6Hj;+`$O#D>RT$^+H7obt8Bv7{;IchgzOq}ajW@OSrOUu;LA9Ynt(8&o&C?-rHpvL-;^{Cp-PYK2)1EBf;v5E9 z@e37s$DSQ)l?)ujT%DT3Cz*?2eTRU&@7xAaz=^ z7F7{N-zXhrsd$mlQj7HJlmhwgZwh3Y1vXp@mA+H`UrxoV)f&eBo})DOpLbkv{CV};?LPG^Y&^Th@@W$%wO%sU&bJImk}UpD zRee%vtXFl0v3>SrjqMj5mmDN{x8Ldy$}||>Wd7n;6K7PKYY$> zcQB)2-}8Td^;zTh&djy*pSS4b&+)X6|Da==HlA!v&S$~0g!qJ#T}@A~-8#|LrcHWA zQbtBbN+SPZ7*~Z~GQwX;BHcw$OK*r+0kw4Q?3hfc?p$_UaV&o;FaFrq1$4-Y(6X6)k467FS=TMkp-7f71F}SnZdNSgprQ< zB`&Zc5i3}FPfHXYu7aiH;g9sf;+o6CAfHk{GgFl#TuiMbAy3QpM5imK1%Y4)jYPuls2{7BqnC0B&500 z_;3DO&KejO#_JWIR#d(2?Cj!P)znmD_rD)ESedwoeq`=AzBb<~RPbln-uB`Bzs@^b8Ws&geq*(R2~AfGsz!! z_;3Hw2G&{^#_s_GHGbzhyE}(BIh(R)LQaE0gKxU8KA5w;Hvgq0xyh&TpLsiYGQC-U zs9{5T?I-^&Keg6`@oULnUsL_+;mmVR=#|#&lx;zS(YJ0*v-kR@b8WufXMziUIYn@e zuY@5xqpiP#Cp|EpR+vlnbn4ffgJgxg{JKy^xS2CL)|(aWN{MdouNfHnd+Ft5-c}4} zE6&XoKB>}_{?fdXGHCPSN==yN+js*Rj{U+KHlv;>wzpnv)0Qf+nvk@ngzr8@8GZCJ zI;b+jnD(kk16FeEnb7Cs@|EaDDLk>(?~GD2?jbrf2Zc_&%hknz}GP=hque`@W`PNvD%Cbs<4&cq1eeRAnFa<18Y zSu4MXh(wnENvb~dda{KJ7du7AHgWr-vpvE=zwnTfuKilwFQiua1OIH6CK;hwr0PYB zmsR+^KX{^rT(;5C(Jg&BX_@jb!ZIvcy3WC0(`JeLzyBvz_DF4I$1PAQ`@NqNe)&1^ ztvJ^&KPUZCFZyR!y@dFLgoI!G8lQho4*KKIDZfO={+#+t&1fEn)DTZ4#JgN|#sAbz z7GC;6qqNEgER>Wpu+nJf80T20D;e^{KcjgrjHHCbWHu&P#Dhh$hsu zBwpc6b`}%koKu`roUSeo$ge6nq?`FNJkej|cr(5K2<&$J55%<1(o){)sHd&ypA&wG zc1xn8qoW&i6ff-b{^0rliTLGBiISb{Yg0R;soEMZw?vNQX->#j5ghqBQ5UU0Cn>-F z?D~bTl2f6}CMVYsWF~54CM3F2T}g?&Ga)`P)s>J?J1If_mn0mR=#_XB zC9|D#oO7KpNJnz*+O<>b2HTyGkeZTO+ZE7JH>GxRijj`?ijH~C`OXC)I#QBd$;nBA zjwGFq#FV6zWKl=$#Duza>nb{2p*sAEj)l%e&Vo=KDRoloriS{FSSKMNHJ~G@ZbE|U zM{=l+PKu7j&Lz&Jp*m`()=m)paA|e8l9N*Ei0zS-P&+ZDPO6a~-4q?moXednLUhzk zteaHF73xP)qRW*O&`~$3uHaQ<9laDCE1j#Ht3!1pC!{8(2s)B=I#OK1U@PcIOioQo zscodAzoKJ}bFFh-sE)d>x+%#a{h@Z*s?oRXBJ`jN_`_;9^sh@xn{bAxlE z^H(S2*TDblfg~<2&SudJv6?YZ7&z+x;K@#Fs~d#t38P81YSrQse&J1t2?_B+y|9(1 zc1D}D?p~GMB{n-BnqZYTtH2k^ib?#Tc`@nmMhOg{3ji2ZH3In4H;%~bEVky<&ah4d zSnrc^UDvJmHnhi0&4uqofR;TcOC;ZQRjLHueb1gabu6qO0si(y3I1*ZqCa@IfxBTN z06r-5#?e};I2zF(HU8M$ zv=IOw8B#n7r7EI7Y23{H>1P0ZVrW+CeVI}nk)Jni?gpQ?Fx1goCVY+&-SYD<++ThL zKuc%MiRB{@iXO@u-l6{dm#={+J(4*KUdrH)5IMlv_!~DE;QXu52(2P8ro+k-qvel( zZ~0?mL*t(k3K-F8ja$3ZGXO|4q#Q&tO4+xT1ecbNcq z4JjuTiVxB48)vzF9RO%=Na;q)l#C4Jq$3GR2SRj*WkC|M(LC9SteP z3MB{8of`kc{qsKo=wujD@3S)H--!IB@xR>Qmw($+4Rm~Ol?cC7Lv&~OOBd+w3cEo7 zIzzk5KvDlP67St4MCQVGyF)j~g*~7%E@Ch)E-O)T;n!3i>7U@@NcL**cxE!v4U>RI#%=0Z%csxa}f{Wo0fwu~-Hs+aED7dtHOkX@JU`0uj9n=EB|3y$9}v0PHfRE?22jA)@!eT(}>)55R*E zfPKc+E?B2fr$a;@g1PWF=mv-2VPoFH^$KqWL}Ve%g-4(p6vCrM!{RxaRR}q%2p)sS zOBlF{w76EqEK+Ya1vd_DPIe(1z$c@D{ua z-S=Q40PaA09RuRWD)ALW--oy01L%GTA3*@_8#lv~B8B-XB8%ZI_!zoDF?<5;$p*BG z$0b@Zj7H>BSPVaQgQxJBv9fPZDXdzEd=87@3+M*V;Y;XzGh!q+IW3W%!-`aA0&~Ey z4%@5ns)+7FJcrHPU5Rc409^>fI_#qot06jH1$6fzgvjbmINyp`AvLcnyoS^OVjuy5VF8t0x^-8L;%C~Y4liez6qiy z6UA@};hsteooceN|L;Fjm`xEmjVOlG2{)KV%pifTj5<5mea&yVmIO5BMy|i0xc48vVKRRt{@Pi-&;Yr-Fu0BqDCWH`FADS zN|CmbaJ%=Hg0@VitrBUg2)FxyL^E1ZeDIz`TP@HI5~~Tf`w;OP0l-1ycstfuA{N8u zh&)Ub!$QIh4iiTR!?hLvu|oR-kw=MQ*v1Wx5=DX5i^QtmOkwqfh&)CV!{dY-93xH; z2D4)0&lJ`wNS!235vL7j(HSa!J*3VMXNhwJFsy{7EhNS*_&Orb6Sv?6!VS(77mX|7 zeoKjVo`^=|CE`5su^U_>E*rD%r75hch`d6aCz`pz72+yk7{ITjOROtId5XS9Tp@(# z-Rr~+0)T7A)B=@Sfue5`R|w&e_ZD%R0N|!^N1dIaP^(h(9pVbnYz4ea+#>+ELx=$x z$;%^|3iAL#k@tx!!~MFZU}(R$goNt z{H$<~6BL8L4>Dv9X3X57GV3FHB+5ghkb5*5 zg8&$5+#q=u6y^tr9EM8siWfPSi?S3qhK!kp2NH{!m6 z?uDqyXbPH&jJAy5d#K>^NopE`(~*%e+g11`lA3`y2+AZ(Ek6@Qz$ZOX@T*>-W(hy? zkbyZfpDFljm8jVW&Ot`rZc*Xa1^65^*AU<9xq_b_OU*;`(E=nourQJX)m|!$S*g?_ z;YS`a9AuZOjJc_ZE)_KOA!DC##9BKlWhgh1}iXbS@1 zS7Yj0mAWn!(OXd-+J@ZQ(GCQ_R%7b>ghJh%is+pv5A8zk-DnR2V5c$Fr&4#MB6=^% zL;H|>KN5Ca_8L=fsnqkSh(3Vw&_U!rgnmN+95AN7ffVYcR74*}d8iP%kD#LnfWv{I zJJJBNtGYtHor=gJl!uNXHz-2Kja43dUE&p?RaQiwKt<>za-Tw{5dbHQsXxRhD(|Ku z`V7iLXOa6HI*$N2V{C2gTMG4lDk3kTJaiek!6kIXnD?(Z9q+2dyDIRER#44qNxVx4 zBk~%$guZZtYv{W1aBoVGSl1Ab=o{!78Uo$oSK#1=F*8EhwdAZ*m3fL-r*B=w-|uu5x=Iita~VA^MXm-~e(U2|zz%ZrOJgZcjwfgUBnyU~&Z< zLJlPX7-Y=tr*eBCiXKK@A%>GH;0SUg3BWMYu%}f2K;oV!sv~j~d7fzQ2BXN)q+z^w zmRYx8ETYGdx8PV(+NmEy8b;sb?-b@6h|DK%K_U9`$#KTC^X(;CK50edcru@yK)S(r zaw2Jb2Kci=b5i6aGM`){JYq~DCzA$?jhFwFSd+-tsOjVkawciCRAT?7z(1pAk+aD; zhVa)__?OgNavnLKG~W5%C=>F@S15V`nNL1|E8s$M5edKo<7yk$O`(=WWC58^E{ASV zKrS|NKQ>n)6_6FE(ZpC{iJ?CkD*QESG_izSY6xGT!lS9t#8PsZA^f}wuR@I`mXXU1 z;bpoj@T$~kVmZ0O5dOXjuSSg~R*)+V;aMuYIyIVDNv<-4k5%C{sL{kKay4nZ%kb~M z3gK-;t|1G^wWJ%YA=eqV^7=7KE0;s$dNQ9}3*BHnxxuKFZ_A|hWE3@voI`Fj^yR~R zMaNszujD3jvmtz;3Xi9@kXy-Z#?3ZPfhSPg$sOcQ5*Qq&JyhYJQ@cpvFwLlbDo;}2 ze?#PMGN0T-y1{O8FIgsjYWaO+1lo|v3hnRIe)8{BzEORqOi|FSsRQIb@*rvS#PR-A z1>P4@hsfW^!-n_{Dt@4VKTH-H;w{q@{B}qkA&-(phAmK8#UFswF%lj(6z@{uMY#4FhsBjzgn0!J$En#$Qzf3WzG9sUm7s=6jEY8N7wRJUu^V)ux>827&Ps{X zg%WIfNp_=*Z2C%t3pRD8aw+4kFnpEL75cM?>`vvAJt#NmPUTUCyyNQ>-T)SnJ*j-M z7v%;$soqB3IMz$7o>UpC57n3ISAuynx2j_Si^%>|J~@DLgZ|V&WAnT_6y6{fk%Opw zaxmovgQy|KynpUec!OC)4yE$RVU!yTrG^{x@(wG!AuJ+CQ2FFY$_++Pql|eg&MUm3 zEFwo!`Q#YN4MtOAjd?XLDZF7UBJ-(yavbFb`P6vIuwNuymU)x|krSwVN_gpV0yWWC z-J9lWvby0cA}3M#4sQSRx~3<`j$#?%j4Mdb(< z(KD%hau(&DP0gVIm}zY7RIE@(vWT2Z<&*O$H<(M!H;T=*4v96Fa#IVah14R-Xx52$ zD)3&ADxelqOAPUYReT>vEv1%G%S(v;HLoacjA9YFg32dXQf{zyU^BJVsFgA+ORUXQWojF>o!VhI9u89BRjF;%4r-?%e1Qs2pmtHa zsXdhO^VWWukWc2bh~7)(llv(5e(C@Pz+U43zot^hv4}oM<&%df_ixl;3V?&gR5Dtj zj%N{FNad49DECpShytL{m|9V#PGAvzjLIjEQ|=SgNeX~t#tqiBhC-dlBJvcKPoAdS z;1qSnI6k(;O1x842SlEwPEqG5H#ke3rwkWcmYNdnEX5%D0(F+UNVzXjmnZ-(7%PPD zD+;p_d6_yzU7_6IGIiCMcI^X&))A4{s8iH+$_=hjH;nCkrGY~G5z#lPQ`9Xv0&W_& z#V0Z|pIpi!`ZkqM-l5!gse2Rvw~dwdQmNZnMBk_K$p@7CA=L4j^PmFo>K2mrUDDo-wh!WqBeo8$vHk15X zF*6mBDD8-~xR7nH%t(o~uBoT^H_q+n;-sIyP{tAc);>Oyy=yU|7ulJ9<_So98+ zOLN$G1)Ba}3jBRU=FzucPudOg=w7rTt&C5hH9%x<`WEa%yFqWdFKu92Cz;fnrl@{` zX$Ig$Dww1C3t%>WYG*irj(|s>)(GIqfpi`kNUtZ2@h!iU@q_3*G>FbG0l)bx89$iL zLxW4ifAv=xKZMRhL+I|5F}}TmA4=z;p(Wz~rQnCrd1x5jvjlunI~hNm&O^iLNhRPv z_*TY`phr_9O2mJeCF4iZd1xd(iQ=obYLev@t{Rjs#*%KBX!ui(CreD5Af|y-QkZv4 zPxtu!>XolPUnWn9my+;y@jr5o0-rC3cWVD4XY>rD>eQwmk(h!k>BbtK4&0r`kAtPW z`{V7mjjwi-GUjNmtr4y%^JU5WT20yucahg`ljF(OfLeO|N zW9WrwEWHrr)8pvz^bk0Kh7;*Y^kjMpJ(ZqDPp4?4E?2%n#n&oTsamaK zbzI~1nAn=m)$|&AEj@`|M-Qjh(|7}oH`2e-n`pe5##?B-mB!m>yq(@b@1)@_`Y*fb zzx?^@Jv83p{Cca?xy`xVIfa}??r=W9JDm^kE@vLI+quWNm)=M3rw`BvX?%$OjXq2l z(nsi{bP@f_F&ZDIPtYf6c#6iSX?%vpXK8$n#^-5#fyNhUe2K=FX?%soS805W#@A_l zgT6`MqHoi8=)3ei`Zj%^#t-O+^dq{MeoQ~1pV9^NGrE9&PU9Ceeo1qkncH+8>;tni<24W%8ME%y?!3Gm$w?dKJU1X4WuknRU#125(?CGQTpLn9U5{ z!fa)>G259P4Bp9LAtiv2IA9yKlPZG8;T{I>Wd;)anEi}!z5gNRH|8({3mJTbIm!qr z2aYiX^l_$uKEV{wCz%5J6my&IOmL@}Gt61$oR|+lxaI#cbC$TmzySoj%5c{h?mB~S zF!(0J-D2=<<_?ojj3*`#lZd;_IAS!hn!Lww_nE2>n1{?GrkHumJYk+P_!+}JXYdOK zzhrP{Qx{WL6YgfhxhCA*gnO89o~fs)mx=3b>SO9_>SyY28eke|8e|%5;)HYrCyCR< zFw<$`3~`PaZW>`4X*xxWGI66#aEu9$HQ{^{9%sViP3MUTrYpom6P{$klTCPv2~Ram zGfg+mFwHd0GR-#4G0ip2GtD>Q1tz@Egcq4`foZX6iRlcp)C8BA@N&}%(@N8QVwGvN zX^jc5HF4`qaJ^}RX`^X_=~vSR(7eNlSx6o-!QV{yun8BMu#iHb$OMm>j+;)HZqp}Ccj;3meA;xEo`~=y z#GNsXCxi?R=S&6kdD9eh!L)?FXu4#&Y=T!zS54PU*G)G}H%+%p*G;!g@Q$g{UDG|& zeG`6QdT4rNDmFbfJuy8sJu^Kwy)eBrbvAb~ySkdYnRCtE%-zl1%stHA%z5T+=APzm z=3eIB=00ZJ*Npp_x&CH6z|0LabA!x-&6CkoG{ijAJj@J-oBNrjq3LLZ8ILsIrbn4a zo5z^(SToKybK}h8%@fQM&6CWN%~Q-%&C|@&%`?n1&9lt2&2!A$T=Q;bo_RMl-wYR+ z7n&EDae;ZU`3PA=E-`O2oghzI%%gozM%gx(NE6i}EnOkLEZC+zuYhGtwZ{A?u zX#Ul_$-LRT*1W~M)x6ET-MqsLcbefYGu&<7W8Q1V`^@{z2h4C5xsg0*K4kvQeArxQ zK4Ly0qSANv-Gs|vh=pVK9;_gW8`tNp9S`}46qEe46+Qi z46zKg46_WkjIfNfz)==B+A_v6){<`-XUVsWx4;ROiIz#0$(AXWzSLApUuv4AFE!oL zmzrVeOU<dU(qZV9b!N)8InB$fcmXnrKmeZDl%o)pB%Q?$=%LU6t z%O%TY%XD%kdBwtAwOq4Yw;VOyu;7~(?v@4Lwj4FxvEaLwdzSka{J?@ATJR$aF19?j zJh5<3E%=!QKeyl)7W~qJJF{Kbt}O1x;$O+lWGAqb*vae^ zb}~DaoyJaQXRrszgXB#1AUTW0vspZc#dBFakHzy@yny8vvTzZL3)sc%5*9CIm$A!P zxPo2DJ|w$QkI7tW6}y^U!>(o5vFq6l>_+xi7H(qUW)^N?@m6*lyPe&^?qqkdyV*VL zUUnb5pT!4Qe2~S5So|A{53{(C#Yb3tl*L6XKE~qXEIz^FlPo^P;?pcX!{W0nKF8wo zEWW^AWG}Io*()r*%5v9Oe4VX$gT2Y#VsEo|SbUeo_gH+NeZW3sAF;*k1NJfdgni0B zW1q86*ca?ewzIX1wX3z8HP?!}TX7F7muKy1?Pcw4?PKk0?Po2Z`&%!W7Ep_*0%{4h zlv+*=u#P7NS_fGNTj3DvQ0p-3aO()`Sz@GhBQwf6+B(L1mKbY&O5|JdI4d{aI>9>8 zI>|cO3a417TBljzbn6W3OzSM`Z0j8BT(^VY6Ny|JZ?kT< z?y%yW)?L=!)`QF*>t5?V>wfD2>p|-w>u=V6H11F1!&a`)dc?|G0y)@)^&xjWD$?y2 zH3mrwRTo$b_c`GM431jOMOIspHLA$U6Wx@t`*vL+T=lb%~$oo&fittpdisZ(uC zk+pV_wN8<>ZjrTKk@f8&>pMl(cjwrsxwiM_+Ws`x_WoR3{kgUemfFxVTZ3gb_cB|< zbvC-l+Nj9dxXAipk+n&Y^`j!|$2)AyPFvHRwoi82n(efGy3_XAPTS`@ZO!-C$i21} zdu=WE+P>Ip`*Odn%mLe1g|Di0ov>In}=^@2yEdc)&Uec-96zVK{R zKX@UkKfD}Oy)-r$81UcGekBTBjRM!A!1X9_BMRP(8UXJ^!Mjo5UKF|?1s+6!hf&~B z6fBM!2%kjJeQkr_v#7!FMbr@3nHvhbaKm6XZaB>4ET^pCtQB0af~gF+Vg>sd(47O< zY~Tqij3GU^GNVi&j|1~epeF}VlE#p;l{zG99qVKaWq&?gXJ7r!GV<=SjBo0J16w(`jRV^`xPt>bIk<}hyE(Xr1A94Q z9|!hx-~a~?a^MgLf8)Sm0X7~Ma_|T@0UqUG5jO!I(;5`o9=cor9 zc*ud-EO^9$Vh%j!;3V5j_>8k2V8C+@yx_n~4s^DIE_Tq>4yIVaGa9@!fo^t?YX{x! zL=XEcm}j2_0YHtaz-3S~wUVC>9x6?%foV5r?hX3kBH?Hn0Kms8Io+CMi3u43*Uw5< z2G-BYkatML+}c4Zk4&$jNz}IADQ9XE(hDwwaS}}YPsWTInk;Plok+}^7z7$Nt46q- zYvG^|6{tS9S{P{k*)_r?R|^O2sz7xa)xtriD^Oi7wQ$hw3RIUkEgZC1f$Gwu$$3D7 z(3cBpXj7pHc|e!JxSCQay|_kjF0^uQi;bv2Vwcq@eFii!L#T$Fx2{HLinEGUw2+3I zYUpyDRSAiUI|?F@W(L*!g) zVJx%kQZ2h_-|tkisfEENXs~ze`<-$=HU0x-9FsD?dNMw3A&*_s&hv-fw(UIGE!(AK z>9fJg8`BeEO!e5XMRJmNF^z@-QghNOxge#?mh!#n^*eM3aH_}tRoa99TfKUc(8k%G zw48uFpJq4wzGGUZKQ=_PY`|O5De(z4Yx2o4I#YCt!fF<7aQy}ybwUzj~EbNribyy6&_hYKZw(w-O5pw#Zd9yqj;R3B; zR$A*!PXk|jNxaU!@4$@wRK-Xd7loRmP0}j{E%J?G7 z@5#}4{aLnd}=1%X%j}6H@96T+_=z#ljsW`@H zaMCZ~j9aNO?viuA^Rn}bXj~v%T-r9;FGIWGK)9$Op~JEJ)I~xr?v5kC-qXN(;Jiq% z91W}|&clJ=(Lw&TPZK=du(o2Q25XV=T@(Lj*`6P~o*((Yh*LzE5?v3k-^?xGgtbMs z$M09+68IC3Ul>cYlS(vSJv1T$g0!e`%`jbd!F8Cp>fGgC5wG2fmayq|ySp zbR?uiZBOjdGn)dzN0n!`r*`<*4xZYjXEp^g>9iaMqKrVG=&bnZtP&_|$OqvAAn?)zwoS7H8Ib{(tXzUf*Tv^R4=PKUG~_ z;ak;@0fla+-b%fldMA~j@M@+VhL!Z5>sG2ZZp2m69}nv*>6TVWQQ_<9S>|cTKCbMs z$+uET_%cFp=QSMF*Hr~p4uZM=2zX(+x|TNq)dipr%t5UH=tEc6@=mlV%s*qc$*~>taQd$dwYj;{J>m!%?rSy4Z{U_AaH?`E($sf70m%+vY5R1>m zHWni$^%sEY?~>PR`@7Tz1t4Ev);vA=A&NMfLMWf>(P1VNk87Bz zDP(nGYD!Et?MMI_ZM zAxgPJV)L1w3^1Q4GJFz?SyT>ShPvcWcMpxl{GmL+3{N3Pq!7bn`J9EAPhI5aDa5C- z`HZOmFf&ug87aif*nD272r$zX%=Fl5IRi1DxyUr)GZ&Fw2Zda#1R%p)`2D+Ku>^mp z3>|fmH!BgtT``3}eJ{WakP9E+BGR8gAr-3t$Osp*NjSnqq}M{h2O)T*3$y(=GM2(; zsla5QOkrTGC$G64k9nkx**tU~z7HR3bDXN+`gAs=pw#$5g*(`ECT(vmStxxOH%1FP5RwMPpK$su?q}M z>OZBXZqrhylgW$Gw~Yp={Pg3oovHz|v8H+NreRLhf)7QC+?$My#W2|bv&2PyUWr)Z zioqO(n58aqTqR;@EM`a@^PWq?JXaTBmbu8`m561q%h532dH}QBg^xa19-FhTKCoKh zB1cyuR>Wf3KNH&u)%w*Sga4@2uTnE6@7~M?TK%pxeLolKG10LNlD_KCT1nZfoXX+}~x5~gVI%axCUo@4YLRY)8M<&Z-CNNJzaAP$b zy~efHwOpMiA&^-bgv=h9yjmNfJ*$Uo(jYsvSs4%v!JW}?)H-$SIXDLMVG|9r-ZeE< zeXyR*Qd383sb|RK^{(ta^i=J^0+52Hpb2bnk-bug4WO9-%y=Cph1{4zYyhuLfY}8x zeN)IPL|^b+0SNgb%qE%KluT?$j>)C~V&;}3<^RNF(;GrIy2wo~Vx!h*0pz8Zv`SFC zj#d+*+hTyo-b3$9CHNvPQl5iwLGdVQXVx)>?1)M{%k?K~m*L+Q~veNLa>E-k;U za-E(|qhNg@xVK-{>T5HsujiiD_ExmiFOritBlQI!OmpDP8K5WxJc1;F%7 zk?XW)N({!=5@2SPBWIT*W|bq-A3-sdUInj;r(knOq^b0pCe3yiS?=)|iRZqitFo=BRId~wO?|wc*W;djUB#VFlb0&eNdD<) z-8S@uoz*WSh-5rW%a}TorgmUR`n1<|1W%`7XQ;#*kLzZrThg>;gvPPZ`*qMusaw(r zf|UP95X3x!AnK|gp_2cdr2fsb+FDlQl48Fv-xCp=MZ}_TRHQ%Zxz^G$Kbw}lI7t@i z;l^Ttr^FvoEM5tQ#AX3`wNTliXzmMi7EL_e;oGaLa8e@P?Lh+onO{z1K6%Yrr?d|`PX$0UdEU=A1L94@30 ze6nI{B>4{yKweCf1D!6W5q#BFptO&K$HfNX2W`^E&H&zM7`jqq2Fe_MY0#0 zO82K*g>R~SR5-Ws%W@O*m&m;j|D?RQ?(L}j(n$=x6b#58X@1ck%=7sF5>__gHgNRPK-SwadioN-t8OSptDQ`~ zNon*q+kMCO9s!;7-Q8Q#dboSK&Bk#51z34XYaGMX$Bf}{CC<_1axryFnl|g@D>W1g zf+10>QE% zNu_9Cps;00B<%Bwulm9z9)E76#On*f@gPF#FaObodcEC!+&rr`C}m(C@c30O{L^xq zuppZ&b`nFrh!&}nS?N)2#*UOWQ7et^>+a`ns=nVFWNXvF|EkFw%O$T*&F)1HP37t4 z!MD}7D!{$^9-MWhk{`H%Nb0=j&uXTD@gSq_Gc;PpJBmC)_6F@%ep$!d$;6+@ZP- zSA%byndb}qT~YYyfEal;tLD=-*0J~+a8Uu(0oN{vYfSiW9 zKXvmj*cWJs_fcy*`iy60@K2iQq#^WZ6IyTRXnp1$=H|PT%1hkAjS`^o0qlk8tz56mLtps)U9NsiPj%FWYC;=5bcDbC8Xlf=x7r z2Ch)E=wF5(oe4RzClqp|`*U}6>WM%WXb>{H7rjMZXn9p00(c^F=wy*6;BD>;i0X2P zPgIw-{)9ZnS`MS!*&~yBxz*)g1al6SGBRnDTU|!{vmSCogYDGMveR|A3dR0P^ugHj7CsOzanx~I2J-SKXG2p8~3K4dhwoOZ|vhi%jc zVt1qtE@u^<76Mq@Ns$~q%^n(baJlRr$wMou4F$n`pkc`D9?65tVT+umqqpt8L6)xk zjJPt$(v|!BTDj6tPxDC)ItXzE!93Sj=gOdR1b<&nIP5D_)`(vbBOb3O;^8xuc*rh+ zNCz?Odq>o#q3_0f(RU+`|7(DMFM5J|XvHtw6Qhkbqy4L5$XC!seok78e$f*akr8>A zA1lvt+cyi#2xAYdK5UI!wrSC%P5Va8o3(G;EH|fN^CsPa;p(IAd9J^ z0Oe`!q!m?&>24x=V04qLwte446mANmyBxvs`>9~S(U0v-RT)_eO8it%rr1> zT2Y0>LcB8oWV#y)@_AzP}Ds>E9I(=#=R6-}a#Jff-}AY{!d68vM@D+)dR z(^xF<1>|Qi9PtDqVP44bdYh?RM8>q;$`DH3$O>G$rNg?yYhj`9S>FZAE{h4Ati9Ua&SvZagQo* zoxCS>y;AsQrO1wZN)Az-2xZY9@Qf{}dTc=@>$HOQktx(XH+i6PM}oj1&l{14D*N%s z8tVdtIm(DUUO9+IR@`7fZb>P|BO4fzJ*vEgN4{@Fp1dc7N6s}O zuU87=k(Z6go0TGXk7r7&<@iVr@os7nd-6g8=v$n>&jK)jcvXZ>q_q1%?B&SuYTDEz# z`CiN8$7mi)-5rg2)UwHAfiaI|Za1|u;`1$#p+B3!~TkKIUcL#A(A=Ggl%Pom?U-A@Y>h~PcZz4tv*i*F=cfGV>) zPpLd3C)W1LQ3cCOv5eIMQbvYvo6`QfIVSx@@b&7?bIx<+^NYf>oapo8@@|y`Fo$v0B&W^5*aZ%T5jS>P+yX?6t zxoa=+wfs1SyA#OagN4EYX=POG$2s-|mbaZOBib#{rwAf`4_1l4*hl)MP+ikIx+2@x zO1`0bcj5{1Y()cK&8|uLJ(ttTX&bIx z|M8$9jJlDJ;mi&9rGJd51A&Dh=-5Up@$f!$p%Opy-}M$|nb)brm^jBe=F!;7e(JDV zg^7#v*KVReHIon8MnSXw6QAySg}x-Cdw7wznIWAAKzKD>RrBlUyj`?q_`K=96cyt* zo63QjOs&wbvoqc9LTOC}x)*!vx}&|jW!(m!FD{3y_g5zEc9A{Nta(tp2eSi*H|bAN z$5DwgcpeZftmHUrle8*h;qSQgL~PGCr&Ny>o%)}?!!$ODCanY)~LE}*l!o+_W^ds zpY@y(2GZb0*cp4;rw8a9H#k~GF~Kdf;Qhf^J#K=ukD155kn98Ie56A4j!vrz*HQJh~{g446` zN4a+x_)`(74pq%#tB4xBj?j9tQyT@yvnIQ#EqoJwfvXR?(xqY({(KoSr=CFTJeXfs zdN3J*6p37W3&?QO3?w!?%OzGQ9K{Z zf0x-%M*SYO?B|w(8jIW^D1dYL*-Dox}6q;blnH4)Y8B5h{}^imyHu!?|+>vO7)}tC7Eqa&=L$!>=S& znN0%U{R1;`slVIbd{9lca`GP~vt5#O#&Jv65b|jB!7Luzd+C)bD^LT3!q zt&wvMLEWWIwROR^^2`OJdd0sQ0o@2xOANryDfwk~>J8DW;u_=CLk7?W2aOsM;Y9@X zDX6v7s@l!&5N&t6+hnMJRuEWIdKsEJD$q1l$c;ULEHdc|9e^1zT(>BQXq~}DM#Z7` z2CPml(2!<25lsJBD0mUsCGoJB!LI38`}PZOnPK@e9YJ`I%f}jL$3j2a?xO^AiTSC* zru5Pwb()Tsf31~XO3#zxf{y;qd7j2j#+ov2>VC%JHw*cP*nZeb$J{wxhwhVNe%K?b z1;;{Jj}q?dwc67juB=O^mhx$9jB9D@%9CtwN5aY%)5u$x#?^XMo5nl%rtfCiKr^6E zGFvzKIdiPO6>8^a+$N{WK1CU##%GB%gypkgaAY89>{}Blh0GY0&z97-)pj_Rok)D#OUe-IGrDmD zbj|P%pM(?lL@ZKW3?i^#l}c6V5nUd|pYRmI+GIhDoZ2G5HEjQHf2dskWI4 zbCnTBg-`zAU~yo+@V96h0ZTulBl?G&wu7v(GmCT?s)N41$kqb=)+fk5&|C_H^!cD! z>>399p_NMOEdaG?*i!fSS&1(!;6)nS#gW=u!faVi&q!^{Qv*^td9MC3tiMY*Z?p6o z^Fccksd8l_I8T`WA&&(0--M8RsZdPpq`sGh5<W_=pR z2TZxSoHh;wyoL{LUyhJj_&@9b~_Th>ycx!`}JFYlewny>GG=rX`6LH)NS z8gI@{;evwb_h70ZsJb>FdY0AuBUh3TSCu(-v6B?2Y@r@;Pcht~* zZU!I0(=O*g!5B%2*fmc_Bl6bnSQS4hC8nknqB@RRoFF%nrh}8v#8sn-fD-hiv1xj{ zj|c%5?`J6An2&H5QWkZAVQ4HH55V87G$}&dMFK}CGUn5$ABLTaC@#@@2z`FJIkF^a zSHxnRZIqhS@BUK?_X1SeexEUzI+qUUF>ntU@f~1lM6vhAoqLzkmnQ9WNc*i+GZx(N z79-d!$)VMJl)0g0|9wdt(@QG&CN1lNfNwCaFgu1lS@ZQb~QvRB<=huy46nr!iu5-a(aXuif{Tm+Wdhe zJ!lpW%zKXNdLHihUmCcptB7b~nG4$7=?5@WS!F1!Ty!>&_TSQp2QHcYfo)p-#>H#b zykqO3YW;2!=vHAIvqg=pL9o?Mw4bhv8s)m)&H!D0>v*`^-L9GVzp6*N(y(avfv^$2?M? z6^FBpY-yj1i6Jcv!&RX6&~P?gk#;Td^LrtYEyHl=V-aD1O`i0}U@EkIUM#ipY~THe zbHd8~Ax2LzLE7|$iNlzNH6lO4?x?Cpn~?-a2Z&$t$~OIv7ek4YTPVt?YilZ_NEGp; zZ)uGhb^M@^TilFX*w$R88`~X1Az{8QPa~y!?oW#$q7eTDH=f3vuA688gE@EUI(rnh z-^$oG-uHZP52;o^a9s)2>xqgCwO9T%2Y~D*Wthf2xfkZ|Rk*B#VwUxj<;5iK% zoV+%t#S0II=x_Y>Ynb(^$}2qg8yjW*r*;FR!M&Xu*d5PaM2LPl{MgDc?zT&LyJGjE z{RCo1BXt=*+PUQq)R)=R>&&i$+<6otH8vl;KM0S`KPY{dV&~m{B+(Cmjw$RLZVbCdyU?ukoLaFXW7H9k)X?d{>4*EQZ{;qQ8Ah~1S%F!C6BpPV3i6McE{5n$(!Hv z0zi(^CAj5s@|00$scvJY>|gJHWeT%HFYbug0wRzyJaF{oBw4HU6^(G9>RzYG(3W&=DEe}=JYI>Z zFZN1M#a~|kT_e$#Gw1ONO9g*|YP+2LhBo}QLi`J>3-Fx1vxCv=B(*UxeYV9MwLkS?)F(B&XVNL>7ZDU%>EX5BynCK4>PLZ&rely{yjRcS0M|Xt&LnK`>cs1P9;0 zwHkw2!4}wklfy;LtOWxtjpx}>g(c(+n|Sr5is7}o$;Rcc%vJ8%nf8?@Jsv2hRV5q< zM;qG-Z2V=;)EQ%>=4ATkcOo^7DbreA25qaFRuw?AP2hjoQ|1MQt0+Ebu6}G0`z%DsQ=tWsxpwWsFo%{VL{>0 zD2^cM?rhrOF9j%l`4V&;j{uK(ZW%(&2$K;D^e?boLq-AW%ALlFIZBMo*<#A@BR>0f z>4_2Z^E=X)hF?RY9#uAvozk`ShGOY$;u2vwU&H;@oKmJ zwdtQ-)7vJ5OQ3{a`nD-8qn^`paQtKD_VrGsp<8HK0pscT^tTub{L1-?;LnWdq?h~n zbqBp>PK#uZWp(R-2lLj+rrKalLAZE*g3kY2@z36Fa33 z+}-sKnD0smlZ$2r>}1I9>T(dP^1z*zMLgrU4})uL+Y!2S@X2x&Q`n(d%m>zDc0YN_ zb#~<{Oy$LY5JhSK>pXJwwe$CX1p`(P5g#{JjcQvQSjC*ksah#y)naISWyHIUCv>z` z8b}b~A7^QWP&PlXQU{*O0C_r*Y5rD~F}35CI{EdWz?S)2EAyzgH-AJ*Ru5f87qLNx zwP?{zVwJ;MSu^9@JiL0|!0tr(8(zg3c+LxKcchD`QM&3{-h{2bsJ4CFQK&5AC&GeH z&&<_|LLxi@D+XLaRpGm*7`@?nkmIi^s?OR_QdD-V5wT?WaU)QY|=Ux zaXuE_j?qbB@Y&|2?#ZC7*|$Z#}5z!j+XjaChj>-3%mIja)Y? zO>&IO@hKzG`2dn)4sA5d3n=X?x?u%7S&vRFAKs%Gb8KE*Qrl0;X3VZ^1C5MYFU z-m@K-eR=}hVyvP1Bn+@v&w)+8_Cu~rRVkZ8xDsJ~?a-MMS7kfcXdan+c-@JcW5ok` z7z|u0s#*PN@e|hVgRYLFY2Oja&^hMcK$z{g6|StILW7$*T{=WH((lTwxx0^6tSPqs za%khn7CdN(wEmP_xx+Lt3#z)~0&2oKp=eTj+rKgkjq$mpVaM_SY8#}RKeB&Jkan46 zU~oCEz`D>@jSjl@%~ln6^|?a!3);9k(4G>I!x1rN-oU3} z-}wQfw+tM(l%H13KfC-ytWg{d!J><9CA|J;N0r=MSoD}JOB zngk4bL{e3TtP}YogjglpZZ7kx?ID9uq#b|f?8`_LmX0j*nTZEpvH<%6ezKcp01z)# z#des1IA^CGY+S*A|t25MqoNRd-D*l$!cFWLUU$ylMotEykRy;BLE}kY3#t0r< z04uhnp;-2ZU}d;H0zgvqVQfgBST`;8J4^&i1H&KQSgFFe42gY;Jw`FBNOwZg>BV`8 z7>iE4NIdINV_g{lZX#S6Yhww)GzLD_jAz+uT;NPp!wJdnh6F9MDLQPV*OY3rT9EG> z(cm7XX+$1R0VsBEuVG(T4`d9(r4p?iMjf!YKr*c!)+Z5Tv&1=qs!uWtg)@$q+EW=~ zt|aZ%K0Ew==uN{KPOeiLX1xgi>nLwZH6Vahz5k)&KkM+Ot*hy?bu>L_M-s*6OWOOv zQ8hPxkKB)Xq=!Ua5t#v?-#fnxts8L8kEXFVqdd*a=|H;%%{=U(xQ7yM;xDUSJ^O4H z@Zq;d$~uQSTUSo{(#XYJU-0^WhEiRr-l#+{!VxjzL@*ai;9MV1e|3;a}$8n?1yN9nf?(!w1Zsa}u;8V-z` z{~;8vWy|Fs0Lx7K@1ZWIdT168oqr{&{fEqW4bn+4O@EY=pmD-F9*zMi(l>V%98Y($bI{m*p3W8=v06PUny{e3e86_g z1Kto1S-USkP0!Ep7CVy}bbTL(8v;!CqNw*lUN@0{ zx=yRvOf3Wvu0Ks%EJ(ys2H0(nS{oQl2knmjP@b)5<`DCKaAfT^4KrmGwQAr@WbWW( zN}U5r`@5mpg~27uI$QFk^e2j!xbP$73x+isP(&XRqrIAbI717&itp#InYh#Iy-W1s zCZvEHQDN#3Co%ama&d)U5X>z}lFwIvn(He^Jzva^!-D1&CFC|bc14IJ2|Z?{xfyMN zhyTEkww@GsI4`%pJXI$NJ7)ZLkla70!W3>g5M`k(keg<=HOh^=_hCP4DnXH_svz*i z4G4J)NpjRE(HaE9~5xTdw}l1uYcv~-fbKMQ~AI02rc@{tzlCTNd)W%esI;U6G<(tmZVFd?JbBM1f-*9}t z!R=hO5QCg-=3_GjM6^MEHDYakyn$+R>R3Hj$&tl2Pm|yVHzqST=GeO9Q@78qs!T{8 z6bwm$KG9lxd1(q;UjfkK&%+RqvJgh9e`~Ynj`KotU@vq2G9BUTd!e9@^D+>LdE)C> zbu2OAC-qlYQHgIXbRXFPtzj1yGyQnbUxa{$!e%<-Q*o zR|g&M{Cy>W0e#tb{5IP~%|$uts{ENM!pBH*f~Y|m}Z1q`}|OLp@Xo;eW_N=yOfA`b@) zH(KF_l`8+k=qY`^Gf-FZUPBSKReInja~dkp59UV2QwxlGAzQHC=X}s8pdX~dZi!RN zgK?0CM~wOBKPQV{GXeGXaFB3cGz7$JH?eELN+%~Z`YS2!3kuUZQwL|*H&6nl`p>~q zSnrmxyLwRdNNtnfZSVf%H`qa;MZDcdq#53SO?-j=LS4*`k8aHowCEWUZAY8=4-Sii z!~5T0Z&_)Lg(y2U#qQy${sgxez}n+u(nxQ=n2cORcg2na-{J7QQ+^P@a^l2-_Ws@$ zHs&g{Q$8fGhUMpe1wS$svrp^%FeU-wPF#7E>G9FV%;H%1vu`YP5~YA2DV^XxHLv9@LC$vRjrCjHGo-@~w?ZHr!!Iydj4z5kLm z8azP&!D>tqp7>51$MtvAeBypj-s*!_=8Al-rANYH#`+$j6{c6@ilUL}UBXg2~5lo5`wsF3)P}IG`>3BvriK z8dC3Uz0#y~fLn-r4tk2ulDj=*P={|Dou_g@;R$(8BpFjK4s*ug3Cfc3n^G>7as5(O zzorCrYtc_ZS<1>gU4Hz_RK!fSpiF>}qdysfROZ znaG-q9c4@8>a?FM`5v!Ecq{9?sROfn@?RP9-DVBO){AnDHH>4lmL$R9vqJxE4-LXL zk$Fo8cK4LOvWAEL`3^46<$u>;k44_7Zoj_MF64+x-R=bxdS8c^`wRDacAb=t;a)y; zfN8_}G7Y;nG#ugkUg6d&hxN+cEcXJ6vG)z4`+UT&|b zr{DQ{9sbe8yeC3eko-hZ`5TrgQ%DayJ*%~B$aj@(?Cg8U?!8``>z_y-AoWe`+X&A% zojEH`!gZM<7?+gaCq?me-Z+KGbf2{BO#@#U3D|2}Tj;VTu_P5;qL8|L?F@164{VYP zFSehKfS{%i;4q(Gsn3C;61RtO-{2qf%Eeze?LG!%lZ@r%f89lXOVIXLmBPpl7{tBA zA0agO?{IGB!u2hxpxn3Q!qyyHFJTIM17D-=$|vJ6AGaUm!mk>$h3xN+ILAdfKJ`Uj zLz4BiOHBT-RJefVaKzi4%q&`=@9ht%n+KX_5L#HywgkCUfVPP2OvD8_XMC}*m%^RO zjnMBheU#`HKiKfG(UBrXtkzPVzHUm75Kt8uV#**e-c)g^nnmD~hjr1>dmc4?pC&m>wpnDhgz z{}`s57h$LP9=|;K{;t!d@BW@{{No5cOjxg}F=r_V9yiO*DKS`2gujWW@+0G2tldt{ zmxjtH!Q$E|!S43Y$1hP8W!KD=g(~2R|2{OjEJ%Iyrks`99@_SY3)?djB?{RGX4atgW);+oy>$=EJ4#EPrLAp)vJ( zfx$sYhGV^}l)j`IE=ejLUPM7%vrGOP%0(z55TpJRgwO9jVw9Hwa*Z) zen9D~fa6ceuMi4&tuEr4rmAH}bJEKP$8X*GjO|etx9C>pGDDbD`o*MQ1-@5lMNhu~ z_9`f78pV3p{{jM)zS00KsRMc!K9l?NFCgus4vY|7cdF6CynZdA)_Mm?8l_E)s zzxYVf!2Re_M2}*t=dIhA$|*YFHft5RI(%j(etE5r z3mY^;GaigW9Po|%Oi<`MJ+fyV+;8Jp{hpazFOcpRHlh|wCL8@I|1M8q(BSbMF%79p z-drxGful7LoN+SnQuUpJ)G+#WB(#9;Wtg6W>Jb;SHE&i1_HXQ4795d~MNA+>i+y*9 z$n@M|xQL}ujGc}Thr__#z6^eSe=2N3Kh!rz7+c+I#fBZ5&UYDMlXvfnTA)riiPD6F zy?+Zu->g$)ZF9lS7J(&c+4+tc>MuRxh&J@4WkCJ?Or~|P(Hlvyq@zHSoJo3n{b=+dwq+#j<%}${ zMU&VD{}6LvCiRI3-T(<#k;9vW#lkUlTxd%?8J4Cl0*M7y%8qngH{VK@U<5uJ3E`f9 ztWqw{#^TUgTRCG5bKttO!fZ4!KmWvOUfJ!_A5ZK8*!YaTk!NzcK2gJ3H>NC*7B~6v zzL^iXo9j1?6JSQyX+r)g{e?69xAGTtjDA3AQ1ILVBC#qnX~ooOOr2NTm@)4tWLv{E z!uM`29wqLiQ(ra~9S(WOTqUeR4C9Ij!kvxKTiRz%)av#a7`<}Q?xH2%W&!a|nf5Z@ z8ge0Ob*@J7s)5KK1Mq{aS6k4djhZUIlR)qyAy|plPTgTY^(aF1Vs}PGQc4qwyb537 zwQkcZNy4&wEZ7w*Zbm}xM_|jjB;>W#!y#IDJfl1-gVrNgzPS=!kNG_p<8cE+TUwOY zft+#UMn5NA1Vc%RLUypFo;2!55(>i%q)TUZ4U&~!rS|{PiT_%P2o=@K@9i$uD05~T z)CgkIX{x|}l+8Q%e%J5|8Ld||A8lDEk`)z&#KV?_0HpAOjfIn8pggsO?uRt~8*K;z zjfEKc!hJE60l78NRZ?kf4JA@9^h*90EU4?q@-!%+I)JrmSfJM`FP2*dYNK<3Q>{3s zA*1ltaP=%YWmAnkR8>dleMe3$@5mObp)od%BtiZ=Lm2PaGblZ>6J>`M^~464Csa^Z zx5VDtRu{A!hDd34bpNA1lc|iqQo8%Iw~aQqSzA%t%;3dV(NtFV2kS#A$rmqB&>|Ki zB%Ha#B4*oiK+*2l6<~L}7wHRCI4Sp)2F*(eS;B1rOKr}vlO8rzGhY11_5x0lN9l66 z467wvp3x(XZmE$^RDE@a$gX+FJ}{nO8uCXfALOwp+cbO`;=1IN{W1CkkH6?V`Z8CH zrs!dnAFa@QjgCP7_pxM%PAEu3qZSXJswG4z9CmanGX^K_d#R=pe?ucLQgHNRW-+=9 zn-+48Vstg=hq4-Wmw5i@@IwCrY?*wflJA4C;y@`){_75_TfWz7nY3pXI&-vJ0w}59n8Q(c6 z+M0$6%(=P3xZyfUOMb27z+~`GBus;uV6Ebtp1Qp1zqGvfqpJ)KMh;vcFVP!F<2Ube z|DL4*N>(0+iS92P0eD=LI)HqLFwGk*I$0pvcrit}_tga$r)IO} zyXtWH&;4jFt@vgXBvKeD%30U?2R`A z5}vi4-t1|}KL7R5lQ{hyl`GGca3ZHm@^ZP=tLg8S)Zzm)%Xoo3+F^g1_ z{m~p-_a{a+)1gN3L0>CfL-g#gwvag=+G};*wBGtk>XN57)?9>GHi`>Acxa+TwOA8= z>fM@EIGVlp{Fh)Gkqb{*ZNGbP>mR&CTCkLK@#mzOIuHJxTXRiVz~s_my7qqHiPcGZVLbAxOT^2M+9%ZDdU8Nxu;oRrrmGqo3rg z%)I-E_jC``2?)r3TcThL>S_MV%=g>lc^L2b-s9P)Zjx)gTmP?IfjK1QJdK;#iZdc1AaeKTH+E*b^sWQ912ILPGMAXQFQV_ zR0r4ySAybLT;JY26Q4Y}k!~)Yf>LcV(5xGLX7$(bjFo@FPO1;bJ6)!cUP&dVt9=zp zzdx88vo$&6b@6)2qGZEB+?`^TaZx`XaH#QH+!Ce-L-|(tl9={9;D+t(=56 zZQ7PKZPypSE0;RSBUdfovP_-{51Q9Ts#a_gdEfI=df)|Gf5CT zoW5S^JSr%+bHWXbz_g2_sIL@m>kx)Y(}CZ9A5CIQ;zdibWHmm%85NU=XUWw@t7c2hF6f5uirA1<-J zPkBPKpenB=KJQomi$0oV91 zQ>6ska1Qy+KUyb!_N`a;$Ln*$>+?T^ceOd3ZqZ`_e$y2FL_z{uh?T3?VT*gpghaBp zFu?}RV%VLp&FZ+GnfqO}7_D4rh0k#MSonRkfblndn*TrFuoqGL9xTHrmOCjyi-*3? zob+*+XkNnp#Cx2<+d$dt8M4nGQ%9mp6JqMV)z7}X^!gUpAG^NJ!~?MoR)umXmBv<@ z|81e33_wCt5ZCN}1`Qe}*NSV8kKkU5(3`5W&9Hm-CB&GBO+@6jPeb3s!Waw+M;RV> zaxg$fH)ce!a{)#8txi{2?+_i~_Mt9PoKBNQTSGfglcSWnc*A#SclRs znzXk*bi&!2)nqFspD>!%2Vd3Xg+?rZmZtx<`S-5gG7B!%)747$)IZnJIZ6xwgh&5+ z&_x1M=|06PCBBHVIzkjR4e2FOBrn}l;IK(Axinj?XoJTANG(QTciNu#3<`)8Tn`Af zK`_`^m!E}gN+rUYP!`-iGr`b~%2IDl>&B{z_gD!Jm!9!2HXrE98f#FTbWq`H=}e1Z zNM5<$3QK(-JW;bKm1G2xw;#FNkXA|-`o0sg+-ra@-hV`#fi}~YAG_98?^C=tJ0CPO z+Ho}^6L<9GcE5n}fK+B5jH28+8 z8xk^q@FQ0v4ofQ{lRE7iSAT2&NrqpmKc@cJ-bohrS08sl&>()y2~1{-RgcO3!JP4f zBWrkm8Z9Ne?2hbCoEQP^rGhXL_Vbm3#mXo3aX{qvP8>uGhb`q~B6sS91Q2KLkBDHX zY{cD-TQqx-XCDIUa^md2q1GKuU!C&>MM#3~vzFW53!9!NZ_Zo#a}+wG+5RNlPnwPv zU2e1lM)ZR&^jEw#hgYBdy)WLQj5u=20F)ob$$s8Hqek#Y-TNfwY<{VJ&8@!g2t6md zSl9G;eadZrbU#_}=Iw~pz0R<%SD6k)jO`WbgrSMu&Uss%`Zu_!!rmE6On-ciuBsVY z8@L}k`25Byl|As>H8gd9yV&Ijj?Gxzag^yMCC=vlaX}U83?gop7_bGS{XMf-Hf8N+ zGw4Xem(%RnghwTd^L+Xc!5{7U6qqw73J`jobD!{~@Y(h&%ndAx??nKk3{&q28-*r$ ze9o@d-Ub_mcAILd1%Ysidk}S>2tkN!;sc0aIGt0xGPnM3KYScb!u+%;SzA##A=f8| z3uA0dBlnAU-kt8iZEN`W5h*Z{Lqux`VQ{A@txS6p(f!FF)>-#hd|B8P#~vqgtu#+W zYtYgTrw}VwbyHcv(hemN>-^cRlSK}QX+Yi_+mziMG($URop3x4| z>jSl}*hD5oDcvVcFkC2>^M~5NX%*P+Cb(_Vba$(yi*7oFaFzJrkfm+%h@&wx_lF*p z@V7-lwBIL83+!uj3c=!aFSa1`uRkIjpt5n!q_qi&_Lk&j-3eWNXK8wGENhKrkBfl>ope{yYte2y7}6trfKu~*1tRUT~MR&_!X#o zrv!diq$5M6!rUtYa0YQ0Y$T>|4QsKrTQG+qg22SvPr$fuV7Ic)&KQN76_ zb%HPLw-k&L20?UPeW=d(G)Oz4tqzO>%0Oy|&7LX;S_!%_pB)A!FN_Tz5*noKPSq~k zUB6>x#fyuMNWBNnwVEXkyqE{SNWitLB$DgZXD2L9gj$LA9F%+g%F8X~iLShV|yngjMT~AlycX<<-iF)NTAv>bOu4t5HZq0#r86 z?Kmz1UUnI*dQ2du6H@wFTL|;na zi4PEgvBZU_X%;2G_K>ld*H+s?P0$A}-58ziO)O4~+P;7@%_F;NiI%K%Q`|y{Lhw6G zDj4D7l`JAX@ZLW+=N`4X)7TBSu@8zRt5WiH2n5}D?7e(goY?HWEgm>kO{pfGMnUh5 z55kUxQp<42Ed9}!B2r32(RFhwA?u4YrwfsR^lg-I-X*5;6hRT}S9F+v6JAy__nebB z$q!sdA}cDP7IN`5%nAO8%-)YEMEu85Z|o9AjzPYwNjxm%Y`s@PC;fC?e84S<^Oco! zxKdx}EnNSBkM9n+i2S!Fm`*!0=9t>7WVCEUC8B*%Lb!;qPymZYolW#Z^fS_h3w%WS zPtUgUobkyjLw~&s2r0uRt!gq%ZBL)hHX;!$w(BLFuLEO`4G?x{y)Q;1p1p=~>>0Uk z&+%$QH7LRhbkY6$+P}T&{)?mOLH!ZmKJNaTf$oi6lGE1+-9XQP@|?crFTwioTa``JB-E<$2z%ZC%0}4>Nf~>Xx2>jld88Ene z%{X?|UN`fTp_B{iYd5YAPs!esnsxG7 zc=h^3XBO=lPaQs0e(#gbkkEiQ z!oXH|Y!Gajkca-QCkrCz2!p{x9-AtCQ(ehmycT7A{m1>Qdc<<%{j0OkCQ$eJZI3+Z zlZp^$ZNeraL(M^>Ti14HEh=`$)rP3~;j8Q7#`_iYU{L$^jed3)U-D8gJcN8Wl*}W8 zY&bNc6GWc5{O_``31jj+{U+dS01QpAa2PTP$vUYZy6{zuc!0flF+R(lZu00_C!aW| zJO;1ATddy}ZIvN|VTn(Bq0vnd{DC?JojE98i=ROk=`qjwD3LDRVoP0D=g(9-vfyWb zp?mnn^X_8Lcl;y&LbyuWI%HiATCh0yiF~*O?UWY}I#^dPj@V@oNAl=`;(wgTkG7jCfl+~yk=B8 z1V({@IWUfTs58j#cw(^s@oWb4>6Id<;s@2iIPXCbLLmA#`_h4c>Xak9b-coRI68&E zuXUGsS8%>C*oFdCFqA9E90Pzr)myj+ZrDO7rS_T@8piic2iUJ?4{xd}-e=|%CeAjj zbYPD)i(WfyAtX`)IN?;T9nSLbmU8rQOlA_dlHeLvK5)gFg=Zfz5E4JoM{Q7JAK63? zqI=V7BO(asvjBX6x@?>(s6UTxLil@w19phtqF3$7>Z!Z(t2<2sz28epQG(w;oV~n{ zuD;WHzrP~AXN_)t!ypKj-TA-3N7K2)Jn9?`22y>jSxolPPeSrjx{59;?D7LLIJfs) z9~~bM{|MTkzf`8wKPS;WC#2ebNvv7Q+WsOBK?}f% zTJY~9{wzcqC^;pj3USEuc^6stMK6iDjLMA4C2amX10b~WOsdDNXIygfF_z??~O>Yu2fPa8oIrl3zW z9^c#=Qg=kB{>tq1_qR)M5nctk1xH|m#J!i1{|p1-{plRO#p3TZ6^7Us`F_+h=ws%D zq4_$@d{v)=BUzO~q_DH->#RU$M{7@H;k2^Hge4U-RLQZS63VmqxoWO5e|7Jm=vu-<(<$YZNP?C;%Xc{;}rNw?TDFmGl?HlN%aA43#Y!ML(KI0!qm4^CKxu z55>4VFN3(lBJ@DwnXAZu76I`A$B3Vr@%Eb9dGhS(!f3kSjJp^U`Sl>w#rFtREHqNS zp=re`i>$>8wS=23~IH6xf5iF<~w@rx(5A=tm`b>|a`@=BUlcbN607=qMW)G74%7 z4gB!wA-pLhb@ToY$6h!zyjvpE4cFk*ro)xrdX5VK7M3d?2}>t0ZNrgmcrXSOg&DvY z0l()8>YFf>Z6{irl*8kXBJY;Tg?|KsbT*ru=kYRocSHviFS(??4=G+omWY|pv0~- zDda0&x>-v00U@k%cR&l?o7VN2jxgT!cu3>L)Ddlegpm1{lxp>=%(0 z>)^)@on@wv{1WE!v7r%$4Ep{abc}$cY$fwMU$onlvO7WiR1qv=`8g^x=+cJR<88Y6 zE&$_ahR44boi~MKm}`@5%B%??YBNfsKxq%?I1+4SyWf+)R%&rL3*`|~{SXIX*GLTb zgFh~vN#!T8NHe8byy87X;Slm_6Q_z0T?BlVBI8ERS8BN0g!;a$5YiF5H^wnn*i=D; zi${1Y=Mf8`@u_Pi;PiWZ2x(8K@Q*rKfn>ZXJBo?!%1~L^i}F&_tGq4xGIZ?lE1dcI zr2MWXgy~~?5I(JVp@dOxI3nht(7QEs`uL{m048YNXfLV7xu;*Z1{xy#0~)ySmfq%eO=E0c7yI zkm)(&ZH*>yu%-3<=#G;9Qa=zZJr)M0$nG`)QK$J+GQ;)kbKUKUsW zjSDs`=OGJ#+2MUa)vG@{a%1%+AYs#K$=Ub0#i4En8#L@AF(3fm?K~s90G>U3kWas= zJ_X&3*qW)5`XiuHMiQD(NxcLSn#AY8~3byNT^)C zO5AR={gn}>Hx13I|2OA_`Bi#YryI%jj4#uvWV`S9B^_#fOp%XkP(CpXCA1TTiSedh*GC!60!Vm+3Jv&il+(t!Si}Mzy zA$g9qp3nQm&H-F|5Bm&1vW*7k8F$3qSY>X9eRDyF*VRy}z6fpC&v9XNR8PE%op--i zRc9}SvdiA^i3CwE-Fjyjx))%Q!l9~tRi4lJu+NA{@Zg_{|Lq)*Uds~3@M zv$A@=c;fY_NDt8VN|l(q5L@HM)~z(l=mLA+3Vg-g8F^!~XmbypQgf!2U36Yzs(2!^ zCeMI0huljCyd2~1wSP0DE^iU1@+Z)<>awb&(xR()Nq|QgwIrOn;EWY=PB0ng7kZ1& z!kT}-`|c8I>{`dsW5|H}T$Fto=ukhFjBE-EnEq54q+V<%t|Kkr;@u$UzZh2E zfR9_wA$`~<+}-G=SC#@V3zqj}Cxs0F&fVi2#(!KJ%HhSYN0K6b!xNVVd&2LE z31EIBrb8W4-z}iX@QX?co7LF;NI`ySNpB%&@7LdO9UzA%S2!XeEFeCnFsQKj7}e9j zvSI;iEEhS;R}NWlHqr?(PrQJWG+hni_?{MY*tiWja_X03Wu(btf*oWG$S8{ z1l|(U!VGN3;VBj_W)%m&Eqc{k?n*-FrzF7=9^XV^C+pOA)$}VUw{Udv4vI(UD9EvG z7%brI|5f!gtPVyrCUar)QysML&FTX)6UYMh_3M8_=>0;;0xwp)=vh(GHFh~E$kogl zEKu!5R~xPYx_G^E;89@#sv*Te+C{H!%Uu@;J)0buNk|}nOmR?i(JRJscO2Fv4XzLl z$nUdaD6%2@B0B9dj>n2RIhSlBD3Cv+IEc3Bm2SD41)+z?fQeyVWT%DkS6&;Bj=1Ur z^~1?#J->-2q=h+Y?1smKiR+W=$@ooo+VhE$*Se!4M*6^&2r?(nZ{4u?oL63Bjo$qeV@2I88R??P z=uSxsqpZB98fCcr%3#4{-#zOx?rQ*C2`9VsH2#YXp+A!YV+(UU{l|*xtG+7@+r$}W z5U0K0)V1xAjV|7O$%s5FM22mqp|w8^*X+7@@gxTuxw{>oSW)FMU7bcpOoV`szquYfVE9W3vy32x zp@u4#oUqA&iDKL|u0M#sTP-oGr*m9Rs z5ZE3;rUnCbbV}H1<#k3pxWYNv8J<5KhR=QaZAN?1*@kOlRlLsP)5rx%amcxOe`W`It-NZT=Wt;bjxrcWkMCx*iDN^G#UU(aV~nLS*~t6C;uYnH$7eP!em1A z*VtW(2lwsM7h&6XZ(N^#&0A~5Bp-uO8LSsJ`;*9M^Cy^IIQ#COF5@{c_i^~&#$CoE z1%V_XWKOQ%y35C-nyO$fTsZ{Zhibq5!(OW}P!r&^e9buT;q2ERU zJ4%QMY>Wa2M{UQEVZ?VxZ-Ha~uf%dUG?yMj4jkwqS|~ID98`xfpJfZSYw{JUz292H zHMc6BxGY#rKaP;~fOt6`LN zx`=gv)DqhPr2OKj@sjyxG2j5pJ<&Tse9EmXf#-5zG}G}GST5q9eJojE?K6LvWqONP z{>eZ3SlZ0ud44%F;1=@${+Z~8#y|2Y3br1lqN@S^=;0~Yjq~2J6ImqcVhF4Jr9t`# z`?uQB+nKr#&I_|x;%gk$AXZ6a)6j9z8Ok%S2fbL%YcADzd@)mf$+5;6i4TTII@M@= z2~*?l@%S0Sv(i1f;TNX)h2OwiWe=WLev$C}p#b2lWyL(YezfV7YDwUiiiOGFq_>(L zj6KPDvUOi<7OH>O9krZM_=t&QY>ds6^jRJ*7_A`Q3V6`GioNjnq?pKDekES;{mpQT zeWv;RUsv2)l8Ng3RBd5GJ?ruA*{55e2cduHYr6PsZMj+#=&0$;@cBbmeD82=MO{7l z5$H_rnW!tecPK#Kb6;R#y`9|++mTf){g2$kPV0hy`{nW58dm4Wwv@NTKeE_+9t)xE zVz)SsoMPF3(@h8RO8T~E7C`N0M@}8+x55{+J;7Zv zPCFb6IUTIGG#4yAXI!;PKP~Tp!e`&#Zsy!ys=Nf&+cklIe&OE2Y{QUA;H@-c zP^gP&-Shk_sgzHXXg7>u5z)_k9_$j!=;v2W;t= zw+@PT{_KTpMot=pp4E0OYlQTXgUwxP=6Y@2REQ=JA%*skmis8%G2&S#+Fk7S-xkD%=vqobgU+q*{Up z$R^R;Px9{wj1RAp$NZ@_CjJ*j;#$raVhWS_!6Ku5@uxyt$qE^}yD#!s63Mu0)^h~}*} zNKPC7pkgA%fz#Sfj3!aee0QXatOE{_HP0nH0NM1azrHtkUbtv#>rq{je)bL%9CXuh zB|r?L9Ft2H%pqBq4c^8@7)Hd*nPwr)L_&_V--%b22}M4_NXX@sIp zkbmStK|zT^R^I?rqEzl_`Tyvw4)oU-x{!F=4#aRj@jKPsG-~T~yn7(HJIa49&_z`F zLqNa>)b^t6u9Ww@#KI65`HMC$IUZ;?I%lK)ESRqD{x*3VbIVj=^8UjsJdo>{u4-v2 zC&Cd^O*%G&pbl2qlx*p#gP3nRMo19Ar_-aDq`xbEK8g2wab#k+(Iai0@YiU9H8kGa zwc@0Lm`4_Stx>=QB-~Ttio% z0I+^j@t<9owA4xzaLnFkAoh@t-V|B>jWg9j=hw;tR!`FN8efZrV{XnJq(IQD`6=vA zSN0q8ikpAr3)*8l5(pWgW6)KhKlbvi(D>ni%EWlezeH>Tx&b0d)KrIOp`AJJAzo?Be zl)C#g@b!$}W4}jr5?DSImslmKpt`-7t;9TED1ATG%@+k!wO+WPOUhl#&COMy|0_k0 z*TIe#?Ul5#$^RNS`q%MC6gEe#f_TPtK!;J)evgk=qNm(+=o2e|*9n`UF1^fwWpdt- zwf>4$Ll9^X5}JSefxAPZxwtmmIIQ%otc0T+aTAo36jm=(xQ1@_O@f7IaZewZPon z&?J5EoOk^9>{fLq$hYC7pyRUH=+r%0jfGX^Y(Kr!905go@KTYA)YZmS>kQd*bW@Q1 zi_QKgNxjv5``dT~5d%^g`D$yS_hkJ32F>%kiJ|zGpJB?gaPc*HoWjj!A^$C-1NwCr zMmO)Y6a8VsBxcXGsC~3Z7+#+&=*U4hYnLHCUY+7C(FsFp*=tu|376-~Ogsv>hMypM zf2i^*pz`9Ov3HyDH15{Zcz=Dgwqi}6%46oXIj7+;VH9EE0?CJdw!q`Xpw zB@~JCF05OBJwIATr$WX6*gaSfqP5Mro{_Cm&3yP1opx#9j+JnJ_pLB`+z+lkr_URz zA9(YRfGY@rhG{e(A1~~mTQ}PAg;M~>N;eleUYMKzUTz|F=}#HL?If1W+pMFOLoC{waya&;Em_XZrgzWE}NQFc>1q_Fdt|VJ3e=Lz0Dfy2yGbeE)bBy>bZw z)clCaSOy7zyk_9NGq+=@r7ymA0^|@KjPB#~up23(1zgfKgpe`Q0gs!{yY@gtDe@H;s!K=y82c&nL+Z z%NJP#sB8JXowujkPGVHw#Fi?d_i;4IXm(;E{4xSsGp*>fIn48wc3-E)+@{uigfQt` zPoGKvgxr3rzlc*vj%!Gfg|Cj)oz`WNRv5;|+4J9H<}d&%Fa0^8(t^53Gibg*g{<^Y&)t zubszcLlf^zvk4`$<1$Z&?4Vlo9+I{}qb2xb#Bl;_l78*5M4l_Q`}$fosd*XC+ajhv>sHtI_{RX0Ic&U8xpb>^ z-T!4YG`)BEYH$r3U?b^fJgq=K{w$D8&WO8i3lSUh5qdW7e-==sV7%V3g$$#bv&V{u zvBgS70{Mi=PF=0o6U5`#VqGGE5E1(>aYX3Ek(ZSLf|xSx@tnT9I6PFWK|ay?`iU!g zLEaK92kmJ<9cw%a%#?vEZ6Nc*f-KF+Cs{Wjaz}G3Sc=6#TIQn18ppyjWw=Tk4D%77 zIdj0V#`dVpVfoUA0~(N)8&4zK2`%Fsu^q(B4-V2+q9qD-Ck_-@z}1@fC@u1!vzK;b z5tyWv`+_HwJn}rN)xS#Ok6}QRfS1yq%BMTM(7%f3PhmiGj+auI!3QlFTP4ejV2dq_ z1}dwNO^U&@#jDDv%bpC=e_;aiV;YDQ^CnumeCCcuRb>w+muYZ+1FPtqlF$P$wO$I`36px#cOaE#1lOBr|d^cGa+#>Nz z-^kX<%BW##2ca7iCddnAi=~VOVi=H}-Zk|LdiptLL+<&33eK`iGwVBWW7 z4)>Kch|58l<|>k`HQc$QYn3ctxsmmmif@&+R6Q)M@}MmAj@M(z|lh$?^ZosJt# zBO4wsBW{)*L}g5v+yoZon>b*%4H-z8-F$stG?Fcr77XOGBRfq-58@v*fQ4NH420O* z7sx}`8xCQ*>$#)DR4iZ5q|CwJ-(uKe>%hQV2QrX&G+KE;Fw(}3ufM}T_xStbj|YD9Agc}#GY7kxN~X*Djne-W0rTS;?B?kuSzqsQN3*I~uHVC( zt9Yc-P8Kg&;@4WNp2CBQZ^#>=Z0K5GWiY_ZOXz|v5% z(^XM(6;EB7$sHyuJo%y#A5;T1!RjRIIZ~eJd38(oUJ`vZVNjxV!$+QIIE~l0a7aaN z3k>aeSu&ZG4L*GTK^JC(tqj&;ly~$N^)G2by*EgWY%Fq&bKLfj#)y6&s5ia=ktYNc8tDJ9?Z}7QC}&y4d%8KPc*rvWu6rDX2!?LfG=6OIC)VYzqsFL z;5RG+?#aNZ6te0Dct}RBy_ErdnlkN=MSYvZyO<_i1ELCs6fgFr-t7;dgd40zHZ*0% zIrAU06p44)O{fM$mW(O-y!_BHlKOPvoTl_Uj1U#Zq;EeaWxzo#Yrm6QAU0PnJkcpy zmg@~n^mrvT#$$Uu0FP~ktYh==k-Q)^qgwA&H3VEW@bu7=NpP zhViO7tUn<_k!QrM48j_fY15YUV-xR^ZeVlf&4AJ6mpufO4F@W?!G%q5Lq-j#J>=(@ z@YdO^A&7UyA#2#Vx?$B64kA4h3~@&e#mE4LwUdEJ8)pQOo1lFd7sia&hxU-#X_wbY zq12WLTlI6EXh%KEo?r;w%r=ByCNwJpc-DQwyF%0+aYQk0xXc^nmnCyhJ;OXEyiM_Q zS(9zUti1F^Pa5+b*%;~?Z_R#6$~T}~j0V*=qd6_Tae+4G53o$pf3>))srvxyAwF(s z&#MrXB_jiA_-?qPnd2%7s+!(n`-)fG4)O2pLt~g8O&1Ge#P@{8R@L! z=Q!;!vte%`64fSNzanI;uKkbq8nR@-HN7se5W9;j!}i6m^|rjxKqG&@M=fJ} z*Gt$;>YS$Eak%rKGDunLf??5(H@d>efA5OdT*XBm28r9Rcr|S!hG59(S`rKr{aM2w zY8zHf5FuF?1&P+U5xmiS#+D9}&~Fb^RtEoely^v$^`+5+`9~>XDIq==n7VgTJ^YRU z>9~L)YGpEO*t~Y*ffkf6LS{hpTPOu(mS10WAOyf_ULS)|k1RHQZa<@RbIX^g?W9Aakxv3)1^2WQ+;fa8;VI;tx51hI_In@~RnWKbpx>Y_<=7#JP3JSVwe4%b+ zQ}toIp09rB88bZiU!ot(2lCyYuyQSbICX!puri2yQ2x@htk0C#GDw-g7E4_Kq<=bT zc_;{O8SzIjASw_~5xnEq-7^bnnaG1F3n%~*{5z=@8#6oDC0$|)L+3BD_zt2Km z6kF_C0TAJx420CU;lV^Rsfiv0Jb|-?lrO`9LPbao(&yonHN`($vda*4X(d`$@%7U$ zAuW#HEW?9JL1h=fivV3|Yva;s$XUlV0 zD3K?OLK3TUXB+BC&Cp=r370KIq8S;=@qlV& zpeVov{EY-&szlN6TF)A5)b=t@X2Cvrn850P{sqhQN|Gpy%xFa%BGjNd5psj{XIy1X zX(@mATDtoCE;ZUoO~(+RqU!%{qJ+<8B-6`;5mU4)f9YSV_6r}0!!GAWXJoc5>TQjK zlw25F8Q7a~0bkL<{Dr*r5u^EX>1EnTDXEjc^hXo#8bq)S(kIB2HBF=~*I!x9DLoJA zCN-l%fiW(Xbtj&1(25IKu3tPiPhO=YJEgMnzYvdQQ_8|-WWFmBukMD^H|s=)jSt!& zeVXKA0g&bt6XWHb))#2|&@8~ROCkCWdZ$=iQF=L&es<*RoPMvHbLcIad-bUx zf5kn2IJYIGX?r)S{BvHe05RQysCI-X4qfktKEjjYmBeKtZs#I@I9}*?9{Wper5AC_ zO!w79aGGOQ?zoCcX>w;>@dcCcw-x-X6!AaiRj%oH{oPLk01BO5>Y767W^oAz`EEK4 zFk%sFH+&9NR6FmN?0<}I=CEHkgqtS2Lq6k)ot@s3re*LG@M*f%)ro>U8gnwONEfBQ zM)Y^5`u8f{1FJtqMv!@CmLGmBDgnN1Y6nvpYb^^V>K|U2YssE`jbSr4igfeuu{ubNdQkwjX#^CbBshP@a_(D>_&!&3P(Z=;p|aFz?> z!f|a_x~k`&B}^-dO*jMEoT#WIm8eGM5483O8Xl6Lt?+*$TH|VHTV*W0yE-k^lywP5 zW3BUUJ_ugk*q=SGyUvI`ca?n6eMM`8=(A6NRJXxg4&RYljyrRHLW$gtQwW-O&8O86t ze@dh>Gn0WM$(ntUvGv9D7ufysw2zH2jNIr_bXD1swTbCKvbZp-Zsc!0eeP z0Lp1SDyY;yNJ-t3-z-^3Nk_}f#@aLCBJC0QV}QR$jn0UW$(6_{H`~WF_C#uvE;UDF zz4xG!|60hj#0ucHFiW%c?BqN%M4}#xGskTG_;?~YQE~U&**PM^h1hs_BG2SjZpru1 zw}s-ZsKwU>2rWDj5sP|rEkXpOrU}C-x+nGCoJ||lseVk|pbgY^Jb8<)?{{DlP7+=P z3SI|pQK3Fq3IiEc>BQzaNr!}5nihg}nF}XLtFDlKV!N%SKFiSzPEpIqIag_lob(>1 zTmp{n(=h(?CFnGM@bTNnQY%{Cu6nEZ+`NsRDx4cinI>hx*~Ytt?@u^DTcFerm}4ip zBi}=Loa1~AS)zj5SX7WM%1qQGe{%ohE0aGe^um3k5FfIPk0lmP6M~En`|_LhB|>vq zW9=41vq+4gqum|}{Aol^9TDL-JYq;cd7(VZp&rUXbnSq*iR+E)`T5Pwa|cqaHyTZ<6mwev$T@%E5U+mATa~+x;z`{! zl*dWkD!*T4Z_7mEPpO?0c1New;j0a1vOtdZ1<(<1nFZa+;|K?Im^e^sRD7(7rZWWh zAGK2P(d5zz69(>w3-cD-@JY1rKiuIJIEYoVi`$)0X8!~3o84Sd*g&Emt5MSlc~^EM z(Ni@cy10EeI+efq2y$?J{_&5B2H^F>HN`RaQ*QQDcnTg7jw78==I=i?t|h36H6Pd| z9i+p9%<*4C@EIPy?=u=W1qg@q_o2dN+KEPtX^q)KmHks1MKm^m^K)L?*2URn+QoKg z^5=?vLl{B0w_VOh`>8sq+_#;w7Qa|Eih72M`eed&Bc&tT;*d=6uqbf}J6S271tSKV z^BtXR_5ZL$HmUWs!mpcVI8{awK4I7Z){?n+Tf zwVvx}eq%_p&7RO0N~$BHsj^0R5=gT(OrxctS?<3@*nq9@Szc!H`O%X-gRM1oeu;w| z+p)^Qzr2543DV5dA8Jc{J2R+FPgyQa_!qsx@YI>jBN6M9Xo$s#|A`$HP3KE>u$OR6 z>dFmV6{A;t7nV2Ecac4{F^xoI>IFH0SPU!!yhhB$qe^~_7wVr9qi{mE-BMl~9^qFB z#|qvO<&!S^gi)vX^U@XuWmH;ve-_XN17`S=;kgWAbU8SWOR@M0+2~ zh*bBfN(eDGtnFmXfs(~DqbL=mxvxeGZt)>)vNW`pH_XG&=tCjS*lBCDoUiPN{+J7!_o%=mZlF<^*-SQC*?X@_ zLC&j1@|VkXVEh!D!UjYpQTs0yW*G)gF>*^HDFRQPbWRXO8P>A!S}G?>)iK4toYWi? zY-lSKS83dKX$EY|%e~Cp+S=h4Vtex(B4Ckegi$>A_Js0w{4#F4iRW1$wr(nUb}7AH zwMuxb2oY9p=FQP+9S!l{uQN-oo4q;sU3t>NzRgc>6GhEbr2Z?TPuC|_h$0Q?!1=%I z1{7!brS_h6s-;!O_m2(y9lqKu-FH!%+rsfa!SL>i#6Qe`^l890QLWTk4$5NwPW)-gp;jY3;x`9(4on#|)Uq^pSz z%r08vxcC>$5b>NC^`)-JY_HtKwL@(h&{nOnob?{s3cr!H7&cvas)5I0;M2ieBzu!% zNH<<2ZOHGh@HoADrj~u^%D5!v)_xgCd%>Nc#lsWvoOGJSv{#4B%**Ij#L(U9d1Oih zK&K}U)$DiEh3XyL?V+xkzLZERZpeffk@?6Ix_CDYKZi0Hd{nRS^g4<#ZU9!B_6 zwMk(u5i80>^HgJE=rO|vnpq)Q1&gbfl|~7{t0wa^UK^(dk09lLxw;q^z#CDlen$pV z&tKh7k$DKy1)`|T!=XZ{#F~+)9E(7@YBm6tbY3}bSjevU_M5)o0N`6+M}*nrlGR!o{hSsi0DlJDE%Jm zr=uh8WJFGipdL!-HrC?FHrCeGGA?rL=@YKbwRP3pk`^%`=6LujIEhC;%FD5z}Oyz(z!z3dMtmfWTmzLg-av#v{{f#`~? zEw}4^41~<8=h6GV)D!qt*t*Q_UguixDs4q>YrORCMlrgntbBxd@hbD1I|3`QuUEO3 zF=z8LQhjbHb66oCD~`3AMoiGRGBl^}iweA6De#G)&s>>g*C}i6tbCs~#<~b5O}ydh0n0T-nu0B z+}f5nd;Z!AB1AsNOw!3VI=9eBsm?Fjy_b^TA-!~FO|hCKx&JTgD5WlM`m%5&Fej8< zX>eNqJ7$@(BeCq8X9H=GgMzzp?DW$?i-?D};Q}L?S zQ}IB8C(11PLvXgtS|?|klTxvBb+`n`qNQHzhRQCKq+JAK7$1u2@%F&xd1RX}|MXn#Sq|!>YXm=c#gp$dzV${M$^Pr{Dg2klWG0gHiH7yJLU|NgaRSqP z*ATaww%z*kp1Zj_efcl)=Q?~#w z$6RU3q{mXaq$w%;hjj6JFuk*bD(8zka`gO)?DQ)wx)p^tjD0aK_?<6_tkSeF& zlvu=9biv+n5%+TR{h4N9W{w%UY)6|_BhSWp(_@~@VR!W2hQMxk&LCXzvUgOo;* zlO|Py_hn(z6hR(kG#4(&lhQK-QuWaRua)T+^imJ-Gvb4g=N2qCzp}Zpm-?a(z`U_e z8`ZOsKafeaq_$MrvVzLBCHGl^K*qz)q^ViIrt{Y0LfY~EM>LU4_E#*Qn=5UGUcTkkP#Iwy4i1caocQ-!iV&rfMq09s$d_pfUfq4T$SsK`T z!OZu7#dHg+@Hqo9cN8I*&LWJ8{0v~LPkMUacfQpyMBeL*7gjuVPQanC{%G+Q1kW{U z`;Pq*Ca7S-T&SC5{AkvBELhYUdxle%fwU$)NUq)UT*`VbD7EU^cJGsl#S4s0Iw^VS zy(FLR3?N1}KOONDD~k~=XK)QkGzep9jI5ux;IK1br?oM9uLw7G)s8rmjKsbXdP32Q z3`Pjm3U&SOD`DrDqB6;joQZB=!CY2-6}X9=J7(V?ku<@V~GlF9RXorRMNG0y?)ck)-*T zL@kOP<$4vZf9S2e-I-Jdw9C;k^er?)srfIgVO_^u5B$jZ7p_sET=!WDXKEEF_b8oA zzsP1S<B7c(=K(XrD;0k8`<4h6#cuxt z#bYqM#P=<7tk11%f43;w*47bC(2bu6&qY#2ScE-K<{PAaeMhnO<7b|vi1p;H$c(Te;d^OP7AtZkhj9O4rE`rTcnGap! zpQGWK+INjz-{dHdxIYxvJ1|2Z^qzdF#!`CwfJGpdm1hz^IHVpAQbC0)qAu2}*v>-H zeVxf()rr*spxcJGW)@L(W(iA#@wcSIEtao!1_O)RF^i-@SoAD~S_!zuA36E;T)Jp*)};ppe4wWCS|h zU!60GIgh)CtW=0qNXOiJR`7LlM|R|FSwoDSH??V(;dA{=Cqm{sR~RM7#kW;B zw;eaXYH#Wj@eBN%qL33DQfp68rfWwhM z@ANt5bLpRh4&WyaD%sbmW81s~q&VuuN>^iqI@JVLGfu|IZraaXa?FKQGSskYiBrIk zqWZU-bqc$PW$;|)35~KsBQ!~P1s7U<~=@;BJwp}rF0^;xJ{TX6*)rnBo zM5bmDm+pFL8#P^TjAk?=wm2*c+m}&+bX&VyE>>!7rNsTbJwd&L{*%C7tkjoZdS#}; z3u5x&9?}vXF>gVa+s1q!7YE9KS!;EtLdYX{PX&|8d?p8#tdG4uN|>0C0bOsT@EkB* zc!f1%cEI+L()iXQ{}x3rg_)B8fZOo{GdE>Mrc#f6R&M>oU$;TB$<(mBbQ?SY!*a&B z;8{jdI1s*oLB5jLCJ(zsjYMAT6>bo_w&?$n_9<&x&VfOQeYq4@cdlIDFto}l6l zc;2a`c?Dp?uSf@;7b$6;1W@uQYQ@bXmo)bR);SbIW9Ju&n;QUmtcp!B^99Atr2rFv z;%@XjsJJ-;K*^wpA3gu6xH$%}PNOItH4k6h><`GJQgn@+pDk*32AGg5mPE|w6g67{ zC`l9-!sl&@nsou|1d8zC^SnjPKtLX@B5&CIdttLEzywRtGIV~ju$dD;iLRIuGM`b{ zObb{?RvZeRw=C=?1QZ}l+ywpQEbK-Bn88iZ1?{|)w7&qT#ACD)c1BCuF8~|DF`@B0 zDJAXOfc#G}P2e4~lJ-S_DPPQP+zx9=`zU~lI|e^)=b^a06R^P^BOSXlRNP(z$Y+Uh zjoC>kZZ8CwGR2fc?-&)grvRwvV-})zn2OsY02|aX@KHNAMeW{ze99Q!$eq5T_HO`F zvKY&Vo!FvwGXNEFOiK8UUQxRyU;{s9C~SwWs9hG2j}vnfx^r3BE~wd^ts5x7wML## zA0Yxy?s)R@^=hhRixt1p0jrlUIPG}r+^wF4vO5K9Lj}2dVIbSBUVsv!gnYU%5aCwO zO(_(fgpQ@BFgsjY)iS*_%HU(0_aTo#a_+56^npa>RYfkGalDe4XKo&4y~wde4DP{% z<*47vTKPS~2*{lO@&ggl7{pIhzwtr9u%84aKgc`euy{d$0l-K`=F}d&x|f;33tf0)s1M}95hLE6^j^W z-micpiY18&FUM=n20y}UP7BY-ZH@-d$ZO65|COsS9d3c7J{PWugew8AiBx?wh?ulK z2#%B692cI5*PIzXoqNjxse2&E2MJ={FM=UB9MpoLKOJ<3u`(86m*tU-MNqFKWh3OZKP76851X26r}F$r;q<}n(P$-p@q z(Z;|z8c|Q*IT%r*$(#V8Zy;h>0J+NR4q99~A3K5)2^gy;UTWR&m20Tk)@8xOX#23D z$Xi@l($u_Bee~_}+vN)IXr;HXq({p)qbIaUNirZKo}(-A@@%CAggmg+TbQz-k+`|E zVd}i+dAu^W(IOoTdw6Ur@qhV{(LJJt`1mAb9UwTUDCUUB)Q+ob7g8DWtVWl@bVeUT z{+j27^3=KLubZplRx<}>O_?L%7bd4cA8U-Rl`qnxjq4H(UaF_O1C*yvV#DW_sJ>^6`BS!1|` zXH69AEo<&w%b)4-l|NFWDd-5sq->rKh{T=Lc>7-mve9>u<3p-6XA6-r=>EGU`3_lf z?s%aZSb50CDqm)zRFsIz=#pDWLA$rH*Yfe7Ke%<(=7StE)KHzI)KmQmP71$OeMRbe zrN;>dR8`0^1z^%}YiMF4*0C-SY7&-k3Puv}iP@D=v$0nF^<7i-{H3*Cx-*%!lL8^= z?%1T>{AC&f5M~Ld>itY$$i)iF>U?smZ*0dzc&75R&4-gbTF=atad3-pk_&dyAiPr>;|#(%!(0|B)IrTMfmZOp|219M(ig`9(mR>?hx152=tD#%an z9w}rO}u|NG;eKf%uqduM28li-*J`+2U zP`o3nz<*L1^uYR|m$!tlFoB^!b~&%4u_JmEurgFUTkX;5Lv_GVy5K+XT)cLCg6kYD z=i&+_4GCFW4swFb~`MF)9)48?0h6_(OMDe{i`<5!J(04mR4-6CHpq7OXTzXFZ0-ys0i zd`@`g)zKj>x!wPAKc^*G(>?uzw>C9T3;lk?5!ZmaD8cCjncEpTx3`R&8#UX&J6wJpdUBHctd@%Nd5)t&Lq0XZSWg(>?9VC!pAisET()Y5M6|YT3U^O}|8(P2w1n^(TP+p|BS+Dv1 zrex5X4^`ZNdQJMV24#_HCFiSDafniHC6S0#l{94k^7?5n5lm8bnCG(#ZL4d zg;E{?6zf>pi?wPPXVet8v;N*mPCt7Et~+Dt!Z`_TD1C zHY!}&C;;dEt<#6%w$}U~pmgP>+j4hIyYul>t+#(pe>=cWD%zpgKu~pJO;br*Ys{+P zXkp6`F#O~%WPW}=ajsq3U#K)}Tha3k!+QIvK zieR*kQ%u=&8=(_f)?Up8Mhw2=X}@*IwTUQb3N3aM_q-v<@2Du*NTV*%7o8HW$6A)S z(8Z4YN5eGxY8XpkeP@kZ9_#7cztKioM^KBYXpE`F5^lbOwI|^Ix;QNvL;7{*lD;Nv ziH0=7w8YheIU@M0zXDydh4S107S77Iby{ugVo#9mw7>@Pc^{LR>v@HEwDC)@Y#;Zd zixUk=Yu8hpY9(gawqo^bCF!|iD<~C9*c1n7MP+SQRq^sD^d-j0h%?)K&IyAOVqNW+ zdSbvWfv;N)(dJXHWG-|dQ$MsVL`OUN31WVX+{7`{H)39qBQ|b_RJ&Mh0}M@l^T+ z#W6!tmIDd*r(e`ow@Nju+@W{6Pp0zvJ)B8YwD`!FCQdq7R(Jg$Q4$+J^4B$g(Em=@ ziDUOph&64W0&cw{XRKJy)0uH{Kj28x+W1&xMa}Zz4$bE?*3>*z`$@d{zBVn-FD{xX z>*9`*158+k0z)54P0r`5i%1H1!!G{YrNN3XuT2kWwj!TT!JSb@H{v&3v8qv9_2X}$ zPV;=fVF5|Xx5BZL>|nGc`pGU0zQAO#%b>s3h7j$c*Gu)&Shoc2)}_-9GJh(JwN;mP zCjc!rEEyIwgDXoeb=2iKBma=QwfD%7Th^ru45EB0i~}f0o5cM(+JOi8&wCWdR?h|A zduVs`r0LbXr;hQfyNLDI_p3xj z&f1;yEx`Jm+C%&5wp{SL`t0wPaU2TYkN`u^ZSS%*?~X7HRGRNEbyBY{s53Z<_WynC zQFLcQAg}Zi9Il6k^0&N=!31P1KKE9upTZrbBVm}7u~^z;F6uq3*Ecrtr@gKi7Tls% zeLLlGAbc)4#k9q|yw{Bh|MKv;pBQI*{r&hH!4`Ce7m_sf!q0Qr1ZDEKf5n(&`@aC9 zDP7hrVyEh+y0?J>30t3JKW)00tl5{*1Fuh#fmD4G?9})hF_*_<^e*gkYXs#kgfdJk z=$v10dqL;?_O~m4rDfM;Mif~`ikvpt?@8R2_(9?qR-mp7^xz!?Y}%Iig%#38qEp=k z(nF_b5a@n%SSalRO{E0+b3OUo-@M;yKW(bzS}ikU{i~Sat!0cIEQG&5y!id$#sAjB zi-nL}omNdY`zFO?FE^voz~yEDwk&pw=q9g=bZYW`r&{VswbYkZ>I*CQ(h6Xz)G?@+ zQmZ4%-PA@Y_m!2}o&vtIf(Xze6*Ti&>Y@qEyfzYa&CLpES0c4L5$sBg1ii0<4mPKj zHvX<#y?xS%6QQu{vlRFNwC#Gp3SbNAhA(=e7eT&E#UHd% zXPbe82339}TLC?li1zlMN(3++U53MbYveYtT6Dfz^bnHgkTtTAPpFuWnp0`uBLg+e z%26;Q5~;@%!HC32Lf@*Oy@=3W143gu6{Cau^jkYz6Q-$u6TBi42I|?Wg2rMUjx_+~wNpU9G((_YnnlVz zUj@~p7OQU#U|QsEpD3RjYML=uDR{(6uS@~MIk4Ueju=?)X%)KJOue5DHk-*gqzW3B zr+}WlUVX;+?DdiK4y(}dxc1`>YH!I`Ks;1@3 z)CBmoX%akQnhd`&O@T*EQ{lI!TKJu5BLwiu3@UP0Q~H-p7$!Jvt(%lesNC(vkcnPIM_$aJ54phdKh@ZfN4u7EOHGDu3DXDncHQAKjlq zFfr)zxXW*|B)YeIu^XDM5iy{k(N(ao3>X7 z;wMLrfGZZ{<%l+gem^?67KR05$mE_73i~3)kVBErBM0U|+26d*Pyj`li4(*4a4D&( z3I$Qm7AJnaeO`PgBQ|}40mFVMWD34*6ib6gPcVF5!5c&yN}k+8uh%D?C5iE4TI>iq z{ws{*#t27JxyK=*_-WN%XZ#q)c5E@`i58JXKJO4A5EKG2WvdCf7F#}|FUI{$AeQ8j ze`zx2#F7j82YTW(EGT%=13}Z;%?Wjb{6}Jd5lyk`d~5YtXr#~r=mj2ACdzqlGK z;)v3C#*SMI>k)7ajjim2LtG{t#noO%mD{N#S&bd19(dPU%q#xY)e;xhXv>4WFJ0YOBZa_O!xXQpFf3!Ky{3_qa_Q7A%osRFvisH3kLo1VDU#WrgJ31 zCk_j2#HyFW?TIl9^k@wS8VtpFs*afn!J)puV9lkV7&9!5?cx`+h{v=9G5S*|rbkeS zMPK2OkRZl%w-d1;_qAirCNRY96iR*Wm>z**RzCFjKGu30D3lj@+_62-mHFH!I9%wK z6*-oSJ;&03Z&<7?VxUk?S}IJ7_RaxOUxaBF9-iOI7|07 zi{ijK_8j8l4KlO)99~y!_W%he`Ek0^WbxFHKWD)05B?XJ%fH8G;j0lM;P#5K zof;BmJ$dSz`7`=`+ZS(ivGCsj5ojT zUZ)Se_K!Q`K7PU5PxQo{Z(MpKW7ZFL%Iw6JKTn z-Cmb(Xk3(t{}B42SyyZw1QU}iC%Fk&8kh02h}5s1$ng8qX7fm~J7;xt03NDm? zjD!mcgBZ%Mq{{5o4@Z>jjwwh_yc;GL?-1^)0Kbj3qInabsy+CuW789Lv^e^xl0f@~hXKzv2e92WkuUr816(EpMoJtbfLugV}j zw++AF6C3Uky9)m;=6xf#8}#bG&mj&%D+s4(5KESDE%f`_%CFt;9D+ELSbl}UV8C4- zLfgAev9-;xZzNd!%*rY1`s>@e^&U{rDZgXeet+)Qv*e!q{DOjOZ+vN#Nf$(JzqQ+6 zl`wMOSod4u&5b9hEE1jYuLj)<`VW_}UWrbdvgvXf_6d8|NAJ6#K`qjU;+_!0e zulp8d-+V{bzw&PF3`?($JnDH^gb;TP(g<rH zfNs;-vWs$TD$62Y!0(eC&{pX2*f2=61q4wD3=v$pi1H8Nl<%=Blt)qn1;=1pNEAey z!&V;f4HmpMr+Vz+cG?`|*wEIixT7t5P%!8hyXED11V_M|TcxPj>8m2@uLyZv4x9zU zBj!1ME}zyn%5yq8+B+8Hz-C2-=nt5Nrz<_(<$0yWEy7xpz*| z4U0#aPK2|&={}jAT~1x4euVcENpK8j*rLec@kqg2AqpTa{VDbjxGO7#fZ)2PS`t&2 z8AHv?tPdc4rngL-K50EgGBfKT`K5%v{j{k*2|!y}e_zNssDWhhJjxLrH6guG2=HEi zBK1FKI&Hm}dRMl{|~Xw=YWtrwFoa2GjXN&xU$ zZAmGp__MkyHtQ>wJZH7n&=-^CjIVl7h=3@24Sf#9Xf2bH#25Pg_xZ#i+>3(#BQBw! zAom7XKT;@H!-ViHY)1b>LEj~sYASmrD16jXQV#a)th=-E=v^ha+(^XW69H8->i^%|{bsvT%wer-US25B!%*<+&t^6+i)>yCc2SV6hLtnPmrC&k425N9t zc|$wE8<4_ZN_mQAnX-}E%7~g#=OUucM zOBkPDm+e8qN%n=VKFc5F^>|i}%@;^EZ0n{;i?o!LgDu98jB0CTie~z1^it$-4ib>7 z&!@7DvTUi9kXI#57r*Jkp$-A{0Z!2$@I9PsC}^$|Nos84|9C*PhF`38>BIREJhp}# zl+>Eq{Xbc$Zb5`oP=8R??_R;%2vXXekQLs?i&cWOI#4L@OE!)M1UxxeH{L0^zU{XV zE(`^ISvjy>R-cNBFjgAE%0~%qq>;TEjXciRrH|xCNgA=0De^dig2&@lJDw7+KNJ*U zE2+l}|Lhjz)RNEXJbb`FnbBz8UPF)JP0*cncXls_*IglqL2^eOO%#eko?s{-XfNvUNX{kvL~@%$Km0zXmi6A$kG%Mjrehfc}e+pMg^2=#D6Z(i?$MD+WSr0n!ca5Em;Q>q~)gbmS!pW$I zBtI8<+|I#$yiWByHgA%Pi;-vmhx>PbtLhi>0ri{ z=B;kUi9+=}dY6-Ge3+VOdX}dyHD7H8pxG^v;yfs58v7XuDTzHphZ{w3Q;Fr=5(!$X zB{o$e1~br30-i(n_em3;Vs`+)9<4U)Y$~;#O|9D5v?@~w^>PFTBRla|;{$Hk8e5TU zTguN!p^8ghpA*SyyoS|td@VnX?}6vQ#gWifgH-5y8Dm=jK$$3t0szzl0LlRX2#NkX zLH^sQZsi&(yVA$D*qy#7JsQXV(_oj<;T%hiI2uqHtD$l^J)BBk11hjXS*gz-LY^oE zjO`b61ce^Hz`a5csWze!OM*g`JPDYovxXN4@9D^7##d zjzX8KSn!KzFS^j{lHU(V6Se+6BnZ|-4_|LqQ8;(ojo1~WMIL4vp#rHrX6b75Z$fTt3wsSchc~HN#Pq;=ktpY+_1Tt* zt#KBySyYAm`EpSH{C1A{B-$XE(GuQS)}VRx2>7b-#T0yzT2_kUu_QN>$|8LzsX3U{ zlB(mutd@pOa^2&qc~9r-c)fYgY@(CguAy=%J)Fv)45&!zeO{+EKcP}5nLB{HC^Tn~ zk2Aw7jaNB>m!!*6R|iOugVX}il^VV!fo7 zkBZ3|t60y@4C<|BU}px9c2QM~bsTYTOhxJe^s>0dYkGh>UM8U$=>wC=ckBgbnEZhuZYT+QX1FkBf>c~C=TIzP85-eu5> z&3#VWi_PUpFDCiWZ2i=eVCTv{BrA0`^bB=Wi?CA@<&oeGN;Om?)dGGFzmP~J>8IF@(xbS& zl@03W_9n_xQX$dPcD6=BEzt<|5kfGM{@y zugA5MGR(+H%U~N+3*{%x0zkWIY$R^}E2_AQ_*(uIqC<%bSt8*HGkgdSEyaT0gw-*l@R(M;p6nz!n*qVnmAzmL!q+TJl7IV<*!Do4X#Yy#8Rdz z>K2LNvPfZ3L(i}*4`cCA)VbOVI*m^zRF?DhSqZQ4CJ4(E0nl!pDnzS#Gj3cxfe!By zFAU=82{^ez)h}?fHMso~5BvZ?`|M zSBB163Cno^c`}|15QTue+Y)|O^`ibXdV@^jO?fPk_~=2wof|~ac+nGj zAwV|O(Iy=-ilEr)B8_`PJ*8ux5IwwWN;IwHXHqAU!O3KBDjA$k24|AN*<|qhFaKZv z1%Oti294k0bH$Bu<6pNt6}+Ab-be*+rh@uZMB>tQ;H^|JA_c5S1y`;ED^tPSsbB)4 z2wRegH`*#j9;$A#>e*a>kSrkH<`1`!_c`WL50fnO`J6|CxTsx0A`kW zot}`3oG6IBeZdlZizbMDFM?6mBMOmscq$FN!-Gf%%J@bBtGj4_LHiCTx>=yZw4+l1Mqs;V0$^-UUD85Zm4~~Q@d(p zsB6(}C2}`Q3C0n@Z-iQG9Zx;c9IWF3yiOfA487_Nwa|K=nx6pH8`Q*nOaZO5Q2P?W zN=qc@2^I80Dm5kvypRf7wFv`lR6+AnsZXz6Ld{E+kDydg%@eoFYb!iQl=q>-7HZ>U zjQxNK??aDEQud+s$UjUV)bK(W-+I#QBI|BE$Vr!_(}e=Lqj#}BCeqkv zC2Zhp`HlR0Jih&qrrfF<R)hDUg|EXoxKKnnNF$EMwI5Njh<;3z??2ig%nC2u zz#&Y*L?Yy3^CrHQ-^{-+37K3P5d4k+x;WD}au0{w(=hdDG;^OMG(X@|r29RZrx;BpQ>KvF&ZolG@=>Qh6qM@Uba_T9 zgg)d`(T!VwH3<-)Say?%y`T`=!KcCmL`+>C>Y+E(66j7|xq~y08Z3Xln|yx#7V}Pg z0M-&_3=oy(^`PceTTt$T)bgWd5xWJucxqoWuuJL8RL}(#H0c@ynsg0-%m5KvXk#4O zKd%(tjXCT_eRHuO2Hjpsb3Ttt2t<@Sp_bdjQ|nT|9v;9X>690nQbdV&W-BE=;?V)$ zM?8Qn!>uu**h#h6UM%2VbV7?>sSWTt2RXcza$Y=n&`}Exnxho_7!5if^8nt6+S2W< zls2|10=_C+>qqk+Yi$eqY^@(Hc&v49Qk%u2=udbZrJelJMTC}Vf6Ari6ucGT{=v z0{?znDX^bMN4ooY0P{#|DbYP_4!JVX+wMzVNBDET^c|(`wx?WaPDSwk{#`}62Qy@0 zBS3X)v@IZkNDC0mCW4``q7+qD5mR0(f8TYBd237Ur5F`6X`P~o`j*sNEkS)t0JG6c z)OL5-8rg{Tcp}K%y+I+jttIsp54N=gNv#);qPMry;kLG1uOJbORE>Q9ihQN8;Lw6@ zL%3YZ1@Q3as(KR>H_D~yo%{j52egvsHN_r}G~T(?dkQc`zJ^AAP%okBsqWTLJ;)y- zm(b=>UK#Mm_hi61rT0}7L@?KGYM_TDpdRl>_OKD4rCTC<(g@I3TeY3wZrlk*fM#ur z?DCZEjet6T5YfFw5c*NYe8y8BrJ!y@+rbgsPJZWyijy2nIh3M034uJdQ)~YcDPkl9 zvtXyb2P(stS;B|m(tytua%x+gTx@LGsI9S-)?t>Sx$D@N@yy<(8qZ<=YaVt9d(mm| z7R310Pc);q?N&J);d{V3N`L*mYG42bbqvV&q3~UJvz6HU@Mb z<|i1XuCSHvS)=9(Yt&Y5zekniTmCy9_HMc)ZL-VAOIP_GyJU#EwvWQstMJ|0@)-Y} zd~;ood5K`qb)s4xz&7|oSd{xMJ$wN?!G>*(j}Fya+KrtaW_(0Ed6oHlehdEtIz6Px z>EV2xjQ11l7TrT6J;?KsrXpK-04;`ZKw)9@C%54}f%e7iop&k^R^d%qVNmj}RRc1r z^GiydYv>>Oy7c4xPtpaf)@4N=!Qn->eM13s^{~+EDh;@O0pu4rxSiqkrnJ2(y;u1Y zVF$x?O}uhDNJC{Ae?q+$*0RLw3rb`xWexbGZ~4iz(Q?8x2A(uM0Z*AWSx%eYx12F;vz#^Uv79q)xBP6{VL5Nw zWw~J5Yq@Cp#B#}W&~n-Ix#fy!zvUOxSC--QVao{mh~;tmsAVL5%rc7p!7`dYZW%+L zu+-3}EKks9EMw_&mT~lX%Xs>tWdeQKGLinp@+3WinM99do}wRzlj$1fX?ip>g&xa1 zLyu>s(i53y=}Am2J%xFWp3F?6r!v#&8O#j2mYGRUV`kCIndj+Q%xrodGl!nd)X{a! z3-pW3i}V6!F8vZSk6y&grx!B|=%vg;dKvQ){WkM5{RXp$u4i7MS1^m|cbFygA!aGP zmU)$4%`BtWGq2GbndS5*=5_jg<_&r)^CtZPQ%`@$yhZP1R?xedmGno<+w{lGDtaID z4*ePPF8u|wn*NemLmyz)(%&%a=&zae^ta3g`WUm3zR0{spI|o8KQNo=51$o z^kjA)J%#<0p2~hk&tX5OYuPX8>Fj=bCi^A*Jo^|y#9_G|i0 z_6WU%{f2&(JxagEeoMd3en-E{9-~*X-_vigKhUe#AL%vhae5v56TN{wL2qVH((kdS z=q>DNdK-I&-p-z-cd+N^gY3`rF7`aVhrK|5%3h@RvX|&j*vs_i>=k-H`wRUQJ3QeC zJ0jsQ`*^}pc4Wf$?5KqA*wG0;vSSj4!Z!+fN)gtkyRudD#Y&`QDWj=NWA0LBN7O=(w z)?2_v3m5~z6A)~&fcGt6n+5E#fbABr!vc0$z+MaZ!~zalz~>gQ-vYj}fWsDW!~%|5 zz%dK>!2*t3zzGXDWdUa_;G6}Vw}6WlaM=QWv49Z_7|DRgA*f-%XaXCKGhh}2<}qM41L_#?A_EpM;3WnuV!&bsEM>ql2E5IHHyBXQ zfE5gQhXIEeu$BR<8L*xK8yT>P0q-+lD+4}Yz=sUj$$;Gq_=o`?GhiPBK4ZWa4ET}( z2N>`T1HNX!w+uMOfQt+`!GIqaaGU|B7;uIG=NNFF0hbx@3j;>5U?dAhv)~05)UaSI z3nsB(JPRhWU@{A)uwW_+=CGia1=CqDlLgPSU@i;hvtS_$US`27EO?UzOIYwK3tnTv z>nwPe1uI$b77JFfU=0h_v0wuWHnZS87Hna`HWqAW!44K2WWg>L>|w#DEZEC}Pgw9d z3-+_%D;6AK!C@90Wx@9>_>KiXvS2s_Ke6B>3r@4(EDJ8N;0g;au;6DFjDq03mP)rt ztRaV31BE+9nB2qdbq(;kgn;t8&R`UH@B|qJ&T2=2N950L@_A-E^I3Tmunh3K96_PK zk60#ty(uqF)S`V)Dn0Nyv^Y7ZEKXF+mnvpSDti90Bo)9`_(6WaE&9ARzau!v<`$(@ zes0o@i$_sE^Ewh|`Rh+BZtzw!q80MF5BW%8@s$b&)7l6kHRtwB1t}hTZnFf<4bC4d=ku95}~;D;(I80=B1s-6>#P61dbHTxkvtrGWE1 z&{mvKIm>rBuT*?p3cNv@#>jIRb?#CkzY&AYN~Db%={}rF$aP5}bAhKN&WBRKMY6b4 zku{f$l_`?-=wJE)Em!>0SDq19pRhO_vT9KeA_Y&XyG7qls;1?bY&jkBZz=%w6FalLZO~epfGc3 zQKqlxpHc|KUPGVY<<;Fn9p-6*IVrF1V;@!>X8H2u9QUsotjI z&hp(;m5Q%Rk?N#=E+Zdx{mVjXi2QvV`Mxt!6oT2B8rgSfMIpGe`~_8%rRhWz@;T{x zz5O*S7Cb^wvzXGW@h6K6H;u|vhS7!8Q2BE+M*h6Ft$9?YJdE~7yPo*$Lo5^rIs#sl z_(kq7)uMn^>N7f%N&}-aWm8ZgJs^=4(}uZ{74{XX9BxEZwL=^ND|}%oJBhwRrdEXQ zfwqaJF~8197E;5=GK`SFt$8-59nDls7fxxNIUJP+=5S=7QZb`cOdZ#_q;(t_5LFTsaIt&%0{wgR>M4j~|3MCKCs<%m#ooSPzmOk= zNhJ8j_G#y8q?nYsx%pF>a##_oBt>z^;X%rg^X~+FezA|YGh9ch!DV|3!OoH4Cw@g^ z;kbWUZL)u5dv*7s&yrvU@h`NXQXx>$Kj1*IrZm@%9*Vf!lhvSv1}$t@JgTyfYm_}X zb93{jGpFDtH|iKDo7#!&OxxTX0E$vOhC7@12=|&_O~s5IcahFz8?}AwVC#D z%>WHE$|A~1rXQ?^nfjy)xFWeKTn$X5=Rw^_=6ug(PRoRrvVlT*v6O?NpKGvuV;3fN zM(G{$^P^GW6)Mv+XJkTHh8#=?7!c}`o1a?{olpmb(9FzPnK%ZQSM+&=D3sbqr&Od+ zdOmY@CQL2s=Neq(@Ro+kJ#KN(K%rc{G^H;BeT}1SX_78*v{42(Vi_^rfS;oxOlUsco zIorr>L?46mGZ$n^vn4&|F@mA&`4!Njv{%v=r%?u&8U?c?ZE4!8hL~p*%(ArC z(v}-yb}E?H)80sX(-3n=!PKX{mA1kVlYTqFtW0}5ZIvOWK*79|_HNp0go#j4Uj?%! zZEf1RwDoBe%Project "/src/Sample.sln" on node 1 (Restore target(s)). + 1>ValidateSolutionConfiguration: + Building solution configuration "Debug|Any CPU". + _GetAllRestoreProjectPathItems: + Determining projects to restore... + Restore: + X.509 certificate chain validation will use the fallback certificate bundle at '/usr/share/dotnet/sdk/9.0.320/trustedroots/codesignctl.pem'. + X.509 certificate chain validation will use the fallback certificate bundle at '/usr/share/dotnet/sdk/9.0.320/trustedroots/timestampctl.pem'. + Restoring packages for /src/Lib/Lib.csproj... + Restoring packages for /src/App/App.csproj... + Generating MSBuild file /src/Lib/obj/Lib.csproj.nuget.g.props. + Generating MSBuild file /src/Lib/obj/Lib.csproj.nuget.g.targets. + Writing assets file to disk. Path: /src/Lib/obj/project.assets.json + Generating MSBuild file /src/App/obj/App.csproj.nuget.g.props. + Generating MSBuild file /src/App/obj/App.csproj.nuget.g.targets. + Writing assets file to disk. Path: /src/App/obj/project.assets.json + Restored /src/App/App.csproj (in 45 ms). + Restored /src/Lib/Lib.csproj (in 41 ms). + + NuGet Config files used: + /root/.nuget/NuGet/NuGet.Config + + Feeds used: + https://api.nuget.org/v3/index.json + 1>Done Building Project "/src/Sample.sln" (Restore target(s)). + 1:2>Project "/src/Sample.sln" on node 1 (default targets). + 1>ValidateSolutionConfiguration: + Building solution configuration "Debug|Any CPU". + 1:2>Project "/src/Sample.sln" (1:2) is building "/src/Lib/Lib.csproj" (3:7) on node 2 (default targets). + 3>PrepareForBuild: + Creating directory "bin/Debug/net9.0/". + Creating directory "obj/Debug/net9.0/". + Creating directory "/src/Lib/obj/Debug/net9.0/refint/". + Creating directory "obj/Debug/net9.0/ref". + 1:2>Project "/src/Sample.sln" (1:2) is building "/src/App/App.csproj" (2:6) on node 1 (default targets). + 2>PrepareForBuild: + Creating directory "bin/Debug/net9.0/". + Creating directory "obj/Debug/net9.0/". + Creating directory "/src/App/obj/Debug/net9.0/refint/". + Creating directory "obj/Debug/net9.0/ref". + 3>_GenerateSourceLinkFile: + Source Link is empty, file 'obj/Debug/net9.0/Lib.sourcelink.json' does not exist. + CoreCompile: + /usr/share/dotnet/dotnet exec "/usr/share/dotnet/sdk/9.0.320/Roslyn/bincore/csc.dll" /noconfig /unsafe- /checked- /nowarn:1701,1702,1701,1702 /fullpaths /nostdlib+ /errorreport:prompt /warn:9 /define:TRACE;DEBUG;NET;NET9_0;NETCOREAPP;NET5_0_OR_GREATER;NET6_0_OR_GREATER;NET7_0_OR_GREATER;NET8_0_OR_GREATER;NET9_0_OR_GREATER;NETCOREAPP1_0_OR_GREATER;NETCOREAPP1_1_OR_GREATER;NETCOREAPP2_0_OR_GREATER;NETCOREAPP2_1_OR_GREATER;NETCOREAPP2_2_OR_GREATER;NETCOREAPP3_0_OR_GREATER;NETCOREAPP3_1_OR_GREATER /highentropyva+ /nullable:enable /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.CSharp.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.VisualBasic.Core.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.VisualBasic.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.Win32.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.Win32.Registry.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/mscorlib.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/netstandard.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.AppContext.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Buffers.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Concurrent.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Immutable.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.NonGeneric.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Specialized.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.Annotations.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.DataAnnotations.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.EventBasedAsync.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.TypeConverter.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Configuration.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Console.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Core.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.Common.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.DataSetExtensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Contracts.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Debug.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.DiagnosticSource.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.FileVersionInfo.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Process.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.StackTrace.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.TextWriterTraceListener.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Tools.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.TraceSource.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Tracing.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Drawing.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Drawing.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Dynamic.Runtime.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Formats.Asn1.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Formats.Tar.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.Calendars.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.Brotli.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.FileSystem.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.ZipFile.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.DriveInfo.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.Watcher.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.IsolatedStorage.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.MemoryMappedFiles.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipelines.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipes.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipes.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.UnmanagedMemoryStream.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Expressions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Parallel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Queryable.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Memory.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Http.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Http.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.HttpListener.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Mail.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.NameResolution.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.NetworkInformation.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Ping.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Quic.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Requests.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Security.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.ServicePoint.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Sockets.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebClient.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebHeaderCollection.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebProxy.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebSockets.Client.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebSockets.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Numerics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Numerics.Vectors.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ObjectModel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.DispatchProxy.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.ILGeneration.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.Lightweight.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Metadata.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.TypeExtensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.Reader.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.ResourceManager.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.Writer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.CompilerServices.Unsafe.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.CompilerServices.VisualC.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Handles.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.JavaScript.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.RuntimeInformation.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Intrinsics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Loader.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Numerics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Formatters.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Xml.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Claims.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Algorithms.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Cng.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Csp.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Encoding.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.OpenSsl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.X509Certificates.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Principal.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Principal.Windows.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.SecureString.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ServiceModel.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ServiceProcess.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.CodePages.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encodings.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.RegularExpressions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Channels.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Overlapped.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Dataflow.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Parallel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Thread.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.ThreadPool.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Timer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Transactions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Transactions.Local.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ValueTuple.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Web.HttpUtility.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Windows.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.Linq.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.ReaderWriter.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.Serialization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XmlDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XmlSerializer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XPath.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XPath.XDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/WindowsBase.dll /debug+ /debug:portable /filealign:512 /optimize- /out:obj/Debug/net9.0/Lib.dll /refout:obj/Debug/net9.0/refint/Lib.dll /target:library /warnaserror- /utf8output /deterministic+ /langversion:13.0 /analyzerconfig:/src/.editorconfig /analyzerconfig:obj/Debug/net9.0/Lib.GeneratedMSBuildEditorConfig.editorconfig /analyzerconfig:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/analyzers/build/config/analysislevel_9_default.globalconfig /analyzer:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll /analyzer:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.NetAnalyzers.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.ComInterfaceGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.JavaScript.JSImportGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.LibraryImportGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.SourceGeneration.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/System.Text.Json.SourceGeneration.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/System.Text.RegularExpressions.Generator.dll Hashing.cs "obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs" obj/Debug/net9.0/Lib.AssemblyInfo.cs /warnaserror+:NU1605,SYSLIB0011 + 3>/src/Lib/Hashing.cs(9,25): warning CA5351: Weak uses a broken cryptographic algorithm MD5 (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5351) [/src/Lib/Lib.csproj] + CompilerServer: server - server processed compilation - Lib (net9.0) + CopyFilesToOutputDirectory: + Copying file from "/src/Lib/obj/Debug/net9.0/Lib.dll" to "/src/Lib/bin/Debug/net9.0/Lib.dll". + Copying reference assembly from "obj/Debug/net9.0/refint/Lib.dll" to "/src/Lib/obj/Debug/net9.0/ref/Lib.dll". + Lib -> /src/Lib/bin/Debug/net9.0/Lib.dll + Copying file from "/src/Lib/obj/Debug/net9.0/Lib.pdb" to "/src/Lib/bin/Debug/net9.0/Lib.pdb". + 3>Done Building Project "/src/Lib/Lib.csproj" (default targets). + 2>_GenerateSourceLinkFile: + Source Link is empty, file 'obj/Debug/net9.0/App.sourcelink.json' does not exist. + CoreCompile: + /usr/share/dotnet/dotnet exec "/usr/share/dotnet/sdk/9.0.320/Roslyn/bincore/csc.dll" /noconfig /unsafe- /checked- /nowarn:1701,1702,1701,1702 /fullpaths /nostdlib+ /errorreport:prompt /warn:9 /define:TRACE;DEBUG;NET;NET9_0;NETCOREAPP;NET5_0_OR_GREATER;NET6_0_OR_GREATER;NET7_0_OR_GREATER;NET8_0_OR_GREATER;NET9_0_OR_GREATER;NETCOREAPP1_0_OR_GREATER;NETCOREAPP1_1_OR_GREATER;NETCOREAPP2_0_OR_GREATER;NETCOREAPP2_1_OR_GREATER;NETCOREAPP2_2_OR_GREATER;NETCOREAPP3_0_OR_GREATER;NETCOREAPP3_1_OR_GREATER /highentropyva+ /nullable:enable /reference:/src/Lib/obj/Debug/net9.0/ref/Lib.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.CSharp.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.VisualBasic.Core.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.VisualBasic.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.Win32.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/Microsoft.Win32.Registry.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/mscorlib.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/netstandard.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.AppContext.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Buffers.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Concurrent.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Immutable.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.NonGeneric.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Collections.Specialized.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.Annotations.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.DataAnnotations.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.EventBasedAsync.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ComponentModel.TypeConverter.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Configuration.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Console.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Core.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.Common.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.DataSetExtensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Data.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Contracts.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Debug.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.DiagnosticSource.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.FileVersionInfo.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Process.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.StackTrace.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.TextWriterTraceListener.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Tools.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.TraceSource.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Diagnostics.Tracing.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Drawing.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Drawing.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Dynamic.Runtime.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Formats.Asn1.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Formats.Tar.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.Calendars.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Globalization.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.Brotli.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.FileSystem.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Compression.ZipFile.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.DriveInfo.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.FileSystem.Watcher.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.IsolatedStorage.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.MemoryMappedFiles.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipelines.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipes.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.Pipes.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.IO.UnmanagedMemoryStream.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Expressions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Parallel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Linq.Queryable.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Memory.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Http.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Http.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.HttpListener.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Mail.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.NameResolution.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.NetworkInformation.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Ping.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Quic.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Requests.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Security.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.ServicePoint.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.Sockets.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebClient.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebHeaderCollection.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebProxy.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebSockets.Client.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Net.WebSockets.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Numerics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Numerics.Vectors.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ObjectModel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.DispatchProxy.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.ILGeneration.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Emit.Lightweight.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Metadata.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Reflection.TypeExtensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.Reader.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.ResourceManager.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Resources.Writer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.CompilerServices.Unsafe.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.CompilerServices.VisualC.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Handles.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.JavaScript.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.InteropServices.RuntimeInformation.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Intrinsics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Loader.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Numerics.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Formatters.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Runtime.Serialization.Xml.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.AccessControl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Claims.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Algorithms.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Cng.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Csp.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Encoding.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.OpenSsl.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.Primitives.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Cryptography.X509Certificates.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Principal.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.Principal.Windows.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Security.SecureString.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ServiceModel.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ServiceProcess.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.CodePages.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encoding.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Encodings.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.Json.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Text.RegularExpressions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Channels.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Overlapped.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Dataflow.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Extensions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Tasks.Parallel.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Thread.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.ThreadPool.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Threading.Timer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Transactions.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Transactions.Local.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.ValueTuple.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Web.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Web.HttpUtility.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Windows.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.Linq.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.ReaderWriter.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.Serialization.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XmlDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XmlSerializer.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XPath.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/System.Xml.XPath.XDocument.dll /reference:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/ref/net9.0/WindowsBase.dll /debug+ /debug:portable /filealign:512 /optimize- /out:obj/Debug/net9.0/App.dll /refout:obj/Debug/net9.0/refint/App.dll /target:exe /warnaserror- /utf8output /deterministic+ /langversion:13.0 /analyzerconfig:/src/.editorconfig /analyzerconfig:obj/Debug/net9.0/App.GeneratedMSBuildEditorConfig.editorconfig /analyzerconfig:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/analyzers/build/config/analysislevel_9_default.globalconfig /analyzer:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.CSharp.NetAnalyzers.dll /analyzer:/usr/share/dotnet/sdk/9.0.320/Sdks/Microsoft.NET.Sdk/targets/../analyzers/Microsoft.CodeAnalysis.NetAnalyzers.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.ComInterfaceGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.JavaScript.JSImportGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.LibraryImportGenerator.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/Microsoft.Interop.SourceGeneration.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/System.Text.Json.SourceGeneration.dll /analyzer:/usr/share/dotnet/packs/Microsoft.NETCore.App.Ref/9.0.22/analyzers/dotnet/cs/System.Text.RegularExpressions.Generator.dll Program.cs "obj/Debug/net9.0/.NETCoreApp,Version=v9.0.AssemblyAttributes.cs" obj/Debug/net9.0/App.AssemblyInfo.cs /warnaserror+:NU1605,SYSLIB0011 + 2>/src/App/Program.cs(4,12): warning CS8600: Converting null literal or possible null value to non-nullable type. [/src/App/App.csproj] + 2>/src/App/Program.cs(5,19): warning CS8602: Dereference of a possibly null reference. [/src/App/App.csproj] + 2>/src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj] + CompilerServer: server - server processed compilation - App (net9.0) + _CopyFilesMarkedCopyLocal: + Copying file from "/src/Lib/bin/Debug/net9.0/Lib.pdb" to "/src/App/bin/Debug/net9.0/Lib.pdb". + Copying file from "/src/Lib/bin/Debug/net9.0/Lib.dll" to "/src/App/bin/Debug/net9.0/Lib.dll". + Creating "/src/App/obj/Debug/net9.0/App.csproj.Up2Date" because "AlwaysCreate" was specified. + Touching "/src/App/obj/Debug/net9.0/App.csproj.Up2Date". + _CopyOutOfDateSourceItemsToOutputDirectory: + Copying file from "/src/App/obj/Debug/net9.0/apphost" to "/src/App/bin/Debug/net9.0/App". + CopyFilesToOutputDirectory: + Copying file from "/src/App/obj/Debug/net9.0/App.dll" to "/src/App/bin/Debug/net9.0/App.dll". + Copying reference assembly from "obj/Debug/net9.0/refint/App.dll" to "/src/App/obj/Debug/net9.0/ref/App.dll". + App -> /src/App/bin/Debug/net9.0/App.dll + Copying file from "/src/App/obj/Debug/net9.0/App.pdb" to "/src/App/bin/Debug/net9.0/App.pdb". + 2>Done Building Project "/src/App/App.csproj" (default targets). + 1>Done Building Project "/src/Sample.sln" (default targets). +Deferred Messages + + Detailed Build Summary + ====================== + + + ============================== Build Hierarchy (IDs represent configurations) ===================================================== + Id : Exclusive Time Total Time Path (Targets) + ----------------------------------------------------------------------------------------------------------------------------------- + 0 : 0.193s 0.480s /src/Sample.sln (Restore) + | 1 : 0.094s 0.094s /src/App/App.csproj (_IsProjectRestoreSupported) + | 2 : 0.145s 0.145s /src/Lib/Lib.csproj (_IsProjectRestoreSupported) + | 4 : 0.009s 0.009s /src/Lib/Lib.csproj (_GenerateRestoreProjectPathWalk) + | 3 : 0.004s 0.010s /src/App/App.csproj (_GenerateRestoreProjectPathWalk) + | . 4 : 0.000s 0.000s /src/Lib/Lib.csproj (_GenerateRestoreProjectPathWalk) + | 6 : 0.036s 0.036s /src/App/App.csproj (_GenerateRestoreGraphProjectEntry) + | 5 : 0.044s 0.044s /src/Lib/Lib.csproj (_GenerateRestoreGraphProjectEntry) + | 8 : 0.020s 0.020s /src/App/App.csproj (_GenerateProjectRestoreGraph) + . 7 : 0.020s 0.020s /src/Lib/Lib.csproj (_GenerateProjectRestoreGraph) + + ============================== Node Utilization (IDs represent configurations) ==================================================== + Timestamp: 1 2 Duration Cumulative + ----------------------------------------------------------------------------------------------------------------------------------- + 639287616012568408: 0 x 0.018s 0.018s + 639287616012745867: 1 x 0.068s 0.086s . + 639287616013424098: | 2 0.027s 0.112s + 639287616013689116: x | 0.119s 0.231s .. + 639287616014874759: 0 x 0.006s 0.236s + 639287616014932450: 3 4 0.003s 0.239s + 639287616014958322: x | 0.006s 0.245s + 639287616015019145: 3 x 0.002s 0.247s + 639287616015036614: 0 x 0.002s 0.249s + 639287616015059701: 6 5 0.036s 0.285s + 639287616015414960: x | 0.008s 0.293s + 639287616015497908: 0 x 0.001s 0.294s + 639287616015503473: 8 7 0.020s 0.313s + 639287616015700372: | x 0.000s 0.314s + 639287616015705287: 0 x 0.166s 0.480s ... + ----------------------------------------------------------------------------------------------------------------------------------- + Utilization: 72.3 55.1 Average Utilization: 63.7 + + Detailed Build Summary + ====================== + + + ============================== Build Hierarchy (IDs represent configurations) ===================================================== + Id : Exclusive Time Total Time Path (Targets) + ----------------------------------------------------------------------------------------------------------------------------------- + 9 : 0.003s 1.293s /src/Sample.sln () + | 11 : 1.106s 1.106s /src/Lib/Lib.csproj () + . 10 : 0.259s 1.291s /src/App/App.csproj () + | | 12 : 0.003s 0.003s /src/Lib/Lib.csproj (GetTargetFrameworks) + | | 13 : 0.001s 0.001s /src/Lib/Lib.csproj (GetNativeManifest) + | . 14 : 0.002s 0.002s /src/Lib/Lib.csproj (GetCopyToOutputDirectoryItems) + + ============================== Node Utilization (IDs represent configurations) ==================================================== + Timestamp: 1 2 Duration Cumulative + ----------------------------------------------------------------------------------------------------------------------------------- + 639287616012568408: 0 x 0.018s 0.018s + 639287616012745867: 1 x 0.068s 0.086s . + 639287616013424098: | 2 0.027s 0.112s + 639287616013689116: x | 0.119s 0.231s .. + 639287616014874759: 0 x 0.006s 0.236s + 639287616014932450: 3 4 0.003s 0.239s + 639287616014958322: x | 0.006s 0.245s + 639287616015019145: 3 x 0.002s 0.247s + 639287616015036614: 0 x 0.002s 0.249s + 639287616015059701: 6 5 0.036s 0.285s + 639287616015414960: x | 0.008s 0.293s + 639287616015497908: 0 x 0.001s 0.294s + 639287616015503473: 8 7 0.020s 0.313s + 639287616015700372: | x 0.000s 0.314s + 639287616015705287: 0 x 0.166s 0.480s ... + 639287616017483914: 9 x 0.002s 0.494s + 639287616017504833: 10 11 0.080s 0.573s . + 639287616018303047: x | 1.026s 1.600s .................... + 639287616028564226: x 12 0.004s 1.603s + 639287616028599628: 10 x 0.006s 1.609s + 639287616028661342: x 13 0.001s 1.610s + 639287616028671626: 10 x 0.143s 1.754s .. + 639287616030105598: x 14 0.002s 1.755s + 639287616030121837: 10 x 0.029s 1.784s + 639287616030413264: 9 x 0.001s 1.785s + ----------------------------------------------------------------------------------------------------------------------------------- + Utilization: 34.1 78.2 Average Utilization: 56.2 + +Build succeeded. + + "/src/Sample.sln" (default target) (1:2) -> + "/src/Lib/Lib.csproj" (default target) (3:7) -> + (CoreCompile target) -> + /src/Lib/Hashing.cs(9,25): warning CA5351: Weak uses a broken cryptographic algorithm MD5 (https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5351) [/src/Lib/Lib.csproj] + + + "/src/Sample.sln" (default target) (1:2) -> + "/src/App/App.csproj" (default target) (2:6) -> + /src/App/Program.cs(4,12): warning CS8600: Converting null literal or possible null value to non-nullable type. [/src/App/App.csproj] + /src/App/Program.cs(5,19): warning CS8602: Dereference of a possibly null reference. [/src/App/App.csproj] + /src/App/Program.cs(3,5): warning CS0168: The variable 'unused' is declared but never used [/src/App/App.csproj] + + 4 Warning(s) + 0 Error(s) + +Time Elapsed 00:00:01.84 diff --git a/pkg/attestation/crafter/materials/testdata/buildlog/sample.binlog b/pkg/attestation/crafter/materials/testdata/buildlog/sample.binlog new file mode 100644 index 0000000000000000000000000000000000000000..2590a5349fb555fb36a0cb834f5e7ccefe85c088 GIT binary patch literal 163968 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

gRL-p>>0 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 literal 0 HcmV?d00001 diff --git a/pkg/attestation/crafter/materials/testdata/buildlog/vcbuild.htm b/pkg/attestation/crafter/materials/testdata/buildlog/vcbuild.htm new file mode 100644 index 0000000000000000000000000000000000000000..d6eb953ea213aefad608cc5b8698aae5493f8f34 GIT binary patch literal 4188 zcmeH~Sx*yD6vxlAiQi#@kpOCo2og3EDWFCKRMZy|L+L`Lg*sC}jGtZo{m<>CGgT0I z;(<(NyUSVsXSu(AF515JtYd9^W_Rt5>A!BfRy+THW2?4p&n>Z+yti%39x?LPKG-Y! zXiGL?6+5y+sMP)Wh&5-{gRtwXjaj!4u?}~v9bq+C8$nv-Dk9FO_#9^os^k-XtU<`s?9)W zksT@T#J^+}eKq=8C4B9&zs^oc-{RS3MaI{6AAMj?`qC_;Q;XRe^Xu$y_U&ChrzUP? z_(hom_xM-TU;--Q+vdu!wxnob|0a@5=H@h0;~LBjbdqk8CT|(-{nI9^wt~-Bu#9Xn z3ZJTTUFb_JyIh^x8g%0IKh!3iOVPvoZr{9ZGq+&MvyOK2=(B}qj_eEndyHzN#&?IY zhkOV0Hn30=52e_v$F~rzmG9if!km!#!#=qspV*XZ9=M0NDdUBJy{miiG91d9gbj)UL9Hc5=oRJ{y) z@@oRk&_jr;`h$?0Re6=wJ02lLsy)y@7)ju9G_-0DsU>HAooYdlO*~@PpuQmEN_aPT z*4U>CEi09<&9L>xh}@}5tubeF^faalxP7}vUOq;0tj4SXP9#0n*;QdXR} zHtIg2+>+~znW|?N?aCNw@K<6q)I8~_N+=4)%txQ)W%=xUHKWncdj;I6Lld52#*sz# z95)$M4{mXT#V*Fpu=U31VglOX_a_a43@a7bX6Bc{<>|DJ*JUbh!>8x$S2FZ^SD0& zGjpsdAyo}}`MHoyWWz&vNk?@j!gB0drSCAkd~OQz=4tAzq0vJS8u1?V2;E^ql!#-? zqg{MMw^)U*@|pHX3fW8@(#@}_>R>aZZ)3sRK2PV4>(|lzIYX5}h)~57Vw7JMEyAX1 zM6uJ;`9kN=c`#G%=gCtjq@F3`d1Pnkys<;P#8YE9GpgR|yguCDMG6UL=7e}k;FWTT g!x*xS`&nm(@nVGzbN@#30O@h<@X32{o)2g7-<(#sL;wH) literal 0 HcmV?d00001 From 74cbef0b41e33ee18d88e0ff13cfe3507d1850aa Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 28 Oct 2026 10:02:56 +0100 Subject: [PATCH 2/3] fix(materials): address review of the BUILD_LOG material - List every tool of an archive in the chainloop.material.tools annotation, so an archive of MSBuild and Visual C++ logs names both. - Count the entries and bytes of the archives inside the material's archive against the material's limits, so sibling nested archives share one budget. - Read the MSBuild summary lines in all seven languages from MSBuild's own translations: the German, Korean and Traditional Chinese counts, and the missing succeeded and failed lines. - Require a banner, a start line, summary counts or a diagnostic: a result line alone, which other tools print too, is not a build. - Keep the escaped braces of .NET format strings. - Use constants for the strings that other tests repeat (goconst). Signed-off-by: Javier Rodriguez Chainloop-Trace-Sessions: 22fceba7-c836-4440-b83a-be86dfa26a91 --- .../crafter/materials/build_log.go | 10 +++ .../crafter/materials/build_log_test.go | 64 ++++++++++++++----- .../crafter/materials/buildlog/binlog.go | 8 ++- .../crafter/materials/buildlog/binlog_test.go | 15 +++++ .../crafter/materials/buildlog/buildlog.go | 40 +++++++++++- .../materials/buildlog/fidelity_test.go | 51 +++++++++++++++ .../materials/buildlog/localized_test.go | 49 ++++++++++++++ .../crafter/materials/buildlog/text.go | 29 ++++----- 8 files changed, 231 insertions(+), 35 deletions(-) diff --git a/pkg/attestation/crafter/materials/build_log.go b/pkg/attestation/crafter/materials/build_log.go index cd9385852..a26d0e72e 100644 --- a/pkg/attestation/crafter/materials/build_log.go +++ b/pkg/attestation/crafter/materials/build_log.go @@ -19,6 +19,7 @@ import ( "context" "errors" "fmt" + "slices" "strconv" schemaapi "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" @@ -89,6 +90,15 @@ func (i *BuildLogCrafter) injectAnnotations(m *api.Attestation_Material, report if report.Tool.Version != "" { m.Annotations[AnnotationToolVersionKey] = report.Tool.Version } + // An archive can hold the logs of several tools: list each of them. + var tools []Tool + for _, l := range report.Logs { + tool := Tool{Name: l.Tool.Name, Version: l.Tool.Version} + if !slices.Contains(tools, tool) { + tools = append(tools, tool) + } + } + SetToolsAnnotation(m, tools) m.Annotations[AnnotationBuildLogLogsCount] = strconv.Itoa(len(report.Logs)) m.Annotations[AnnotationBuildLogWarningsCount] = strconv.Itoa(report.Summary.Warnings) m.Annotations[AnnotationBuildLogErrorsCount] = strconv.Itoa(report.Summary.Errors) diff --git a/pkg/attestation/crafter/materials/build_log_test.go b/pkg/attestation/crafter/materials/build_log_test.go index 40c1f645a..37c5b4827 100644 --- a/pkg/attestation/crafter/materials/build_log_test.go +++ b/pkg/attestation/crafter/materials/build_log_test.go @@ -25,6 +25,7 @@ import ( contractAPI "github.com/chainloop-dev/chainloop/app/controlplane/api/workflowcontract/v1" "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/buildlog" "github.com/chainloop-dev/chainloop/pkg/casclient" mUploader "github.com/chainloop-dev/chainloop/pkg/casclient/mocks" "github.com/rs/zerolog" @@ -33,6 +34,12 @@ import ( "github.com/stretchr/testify/require" ) +// Values that other tests of the package repeat. +const ( + buildLogMissingFile = "no such file or directory" + buildLogOtherMaterial = "./testdata/sbom-spdx.json" +) + func TestNewBuildLogCrafter(t *testing.T) { testCases := []struct { name string @@ -40,11 +47,11 @@ func TestNewBuildLogCrafter(t *testing.T) { wantErr bool }{ { - name: "happy path", + name: "build log material", input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_BUILD_LOG}, }, { - name: "wrong type", + name: "other material type", input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_SARIF}, wantErr: true, }, @@ -77,6 +84,21 @@ func writeBuildLogZip(t *testing.T) string { return p } +// writeMixedBuildLogZip zips a binary log with a Visual C++ build log, as a +// product that builds with both tools does. +func writeMixedBuildLogZip(t *testing.T) string { + t.Helper() + entries := map[string][]byte{} + for _, n := range []string{"sample.binlog", "vcbuild.htm"} { + content, err := os.ReadFile(filepath.Join("./testdata/buildlog", n)) + require.NoError(t, err) + entries["logs/"+n] = content + } + p := filepath.Join(t.TempDir(), "logs.zip") + writeZip(t, p, entries) + return p +} + // writeOldBinlog writes a binary log header in a format version that predates // length-prefixed records. func writeOldBinlog(t *testing.T) string { @@ -99,13 +121,13 @@ func TestBuildLogCrafter_Craft(t *testing.T) { annotations map[string]string }{ { - name: "invalid path", + name: "missing file", filePath: func(*testing.T) string { return "./testdata/non-existing.binlog" }, - wantErr: "no such file or directory", + wantErr: buildLogMissingFile, }, { - name: "wrong content", - filePath: func(*testing.T) string { return "./testdata/sbom-spdx.json" }, + name: "not a build log", + filePath: func(*testing.T) string { return buildLogOtherMaterial }, wantErr: "does not look like a build log", }, { @@ -117,35 +139,43 @@ func TestBuildLogCrafter_Craft(t *testing.T) { name: "msbuild binary log", filePath: func(*testing.T) string { return "./testdata/buildlog/sample.binlog" }, annotations: map[string]string{ - "chainloop.material.tool.name": "msbuild", - "chainloop.material.build_log.logs.count": "1", - "chainloop.material.build_log.warnings.count": "4", - "chainloop.material.build_log.errors.count": "0", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationBuildLogLogsCount: "1", + materials.AnnotationBuildLogWarningsCount: "4", + materials.AnnotationBuildLogErrorsCount: "0", }, }, { name: "msbuild text log", filePath: func(*testing.T) string { return "./testdata/buildlog/msbuild.txt" }, annotations: map[string]string{ - "chainloop.material.tool.name": "msbuild", - "chainloop.material.build_log.warnings.count": "4", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationBuildLogWarningsCount: "4", }, }, { name: "visual c++ build log", filePath: func(*testing.T) string { return "./testdata/buildlog/vcbuild.htm" }, annotations: map[string]string{ - "chainloop.material.tool.name": "vcbuild", - "chainloop.material.build_log.warnings.count": "4", + materials.AnnotationToolNameKey: buildlog.ToolVCBuild, + materials.AnnotationBuildLogWarningsCount: "4", }, }, { name: "archive of binary logs", filePath: writeBuildLogZip, annotations: map[string]string{ - "chainloop.material.tool.name": "msbuild", - "chainloop.material.build_log.logs.count": "2", - "chainloop.material.build_log.warnings.count": "4", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationBuildLogLogsCount: "2", + materials.AnnotationBuildLogWarningsCount: "4", + }, + }, + { + name: "archive of binary and Visual C++ build logs", + filePath: writeMixedBuildLogZip, + annotations: map[string]string{ + materials.AnnotationToolsKey: `["msbuild","vcbuild"]`, + materials.AnnotationBuildLogLogsCount: "2", }, }, } diff --git a/pkg/attestation/crafter/materials/buildlog/binlog.go b/pkg/attestation/crafter/materials/buildlog/binlog.go index f5290aed4..566af253e 100644 --- a/pkg/attestation/crafter/materials/buildlog/binlog.go +++ b/pkg/attestation/crafter/materials/buildlog/binlog.go @@ -88,8 +88,9 @@ var ErrUnsupportedBinlog = errors.New("unsupported MSBuild binary log version") // gigabytes. const maxBinlogRecord = 64 << 20 -// formatArgRe matches a .NET composite-format placeholder such as {0} or {1,-8:N2}. -var formatArgRe = regexp.MustCompile(`\{(\d+)(?:,[^}:]*)?(?::[^}]*)?\}`) +// formatArgRe matches a .NET composite-format placeholder such as {0} or +// {1,-8:N2}, or an escaped brace, "{{" or "}}". +var formatArgRe = regexp.MustCompile(`\{\{|\}\}|\{(\d+)(?:,[^}:]*)?(?::[^}]*)?\}`) // isBinlog reports whether peeked bytes are the start of an MSBuild binary log: // a gzip stream whose first decompressed int32 is a plausible format version. @@ -401,6 +402,9 @@ func formatMessage(format string, arguments []string) string { return format } return formatArgRe.ReplaceAllStringFunc(format, func(m string) string { + if m == "{{" || m == "}}" { + return m[:1] + } i, err := strconv.Atoi(formatArgRe.FindStringSubmatch(m)[1]) if err != nil || i >= len(arguments) { return m diff --git a/pkg/attestation/crafter/materials/buildlog/binlog_test.go b/pkg/attestation/crafter/materials/buildlog/binlog_test.go index 5c1bf9325..db4b11907 100644 --- a/pkg/attestation/crafter/materials/buildlog/binlog_test.go +++ b/pkg/attestation/crafter/materials/buildlog/binlog_test.go @@ -238,3 +238,18 @@ func TestParseBinlogMalformedRecordFails(t *testing.T) { _, err := Parse(w.bytes(t)) assert.Error(t, err) } + +func TestFormatMessage(t *testing.T) { + // .NET composite format: "{{" and "}}" are literal braces. + for format, want := range map[string]string{ + "{0} is unused": "x is unused", + "{{0}} is {0}": "{0} is x", + "{{{0}}}": "{x}", + "}} {1,-8:N2} {{": "} y {", + "{2} has no value": "{2} has no value", + "{{ and }} only": "{ and } only", + "unbalanced } and {": "unbalanced } and {", + } { + assert.Equal(t, want, formatMessage(format, []string{"x", "y"}), format) + } +} diff --git a/pkg/attestation/crafter/materials/buildlog/buildlog.go b/pkg/attestation/crafter/materials/buildlog/buildlog.go index a0e84b8f6..43bd9e303 100644 --- a/pkg/attestation/crafter/materials/buildlog/buildlog.go +++ b/pkg/attestation/crafter/materials/buildlog/buildlog.go @@ -228,7 +228,7 @@ func isArchiveName(name string) bool { // hold the binary logs already zipped per solution. Other entries are skipped, // but at least one log must be found. func parseArchive(walk walker) (*Report, error) { - var c collected + c := collected{budget: budget{limits: archiveLimits()}} if err := c.walk(walk, "", 0); err != nil { return nil, fmt.Errorf("reading build log archive: %w", err) } @@ -248,6 +248,40 @@ func parseArchive(walk walker) (*Report, error) { type collected struct { logs []*parsedLog skipped []string + budget budget +} + +// budget counts the entries and bytes read from the material's archive and +// from the archives inside it. Each archive walk applies its limits to itself +// only, so without a shared count every nested archive could expand to the +// full limits again. +type budget struct { + limits archiveio.Limits + entries int + read int64 +} + +// entry counts an entry and returns a reader that counts its bytes. +func (b *budget) entry(r io.Reader) (io.Reader, error) { + b.entries++ + if b.entries > b.limits.MaxEntries { + return nil, archiveio.ErrTooManyEntries + } + return &budgetReader{r: r, b: b}, nil +} + +type budgetReader struct { + r io.Reader + b *budget +} + +func (br *budgetReader) Read(p []byte) (int, error) { + n, err := br.r.Read(p) + br.b.read += int64(n) + if br.b.read > br.b.limits.MaxTotalSize { + return n, archiveio.ErrArchiveTooLarge + } + return n, err } // projectImportsSuffix names the archive of project files that @@ -260,6 +294,10 @@ const projectImportsSuffix = ".projectimports.zip" func (c *collected) walk(walk walker, prefix string, depth int) error { return walk(func(name string, r io.Reader) error { source := prefix + name + r, err := c.budget.entry(r) + if err != nil { + return err + } switch { case strings.HasSuffix(strings.ToLower(name), projectImportsSuffix): c.skipped = append(c.skipped, source) diff --git a/pkg/attestation/crafter/materials/buildlog/fidelity_test.go b/pkg/attestation/crafter/materials/buildlog/fidelity_test.go index 26a46bdb7..f947dcb43 100644 --- a/pkg/attestation/crafter/materials/buildlog/fidelity_test.go +++ b/pkg/attestation/crafter/materials/buildlog/fidelity_test.go @@ -21,8 +21,10 @@ package buildlog import ( "bytes" + "io" "testing" + "github.com/chainloop-dev/chainloop/pkg/attestation/crafter/materials/archiveio" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -185,3 +187,52 @@ func TestParseDiagnosticLineEdgeCases(t *testing.T) { func TestSniffOldMSBuildBanner(t *testing.T) { assert.Equal(t, FormatMSBuildText, sniffText([]byte("Microsoft (R) Build Engine Version 3.5.30729.1\r\n[Microsoft .NET Framework, Version 2.0.50727.8806]\r\n"))) } + +func TestParseRejectsResultLineWithoutBuild(t *testing.T) { + // Other tools print "Build succeeded." too. A result line with no sign + // of an MSBuild build would pass as a clean build. + for name, log := range map[string]string{ + "result line only": "Build succeeded.\n", + "inside other output": "Restoring tools\nBuild succeeded.\nDone in 3s\n", + "translated result only": "La génération a réussi.\n", + } { + t.Run(name, func(t *testing.T) { + _, err := Parse([]byte(log)) + assert.ErrorIs(t, err, ErrNoBuildLog) + }) + } + + t.Run("with its counts", func(t *testing.T) { + // A clean `dotnet build`: the counts after the result line are the + // build's own record that nothing was reported. + report, err := Parse([]byte("Build succeeded.\n 0 Warning(s)\n 0 Error(s)\n")) + require.NoError(t, err) + require.Len(t, report.Logs, 1) + assert.Equal(t, ResultSucceeded, report.Logs[0].Result) + assert.Equal(t, &Counts{}, report.Logs[0].Reported) + }) +} + +func TestParseArchiveBoundsNestedArchivesTogether(t *testing.T) { + // Each archive walk applies the limits to itself only, so the archives + // inside the material's archive must share what the material allows. + log := readTestdata(t, msbuildText) + nested := zipOf(t, map[string][]byte{"build.log": log}) + outer := zipOf(t, map[string][]byte{"a.zip": nested, "b.zip": nested}) + walk := func(yield func(string, io.Reader) error) error { + return archiveio.WalkBytes(outer, archiveio.DetectBytes(outer), archiveLimits(), yield) + } + + // Room for one nested archive and its log, not for two. + one := int64(len(nested) + len(log)) + c := collected{budget: budget{limits: archiveio.Limits{MaxEntries: 1024, MaxTotalSize: one + one/2}}} + require.ErrorIs(t, c.walk(walk, "", 0), archiveio.ErrArchiveTooLarge) + + // Four entries: two archives and a log in each. + c = collected{budget: budget{limits: archiveio.Limits{MaxEntries: 3, MaxTotalSize: 1 << 30}}} + require.ErrorIs(t, c.walk(walk, "", 0), archiveio.ErrTooManyEntries) + + c = collected{budget: budget{limits: archiveio.Limits{MaxEntries: 4, MaxTotalSize: 2 * one}}} + require.NoError(t, c.walk(walk, "", 0)) + assert.Len(t, c.logs, 2) +} diff --git a/pkg/attestation/crafter/materials/buildlog/localized_test.go b/pkg/attestation/crafter/materials/buildlog/localized_test.go index 52325015a..8ea4ca874 100644 --- a/pkg/attestation/crafter/materials/buildlog/localized_test.go +++ b/pkg/attestation/crafter/materials/buildlog/localized_test.go @@ -82,6 +82,55 @@ func TestParseLocalizedMSBuildTextLogs(t *testing.T) { result: "Сборка успешно завершена.", warnings: "Предупреждений: 1", errors: "Ошибок: 0", wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, }, + // The current translations in MSBuild's resources. + { + lang: "de current", + start: "Der Buildvorgang wurde am 28.10.2026 09:00:00 gestartet.", project: `1>Projekt "C:\src\a.sln" auf Knoten "1" (Standardziele).`, + result: "Der Buildvorgang wurde erfolgreich ausgeführt.", warnings: " 1 Warnung(en)", errors: " 0 Fehler", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + { + lang: "de failed", + start: "Der Buildvorgang wurde am 28.10.2026 09:00:00 gestartet.", project: `1>Projekt "C:\src\a.sln" auf Knoten "1" (Standardziele).`, + result: "Fehler beim Buildvorgang.", warnings: " 1 Warnung(en)", errors: " 1 Fehler", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "ko succeeded", + start: "빌드 시작: 2026-10-28 오전 9:00:00", project: `1>1 노드의 "C:\src\a.sln" 프로젝트(기본 대상)입니다.`, + result: "빌드했습니다.", warnings: " 경고 1개", errors: " 오류 0개", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + { + lang: "ko failed with counts", + start: "빌드 시작: 2026-10-28 오전 9:00:00", project: `1>1 노드의 "C:\src\a.sln" 프로젝트(기본 대상)입니다.`, + result: "빌드하지 못했습니다.", warnings: " 경고 1개", errors: " 오류 1개", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "zh-TW failed", + start: "已經開始建置於 2026/10/28 上午 09:00:00。", project: `1>節點 1 (預設目標) 上的專案 "C:\src\a.sln"。`, + result: "建置失敗。", warnings: " 1 個警告", errors: " 1 個錯誤", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "ja failed", + start: "2026/10/28 9:00:00 にビルドを開始しました。", project: `1>ノード 1 上のプロジェクト "C:\src\a.sln" (既定のターゲット)。`, + result: "ビルドに失敗しました。", warnings: " 1 個の警告", errors: " 1 エラー", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, + { + lang: "fr succeeded", + start: "La génération a démarré 28/10/2026 09:00:00.", project: `1>Projet "C:\src\a.sln" sur le nœud 1 (cibles par défaut).`, + result: "La génération a réussi.", warnings: " 1 Avertissement(s)", errors: " 0 Erreur(s)", + wantResult: ResultSucceeded, wantReported: &Counts{Warnings: 1}, + }, + { + lang: "ru failed", + start: "Сборка начата 28.10.2026 9:00:00.", project: `1>Проект "C:\src\a.sln" в узле 1 (целевые объекты по умолчанию).`, + result: "Ошибка сборки.", warnings: "Предупреждений: 1", errors: "Ошибок: 1", + wantResult: ResultFailed, wantReported: &Counts{Warnings: 1, Errors: 1}, + }, } for _, tc := range testCases { t.Run(tc.lang, func(t *testing.T) { diff --git a/pkg/attestation/crafter/materials/buildlog/text.go b/pkg/attestation/crafter/materials/buildlog/text.go index becde225c..4f5d2367f 100644 --- a/pkg/attestation/crafter/materials/buildlog/text.go +++ b/pkg/attestation/crafter/materials/buildlog/text.go @@ -40,9 +40,10 @@ var ( diagnosticRe = regexp.MustCompile(`^\s*(?:\d+(?::\d+)?>)?(.*?)(?:\((\d+)(?:([,-])(\d+))?(?:[,-]\d+)*\))?\s*:\s*(?:[^:]*?\s)?(fatal error|error|warning)(?:\s+([A-Za-z]+[0-9]+))?\s*:\s*(.*?)(?:\s+\[([^\]\-][^\]]*)\])?\s*$`) // MSBuild's own lines are translated when Visual Studio runs in another // language, while compiler diagnostics keep the canonical English form. The - // patterns below hold the English line and the translations seen in real - // logs (zh-CN, zh-TW, ja, ko, de, fr, ru). A translation not listed leaves - // the fact unknown, which the policy treats as incomplete evidence. + // patterns below hold the English line, its zh-CN, zh-TW, ja, ko, de, fr and + // ru translations in MSBuild's resources, and older translations seen in + // real logs. A translation not listed leaves the fact unknown, which the + // policy treats as incomplete evidence. // msbuildBannerRe extracts the MSBuild version from the console banner. msbuildBannerRe = regexp.MustCompile(`^(?:MSBuild (?i:version)|Microsoft \(R\) Build Engine (?i:version)|.*MSBuild 版本) ([0-9][^\s]*)`) @@ -50,15 +51,15 @@ var ( // diagnostics logged while it runs, then the end-of-build summary that // repeats them. msbuildStartRe = regexp.MustCompile(`^(?:Build started\b|生成启动时间为|已經開始建置於|빌드 시작:|Der Buildvorgang wurde am .* gestartet\.|La génération a démarré|Сборка начата)|にビルドを開始しました。\s*$`) - msbuildSucceededRe = regexp.MustCompile(`^(?:Build succeeded\.|已成功生成。|生成成功。|建置成功。|ビルドに成功しました。|Build erfolgreich\.|Сборка успешно завершена\.)\s*$`) - msbuildFailedRe = regexp.MustCompile(`^(?:Build FAILED\.|生成失败。|빌드하지 못했습니다\.|ÉCHEC de la build\.)\s*$`) + msbuildSucceededRe = regexp.MustCompile(`^(?:Build succeeded\.|已成功生成。|生成成功。|建置成功。|ビルドに成功しました。|빌드했습니다\.|Der Buildvorgang wurde erfolgreich ausgeführt\.|Build erfolgreich\.|La génération a réussi\.|Сборка успешно завершена\.)\s*$`) + msbuildFailedRe = regexp.MustCompile(`^(?:Build FAILED\.|生成失败。|建置失敗。|ビルドに失敗しました。|빌드하지 못했습니다\.|Fehler beim Buildvorgang\.|ÉCHEC de la build\.|Ошибка сборки\.)\s*$`) // msbuildProjectRe matches the start of a project build, // `Project "path" on node N`, which may follow other text on its line. msbuildProjectRe = regexp.MustCompile(`\bProject "([^"]+)" on node \d+|项目“([^”]+)”在节点 \d+ 上|節點 \d+ .*?上的專案 "([^"]+)"|ノード \d+ 上のプロジェクト "([^"]+)"|\d+ 노드의 "([^"]+)" 프로젝트|Projekt "([^"]+)" auf Knoten|Projet "([^"]+)" sur le nœud|Проект "([^"]+)" в узле`) // msbuildWarningsRe and msbuildErrorsRe match the summary counters // "N Warning(s)" and "N Error(s)". - msbuildWarningsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Warning\(s\)|个警告|個の警告|Avertissement\(s\))|Предупреждений: (\d+))\s*$`) - msbuildErrorsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Error\(s\)|个错误|エラー|Erreur\(s\))|Ошибок: (\d+))\s*$`) + msbuildWarningsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Warning\(s\)|个警告|個警告|個の警告|Warnung\(en\)|Avertissement\(s\))|경고 (\d+)개|Предупреждений: (\d+))\s*$`) + msbuildErrorsRe = regexp.MustCompile(`^\s*(?:(\d+) (?:Error\(s\)|个错误|個錯誤|エラー|Fehler|Erreur\(s\))|오류 (\d+)개|Ошибок: (\d+))\s*$`) // ciPrefixRe matches what CI systems put before each console line: an ISO // timestamp (GitHub Actions, Azure DevOps) or "[hh:mm:ss] " (AppVeyor). ciPrefixRe = regexp.MustCompile(`^(?:\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?Z |\[\d{2}:\d{2}:\d{2}\] )`) @@ -150,9 +151,11 @@ type run struct { reported *Counts } -// empty reports whether nothing of the run has been seen yet. -func (r *run) empty() bool { - return !r.opened && r.result == "" && r.reported == nil && len(r.inline)+len(r.summary) == 0 +// build reports whether the run shows an MSBuild build: its banner or start +// line, the counts of its summary, or a diagnostic. A result line alone does +// not, as other tools print "Build succeeded." too. +func (r *run) build() bool { + return r.opened || r.reported != nil || len(r.inline)+len(r.summary) > 0 } func (r *run) count() *Counts { @@ -249,7 +252,7 @@ func scanMSBuild(scanner *bufio.Scanner) ([]*parsedLog, error) { var logs []*parsedLog cur := &run{} next := func() { - if !cur.empty() { + if cur.build() { logs = append(logs, cur.log()) } cur = &run{} @@ -306,10 +309,6 @@ func scanMSBuild(scanner *bufio.Scanner) ([]*parsedLog, error) { } } next() - if len(logs) == 0 { - // A recognized log with nothing in it is still one build. - logs = append(logs, (&run{}).log()) - } return logs, scanner.Err() } From 8234a05c6bb7ff0eda74076548def4edb546c288 Mon Sep 17 00:00:00 2001 From: Javier Rodriguez Date: Wed, 28 Oct 2026 13:57:27 +0100 Subject: [PATCH 3/3] refactor(materials): rename BUILD_LOG to MSBUILD_LOG BUILD_LOG is not a standard: every format the material reads (the MSBuild binary and text logs and the Visual C++ BuildLog.htm) is specific to Microsoft build tools. Name the material type after them, as other tool formats are named. - Rename the material type, its crafter and its annotations (chainloop.material.msbuild_log.*). - Say in the type's description that BuildLog.htm is written by VCBuild. Signed-off-by: Javier Rodriguez Chainloop-Trace-Sessions: 22fceba7-c836-4440-b83a-be86dfa26a91 --- app/cli/documentation/cli-reference.md | 4 +- .../workflowcontract/v1/crafting_schema.ts | 17 ++++---- ...on.v1.Attestation.Material.jsonschema.json | 4 +- ...tation.v1.Attestation.Material.schema.json | 4 +- ...tation.v1.PolicyEvaluation.jsonschema.json | 2 +- ...ttestation.v1.PolicyEvaluation.schema.json | 2 +- ...v1.CraftingSchema.Material.jsonschema.json | 2 +- ...act.v1.CraftingSchema.Material.schema.json | 2 +- ...ct.v1.PolicyGroup.Material.jsonschema.json | 2 +- ...ntract.v1.PolicyGroup.Material.schema.json | 2 +- ...flowcontract.v1.PolicySpec.jsonschema.json | 2 +- ...workflowcontract.v1.PolicySpec.schema.json | 2 +- ...owcontract.v1.PolicySpecV2.jsonschema.json | 2 +- ...rkflowcontract.v1.PolicySpecV2.schema.json | 2 +- .../workflowcontract/v1/crafting_schema.pb.go | 21 +++++----- .../workflowcontract/v1/crafting_schema.proto | 7 ++-- .../v1/crafting_schema_test.go | 2 +- .../v1/crafting_schema_validations.go | 2 +- .../api/attestation/v1/crafting_state.go | 2 +- .../api/attestation/v1/crafting_state_test.go | 6 +-- .../crafter/materials/buildlog/buildlog.go | 6 +-- .../crafter/materials/materials.go | 4 +- .../{build_log.go => msbuild_log.go} | 32 +++++++-------- ...{build_log_test.go => msbuild_log_test.go} | 40 +++++++++---------- 24 files changed, 87 insertions(+), 84 deletions(-) rename pkg/attestation/crafter/materials/{build_log.go => msbuild_log.go} (67%) rename pkg/attestation/crafter/materials/{build_log_test.go => msbuild_log_test.go} (81%) diff --git a/app/cli/documentation/cli-reference.md b/app/cli/documentation/cli-reference.md index 4c9addc02..875069029 100644 --- a/app/cli/documentation/cli-reference.md +++ b/app/cli/documentation/cli-reference.md @@ -264,7 +264,7 @@ Options --append reserved for a future release: will control whether --policy-input and --policy-input-from-file append to (rather than replace) the contract-declared value; has no effect yet --attestation-id string Unique identifier of the in-progress attestation -h, --help help for add ---kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "BUILD_LOG" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string kind of the material to be recorded: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "MSBUILD_LOG" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --max-extract-entries int max number of files to extract when --value is an archive (default 10000) --max-extract-size string max total uncompressed size to extract when --value is an archive (default "1GiB") --name string name of the material as shown in the contract @@ -2876,7 +2876,7 @@ Options --annotation strings Key-value pairs of material annotations (key=value) -h, --help help for eval --input stringArray Key-value pairs of policy inputs (key=value) ---kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "BUILD_LOG" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] +--kind string Kind of the material: ["ARTIFACT" "ASYNCAPI_SPEC" "ATTESTATION" "BLACKDUCK_SCA_JSON" "CERTCC_DRANZER" "CHAINLOOP_AI_AGENT_CONFIG" "CHAINLOOP_AI_CODING_SESSION" "CHAINLOOP_AI_SECURITY_CONTEXT" "CHAINLOOP_PR_INFO" "CHAINLOOP_RUNNER_CONTEXT" "CHECKMARX_JSON" "COBERTURA_XML" "CONTAINER_IMAGE" "CSAF_INFORMATIONAL_ADVISORY" "CSAF_SECURITY_ADVISORY" "CSAF_SECURITY_INCIDENT_RESPONSE" "CSAF_VEX" "EVIDENCE" "GHAS_CODE_SCAN" "GHAS_DEPENDENCY_SCAN" "GHAS_SECRET_SCAN" "GITLAB_SECURITY_REPORT" "GITLEAKS_JSON" "GRAPHQL_SPEC" "HELM_CHART" "JACOCO_XML" "JUNIT_XML" "MSBUILD_LOG" "OPENAPI_SPEC" "OPENVEX" "OSSF_SCORECARD_JSON" "OVERSECURED_JSON" "PITEST_XML" "RADAMSA_CRASHES" "RADAMSA_REPORT" "SARIF" "SBOM_CYCLONEDX_JSON" "SBOM_SPDX_JSON" "SLSA_PROVENANCE" "STRING" "SYSINTERNALS_ACCESSCHK" "SYSINTERNALS_SIGCHECK" "TRUFFLEHOG_JSON" "TWISTCLI_SCAN_JSON" "YELP_DETECT_SECRETS_BASELINE" "ZAP_DAST_ZIP"] --material string Path to material or attestation file -p, --policy string Policy reference (./my-policy.yaml, https://my-domain.com/my-policy.yaml, chainloop://my-stored-policy) (default "policy.yaml") --project string Project name to use as engine context for chainloop.* built-ins diff --git a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts index 9a7b98f9e..66b585d7c 100644 --- a/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts +++ b/app/controlplane/api/gen/frontend/workflowcontract/v1/crafting_schema.ts @@ -343,11 +343,12 @@ export enum CraftingSchema_Material_MaterialType { */ CHAINLOOP_AI_SECURITY_CONTEXT = 45, /** - * BUILD_LOG - Build log of Microsoft build tools: an MSBuild binary log (-bl), an - * MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive - * of them. Its compiler and build warnings and errors are evaluated. + * MSBUILD_LOG - Build log of Microsoft build tools: an MSBuild binary log (-bl), an + * MSBuild text log, a Visual C++ build log (BuildLog.htm, written by + * VCBuild), or an archive of them. Its compiler and build warnings and + * errors are evaluated. */ - BUILD_LOG = 46, + MSBUILD_LOG = 46, UNRECOGNIZED = -1, } @@ -492,8 +493,8 @@ export function craftingSchema_Material_MaterialTypeFromJSON(object: any): Craft case "CHAINLOOP_AI_SECURITY_CONTEXT": return CraftingSchema_Material_MaterialType.CHAINLOOP_AI_SECURITY_CONTEXT; case 46: - case "BUILD_LOG": - return CraftingSchema_Material_MaterialType.BUILD_LOG; + case "MSBUILD_LOG": + return CraftingSchema_Material_MaterialType.MSBUILD_LOG; case -1: case "UNRECOGNIZED": default: @@ -595,8 +596,8 @@ export function craftingSchema_Material_MaterialTypeToJSON(object: CraftingSchem return "PITEST_XML"; case CraftingSchema_Material_MaterialType.CHAINLOOP_AI_SECURITY_CONTEXT: return "CHAINLOOP_AI_SECURITY_CONTEXT"; - case CraftingSchema_Material_MaterialType.BUILD_LOG: - return "BUILD_LOG"; + case CraftingSchema_Material_MaterialType.MSBUILD_LOG: + return "MSBUILD_LOG"; case CraftingSchema_Material_MaterialType.UNRECOGNIZED: default: return "UNRECOGNIZED"; diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json index 63364b108..52a4d3b99 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.jsonschema.json @@ -63,7 +63,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" @@ -162,7 +162,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json index 51c5d3dca..c62fd6372 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.Attestation.Material.schema.json @@ -63,7 +63,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" @@ -162,7 +162,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" 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 315ce7eb7..11aea81ba 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.jsonschema.json @@ -174,7 +174,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" 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 c3e2a8be6..b53938a6b 100644 --- a/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json +++ b/app/controlplane/api/gen/jsonschema/attestation.v1.PolicyEvaluation.schema.json @@ -174,7 +174,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json index 3858a8f0e..40e442bc3 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.jsonschema.json @@ -84,7 +84,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json index 73a83a27a..215b47d86 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.CraftingSchema.Material.schema.json @@ -84,7 +84,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json index dce614f15..079b84634 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.jsonschema.json @@ -108,7 +108,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json index 66c3acf3b..0d0b7a013 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicyGroup.Material.schema.json @@ -108,7 +108,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json index f439de8b5..fd3f11692 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.jsonschema.json @@ -82,7 +82,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json index 5f4823951..17ac102d1 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpec.schema.json @@ -82,7 +82,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json index baca32956..d9e4b8c16 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.jsonschema.json @@ -103,7 +103,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json index 3f6e96ba8..2f63c34c8 100644 --- a/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json +++ b/app/controlplane/api/gen/jsonschema/workflowcontract.v1.PolicySpecV2.schema.json @@ -103,7 +103,7 @@ "OVERSECURED_JSON", "PITEST_XML", "CHAINLOOP_AI_SECURITY_CONTEXT", - "BUILD_LOG" + "MSBUILD_LOG" ], "title": "Material Type", "type": "string" diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go index 2e31a7365..0381d74cf 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.pb.go @@ -261,9 +261,10 @@ const ( // and byte-verifiable evidence anchors backing each claim CraftingSchema_Material_CHAINLOOP_AI_SECURITY_CONTEXT CraftingSchema_Material_MaterialType = 45 // Build log of Microsoft build tools: an MSBuild binary log (-bl), an - // MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive - // of them. Its compiler and build warnings and errors are evaluated. - CraftingSchema_Material_BUILD_LOG CraftingSchema_Material_MaterialType = 46 + // MSBuild text log, a Visual C++ build log (BuildLog.htm, written by + // VCBuild), or an archive of them. Its compiler and build warnings and + // errors are evaluated. + CraftingSchema_Material_MSBUILD_LOG CraftingSchema_Material_MaterialType = 46 ) // Enum value maps for CraftingSchema_Material_MaterialType. @@ -315,7 +316,7 @@ var ( 43: "OVERSECURED_JSON", 44: "PITEST_XML", 45: "CHAINLOOP_AI_SECURITY_CONTEXT", - 46: "BUILD_LOG", + 46: "MSBUILD_LOG", } CraftingSchema_Material_MaterialType_value = map[string]int32{ "MATERIAL_TYPE_UNSPECIFIED": 0, @@ -364,7 +365,7 @@ var ( "OVERSECURED_JSON": 43, "PITEST_XML": 44, "CHAINLOOP_AI_SECURITY_CONTEXT": 45, - "BUILD_LOG": 46, + "MSBUILD_LOG": 46, } ) @@ -2111,7 +2112,7 @@ var File_workflowcontract_v1_crafting_schema_proto protoreflect.FileDescriptor const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\n" + - ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\xa8\x13\n" + + ")workflowcontract/v1/crafting_schema.proto\x12\x13workflowcontract.v1\x1a\x1bbuf/validate/validate.proto\"\xaa\x13\n" + "\x0eCraftingSchema\x122\n" + "\x0eschema_version\x18\x01 \x01(\tB\v\xbaH\x06r\x04\n" + "\x02v1\x18\x01R\rschemaVersion\x12N\n" + @@ -2134,7 +2135,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x0fDAGGER_PIPELINE\x10\x06\x12\x15\n" + "\x11TEAMCITY_PIPELINE\x10\a\x12\x13\n" + "\x0fTEKTON_PIPELINE\x10\b\x12\x15\n" + - "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\xf3\f\n" + + "\x11CHAINLOOP_SANDBOX\x10\t:\x02\x18\x01\x1a\xf5\f\n" + "\bMaterial\x12[\n" + "\x04type\x18\x01 \x01(\x0e29.workflowcontract.v1.CraftingSchema.Material.MaterialTypeB\f\xbaH\a\x82\x01\x04\x10\x01 \x00\x18\x01R\x04type\x12\x99\x01\n" + "\x04name\x18\x02 \x01(\tB\x84\x01\xbaH\x7f\xba\x01|\n" + @@ -2145,7 +2146,7 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\vskip_upload\x18\x06 \x01(\bR\n" + "skipUpload\x12\xaa\x01\n" + "\x05group\x18\a \x01(\tB\x93\x01\xbaH\x8f\x01\xba\x01\x8b\x01\n" + - "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\x98\b\n" + + "\x0egroup.dns-1123\x12:must contain only lowercase letters, numbers, and hyphens.\x1a=this == '' || this.matches('^[a-z0-9]([-a-z0-9]*[a-z0-9])?$')R\x05group\"\x9a\b\n" + "\fMaterialType\x12\x1d\n" + "\x19MATERIAL_TYPE_UNSPECIFIED\x10\x00\x12\n" + "\n" + @@ -2197,8 +2198,8 @@ const file_workflowcontract_v1_crafting_schema_proto_rawDesc = "" + "\x10OVERSECURED_JSON\x10+\x12\x0e\n" + "\n" + "PITEST_XML\x10,\x12!\n" + - "\x1dCHAINLOOP_AI_SECURITY_CONTEXT\x10-\x12\r\n" + - "\tBUILD_LOG\x10.:\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + + "\x1dCHAINLOOP_AI_SECURITY_CONTEXT\x10-\x12\x0f\n" + + "\vMSBUILD_LOG\x10.:\x02\x18\x01:\x02\x18\x01\"\xfb\x01\n" + "\x10CraftingSchemaV2\x128\n" + "\vapi_version\x18\x01 \x01(\tB\x17\xbaH\x14r\x12\n" + "\x10chainloop.dev/v1R\n" + diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto index 8c0bd395e..4124983c1 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema.proto +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema.proto @@ -210,9 +210,10 @@ message CraftingSchema { // and byte-verifiable evidence anchors backing each claim CHAINLOOP_AI_SECURITY_CONTEXT = 45; // Build log of Microsoft build tools: an MSBuild binary log (-bl), an - // MSBuild text log, a Visual C++ build log (BuildLog.htm), or an archive - // of them. Its compiler and build warnings and errors are evaluated. - BUILD_LOG = 46; + // MSBuild text log, a Visual C++ build log (BuildLog.htm, written by + // VCBuild), or an archive of them. Its compiler and build warnings and + // errors are evaluated. + MSBUILD_LOG = 46; } } } diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go b/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go index 66c656332..f1ef65ea2 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema_test.go @@ -361,7 +361,7 @@ func TestMaterialKindsExcludedFromAutoDetection(t *testing.T) { }, { name: "build log probing parses whole logs of hundreds of megabytes", - kind: v1.CraftingSchema_Material_BUILD_LOG, + kind: v1.CraftingSchema_Material_MSBUILD_LOG, }, } diff --git a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go index d87741386..d4857e963 100644 --- a/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go +++ b/app/controlplane/api/workflowcontract/v1/crafting_schema_validations.go @@ -42,7 +42,7 @@ import ( // an app platform), but the export envelope is not published by the vendor, // so pinning it here would bet every other JSON kind on a shape inferred // from a sample. -// - BUILD_LOG: recognizing a log means parsing it, and diagnostic-verbosity +// - MSBUILD_LOG: recognizing a log means parsing it, and diagnostic-verbosity // text logs reach hundreds of megabytes; probing every file for it would // slow down every auto-detected material. // diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state.go index 413b2e97f..00410aef4 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state.go @@ -351,7 +351,7 @@ func (m *Attestation_Material) ingestMaterialToJSON(rawMaterial []byte, value st return nil, fmt.Errorf("invalid dranzer material: %w", err) } return json.Marshal(bundle) - case v1.CraftingSchema_Material_BUILD_LOG: + case v1.CraftingSchema_Material_MSBUILD_LOG: // Build logs are binary (MSBuild -bl), plain text or HTML, possibly // several in an archive. They project to the normalized diagnostics // only, with no raw text: diagnostic-verbosity text logs reach hundreds diff --git a/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go b/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go index e2159bb27..b7af12f62 100644 --- a/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go +++ b/pkg/attestation/crafter/api/attestation/v1/crafting_state_test.go @@ -372,13 +372,13 @@ func TestDranzerBundleIsEvaluable(t *testing.T) { assert.Len(t, decoded["reports"], 4) } -// TestBuildLogIsEvaluable guards that a BUILD_LOG material, which is stored as +// TestMSBuildLogIsEvaluable guards that an MSBUILD_LOG material, which is stored as // the raw (often gzip-compressed binary) log, projects to the normalized // diagnostics policies evaluate. Handing the engine the raw bytes would fail // to parse, or for a text log yield nothing to evaluate. -func TestBuildLogIsEvaluable(t *testing.T) { +func TestMSBuildLogIsEvaluable(t *testing.T) { m := &Attestation_Material{ - MaterialType: schemaapi.CraftingSchema_Material_BUILD_LOG, + MaterialType: schemaapi.CraftingSchema_Material_MSBUILD_LOG, M: &Attestation_Material_Artifact_{ Artifact: &Attestation_Material_Artifact{Name: "build-log", Digest: "sha256:deadbeef"}, }, diff --git a/pkg/attestation/crafter/materials/buildlog/buildlog.go b/pkg/attestation/crafter/materials/buildlog/buildlog.go index 43bd9e303..f4c5b3e7d 100644 --- a/pkg/attestation/crafter/materials/buildlog/buildlog.go +++ b/pkg/attestation/crafter/materials/buildlog/buildlog.go @@ -124,7 +124,7 @@ type Log struct { Reported *Counts `json:"reported,omitempty"` } -// Report is the policy projection of a BUILD_LOG material. Skipped lists the +// Report is the policy projection of an MSBUILD_LOG material. Skipped lists the // archive entries that were not read as logs, so a policy can tell a log that // could not be read from a file that is not a log. type Report struct { @@ -152,7 +152,7 @@ func archiveLimits() archiveio.Limits { return archiveio.Limits{MaxEntries: 1024, MaxTotalSize: 4 << 30} } -// Parse projects a BUILD_LOG material held in memory. +// Parse projects an MSBUILD_LOG material held in memory. func Parse(data []byte) (*Report, error) { if isBinlog(data) { return parseSingle(bytes.NewReader(data)) @@ -165,7 +165,7 @@ func Parse(data []byte) (*Report, error) { return parseSingle(bytes.NewReader(data)) } -// ParseFile projects a BUILD_LOG material on disk, streaming it. +// ParseFile projects an MSBUILD_LOG material on disk, streaming it. func ParseFile(p string) (*Report, error) { f, err := os.Open(p) if err != nil { diff --git a/pkg/attestation/crafter/materials/materials.go b/pkg/attestation/crafter/materials/materials.go index fb8cc78e5..d7dbc0151 100644 --- a/pkg/attestation/crafter/materials/materials.go +++ b/pkg/attestation/crafter/materials/materials.go @@ -491,8 +491,8 @@ func Craft(ctx context.Context, materialSchema *schemaapi.CraftingSchema_Materia crafter, err = NewCheckmarxCrafter(materialSchema, casBackend, logger) case schemaapi.CraftingSchema_Material_OVERSECURED_JSON: crafter, err = NewOversecuredCrafter(materialSchema, casBackend, logger) - case schemaapi.CraftingSchema_Material_BUILD_LOG: - crafter, err = NewBuildLogCrafter(materialSchema, casBackend, logger) + case schemaapi.CraftingSchema_Material_MSBUILD_LOG: + crafter, err = NewMSBuildLogCrafter(materialSchema, casBackend, logger) default: return nil, fmt.Errorf("material of type %q not supported yet", materialSchema.Type) } diff --git a/pkg/attestation/crafter/materials/build_log.go b/pkg/attestation/crafter/materials/msbuild_log.go similarity index 67% rename from pkg/attestation/crafter/materials/build_log.go rename to pkg/attestation/crafter/materials/msbuild_log.go index a26d0e72e..ee2d7bf0c 100644 --- a/pkg/attestation/crafter/materials/build_log.go +++ b/pkg/attestation/crafter/materials/msbuild_log.go @@ -30,16 +30,16 @@ import ( ) const ( - // AnnotationBuildLogLogsCount is the number of build logs in a BUILD_LOG + // AnnotationMSBuildLogLogsCount is the number of build logs in an MSBUILD_LOG // material: 1 for a single log, or the number of logs found in an archive. - AnnotationBuildLogLogsCount = "chainloop.material.build_log.logs.count" - // AnnotationBuildLogWarningsCount and AnnotationBuildLogErrorsCount are the - // number of distinct warnings and errors in a BUILD_LOG material. - AnnotationBuildLogWarningsCount = "chainloop.material.build_log.warnings.count" - AnnotationBuildLogErrorsCount = "chainloop.material.build_log.errors.count" + AnnotationMSBuildLogLogsCount = "chainloop.material.msbuild_log.logs.count" + // AnnotationMSBuildLogWarningsCount and AnnotationMSBuildLogErrorsCount are the + // number of distinct warnings and errors in an MSBUILD_LOG material. + AnnotationMSBuildLogWarningsCount = "chainloop.material.msbuild_log.warnings.count" + AnnotationMSBuildLogErrorsCount = "chainloop.material.msbuild_log.errors.count" ) -// BuildLogCrafter stores the log of a Microsoft build (MSBuild binary or text +// MSBuildLogCrafter stores the log of a Microsoft build (MSBuild binary or text // log, or a Visual C++ BuildLog.htm) as evidence of the warnings and errors the // build produced. The log is stored as-is; its projection to the JSON the // policy engine consumes happens later, at evaluation time. @@ -47,20 +47,20 @@ const ( // The value may also be an archive of logs, for builds that produce one binary // log per solution. The archive is recorded whole and the projection // aggregates its entries. -type BuildLogCrafter struct { +type MSBuildLogCrafter struct { *crafterCommon backend *casclient.CASBackend } -func NewBuildLogCrafter(schema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*BuildLogCrafter, error) { - if schema.Type != schemaapi.CraftingSchema_Material_BUILD_LOG { +func NewMSBuildLogCrafter(schema *schemaapi.CraftingSchema_Material, backend *casclient.CASBackend, l *zerolog.Logger) (*MSBuildLogCrafter, error) { + if schema.Type != schemaapi.CraftingSchema_Material_MSBUILD_LOG { return nil, fmt.Errorf("material type is not a build log") } craftCommon := &crafterCommon{logger: l, input: schema} - return &BuildLogCrafter{backend: backend, crafterCommon: craftCommon}, nil + return &MSBuildLogCrafter{backend: backend, crafterCommon: craftCommon}, nil } -func (i *BuildLogCrafter) Craft(ctx context.Context, filePath string) (*CraftResult, error) { +func (i *MSBuildLogCrafter) Craft(ctx context.Context, filePath string) (*CraftResult, error) { // Parse with the same code the policy projection uses, so a material // accepted here is one the projection can read. Were the two to disagree, a // log would be attested and then fail, or worse pass, the policy gate. @@ -82,7 +82,7 @@ func (i *BuildLogCrafter) Craft(ctx context.Context, filePath string) (*CraftRes return craftResult(m, nil) } -func (i *BuildLogCrafter) injectAnnotations(m *api.Attestation_Material, report *buildlog.Report) { +func (i *MSBuildLogCrafter) injectAnnotations(m *api.Attestation_Material, report *buildlog.Report) { if m.Annotations == nil { m.Annotations = make(map[string]string) } @@ -99,7 +99,7 @@ func (i *BuildLogCrafter) injectAnnotations(m *api.Attestation_Material, report } } SetToolsAnnotation(m, tools) - m.Annotations[AnnotationBuildLogLogsCount] = strconv.Itoa(len(report.Logs)) - m.Annotations[AnnotationBuildLogWarningsCount] = strconv.Itoa(report.Summary.Warnings) - m.Annotations[AnnotationBuildLogErrorsCount] = strconv.Itoa(report.Summary.Errors) + m.Annotations[AnnotationMSBuildLogLogsCount] = strconv.Itoa(len(report.Logs)) + m.Annotations[AnnotationMSBuildLogWarningsCount] = strconv.Itoa(report.Summary.Warnings) + m.Annotations[AnnotationMSBuildLogErrorsCount] = strconv.Itoa(report.Summary.Errors) } diff --git a/pkg/attestation/crafter/materials/build_log_test.go b/pkg/attestation/crafter/materials/msbuild_log_test.go similarity index 81% rename from pkg/attestation/crafter/materials/build_log_test.go rename to pkg/attestation/crafter/materials/msbuild_log_test.go index 37c5b4827..8f0e778d7 100644 --- a/pkg/attestation/crafter/materials/build_log_test.go +++ b/pkg/attestation/crafter/materials/msbuild_log_test.go @@ -40,7 +40,7 @@ const ( buildLogOtherMaterial = "./testdata/sbom-spdx.json" ) -func TestNewBuildLogCrafter(t *testing.T) { +func TestNewMSBuildLogCrafter(t *testing.T) { testCases := []struct { name string input *contractAPI.CraftingSchema_Material @@ -48,7 +48,7 @@ func TestNewBuildLogCrafter(t *testing.T) { }{ { name: "build log material", - input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_BUILD_LOG}, + input: &contractAPI.CraftingSchema_Material{Type: contractAPI.CraftingSchema_Material_MSBUILD_LOG}, }, { name: "other material type", @@ -59,7 +59,7 @@ func TestNewBuildLogCrafter(t *testing.T) { for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - _, err := materials.NewBuildLogCrafter(tc.input, nil, nil) + _, err := materials.NewMSBuildLogCrafter(tc.input, nil, nil) if tc.wantErr { assert.Error(t, err) return @@ -113,7 +113,7 @@ func writeOldBinlog(t *testing.T) string { return p } -func TestBuildLogCrafter_Craft(t *testing.T) { +func TestMSBuildLogCrafter_Craft(t *testing.T) { testCases := []struct { name string filePath func(t *testing.T) string @@ -139,50 +139,50 @@ func TestBuildLogCrafter_Craft(t *testing.T) { name: "msbuild binary log", filePath: func(*testing.T) string { return "./testdata/buildlog/sample.binlog" }, annotations: map[string]string{ - materials.AnnotationToolNameKey: buildlog.ToolMSBuild, - materials.AnnotationBuildLogLogsCount: "1", - materials.AnnotationBuildLogWarningsCount: "4", - materials.AnnotationBuildLogErrorsCount: "0", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationMSBuildLogLogsCount: "1", + materials.AnnotationMSBuildLogWarningsCount: "4", + materials.AnnotationMSBuildLogErrorsCount: "0", }, }, { name: "msbuild text log", filePath: func(*testing.T) string { return "./testdata/buildlog/msbuild.txt" }, annotations: map[string]string{ - materials.AnnotationToolNameKey: buildlog.ToolMSBuild, - materials.AnnotationBuildLogWarningsCount: "4", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationMSBuildLogWarningsCount: "4", }, }, { name: "visual c++ build log", filePath: func(*testing.T) string { return "./testdata/buildlog/vcbuild.htm" }, annotations: map[string]string{ - materials.AnnotationToolNameKey: buildlog.ToolVCBuild, - materials.AnnotationBuildLogWarningsCount: "4", + materials.AnnotationToolNameKey: buildlog.ToolVCBuild, + materials.AnnotationMSBuildLogWarningsCount: "4", }, }, { name: "archive of binary logs", filePath: writeBuildLogZip, annotations: map[string]string{ - materials.AnnotationToolNameKey: buildlog.ToolMSBuild, - materials.AnnotationBuildLogLogsCount: "2", - materials.AnnotationBuildLogWarningsCount: "4", + materials.AnnotationToolNameKey: buildlog.ToolMSBuild, + materials.AnnotationMSBuildLogLogsCount: "2", + materials.AnnotationMSBuildLogWarningsCount: "4", }, }, { name: "archive of binary and Visual C++ build logs", filePath: writeMixedBuildLogZip, annotations: map[string]string{ - materials.AnnotationToolsKey: `["msbuild","vcbuild"]`, - materials.AnnotationBuildLogLogsCount: "2", + materials.AnnotationToolsKey: `["msbuild","vcbuild"]`, + materials.AnnotationMSBuildLogLogsCount: "2", }, }, } schema := &contractAPI.CraftingSchema_Material{ Name: "build-log", - Type: contractAPI.CraftingSchema_Material_BUILD_LOG, + Type: contractAPI.CraftingSchema_Material_MSBUILD_LOG, } l := zerolog.Nop() @@ -195,7 +195,7 @@ func TestBuildLogCrafter_Craft(t *testing.T) { } backend := &casclient.CASBackend{Uploader: uploader} - crafter, err := materials.NewBuildLogCrafter(schema, backend, &l) + crafter, err := materials.NewMSBuildLogCrafter(schema, backend, &l) require.NoError(t, err) got, err := craftedMaterial(crafter.Craft(context.TODO(), tc.filePath(t))) @@ -205,7 +205,7 @@ func TestBuildLogCrafter_Craft(t *testing.T) { } require.NoError(t, err) - assert.Equal(t, contractAPI.CraftingSchema_Material_BUILD_LOG.String(), got.MaterialType.String()) + assert.Equal(t, contractAPI.CraftingSchema_Material_MSBUILD_LOG.String(), got.MaterialType.String()) assert.True(t, got.UploadedToCas) for k, v := range tc.annotations { assert.Equal(t, v, got.Annotations[k], k)