From 9f17aaba047ac65602d3f1222ab82c1daf6d85dd Mon Sep 17 00:00:00 2001 From: Asra Ali Date: Fri, 7 Oct 2022 16:00:05 -0500 Subject: [PATCH] gcb: add gcb compatibility Signed-off-by: Asra Ali --- cli/slsa-verifier/main_test.go | 9 +- .../v0.3/gcloud-container-gcs.json | 130 ++++++++++++++++++ .../v0.3/gcloud-container-gcs/index.json | 21 +++ verifiers/internal/gcb/intoto.go | 68 +++++++++ verifiers/internal/gcb/provenance.go | 24 ++-- verifiers/internal/gcb/provenance_test.go | 14 ++ .../gcb/testdata/gcloud-container-gcs.json | 117 ++++++++++++++++ 7 files changed, 372 insertions(+), 11 deletions(-) create mode 100644 cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs.json create mode 100644 cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs/index.json create mode 100644 verifiers/internal/gcb/intoto.go create mode 100644 verifiers/internal/gcb/testdata/gcloud-container-gcs.json diff --git a/cli/slsa-verifier/main_test.go b/cli/slsa-verifier/main_test.go index dff2fb54b..4ccb07bf5 100644 --- a/cli/slsa-verifier/main_test.go +++ b/cli/slsa-verifier/main_test.go @@ -821,6 +821,13 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) { provenance: "gcloud-container-github.json", source: "github.com/laurentsimon/gcb-tests", }, + { + name: "valid main branch gcs", + artifact: "gcloud-container-gcs", + provenance: "gcloud-container-gcs.json", + minversion: "v0.3", + source: "gs://slsa-tooling_cloudbuild/source/1663616632.078353-fc7db143dcc64b5f9fe71d0497125ca1.tgz", + }, { name: "mismatch input builder version", artifact: "gcloud-container-github", @@ -966,7 +973,7 @@ func Test_runVerifyGCBArtifactImage(t *testing.T) { t.Run(tt.name, func(t *testing.T) { t.Parallel() - checkVersions := getBuildersAndVersions(t, "", nil, GCB_ARTIFACT_IMAGE_BUILDERS) + checkVersions := getBuildersAndVersions(t, tt.minversion, nil, GCB_ARTIFACT_IMAGE_BUILDERS) if tt.noversion { checkVersions = []string{""} } diff --git a/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs.json b/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs.json new file mode 100644 index 000000000..4f1f0044e --- /dev/null +++ b/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs.json @@ -0,0 +1,130 @@ +{ + "image_summary": { + "digest": "sha256:71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c", + "fully_qualified_digest": "us-west2-docker.pkg.dev/slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3@sha256:71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c", + "registry": "us-west2-docker.pkg.dev", + "repository": "example-package-repo" + }, + "provenance_summary": { + "provenance": [ + { + "build": { + "intotoStatement": { + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.1", + "slsaProvenance": { + "builder": { + "id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.3" + }, + "materials": [ + { + "digest": { + "md5": "7d4467b5b74959c39c08b3c8545ffac1", + "sha256": "9c4e7e28bef5c9ffabea170a40bfb6e286e170100c0eb02d7e06c7dd8c2238e4" + }, + "uri": "gs://slsa-tooling_cloudbuild/source/1663616632.078353-fc7db143dcc64b5f9fe71d0497125ca1.tgz#1663616635134845" + } + ], + "metadata": { + "buildFinishedOn": "2022-09-19T19:44:34.152421Z", + "buildInvocationId": "393794dc-82ec-495b-8f84-659dec87d381", + "buildStartedOn": "2022-09-19T19:43:56.076114651Z" + }, + "recipe": { + "arguments": { + "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.Build", + "id": "393794dc-82ec-495b-8f84-659dec87d381", + "name": "projects/819720953812/locations/us-west2/builds/393794dc-82ec-495b-8f84-659dec87d381", + "options": { + "logging": "CLOUD_LOGGING_ONLY", + "pool": {}, + "requestedVerifyOption": "VERIFIED", + "sourceProvenanceHash": [ + "SHA256" + ] + }, + "sourceProvenance": { + "fileHashes": { + "gs://slsa-tooling_cloudbuild/source/1663616632.078353-fc7db143dcc64b5f9fe71d0497125ca1.tgz#1663616635134845": { + "fileHash": [ + { + "type": "SHA256", + "value": "nE5+KL71yf+r6hcKQL+24obhcBAMDrAtfgbH3YwiOOQ=" + }, + { + "type": "MD5", + "value": "fURntbdJWcOcCLPIVF/6wQ==" + } + ] + } + }, + "resolvedStorageSource": { + "bucket": "slsa-tooling_cloudbuild", + "generation": "1663616635134845", + "object": "source/1663616632.078353-fc7db143dcc64b5f9fe71d0497125ca1.tgz" + } + }, + "steps": [ + { + "args": [ + "build", + "-t", + "us-west2-docker.pkg.dev/slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3", + "." + ], + "name": "gcr.io/cloud-builders/docker", + "pullTiming": { + "endTime": "2022-09-19T19:44:02.300470459Z", + "startTime": "2022-09-19T19:44:02.296558439Z" + }, + "status": "SUCCESS", + "timing": { + "endTime": "2022-09-19T19:44:32.329696525Z", + "startTime": "2022-09-19T19:44:02.296558439Z" + } + } + ], + "substitutions": { + "_IMAGE_NAME": "slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3" + } + }, + "definedInMaterial": "-1", + "type": "https://cloudbuild.googleapis.com/CloudBuildSteps@v0.1" + } + }, + "subject": [ + { + "digest": { + "sha256": "71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c" + }, + "name": "https://us-west2-docker.pkg.dev/slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3" + }, + { + "digest": { + "sha256": "71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c" + }, + "name": "https://us-west2-docker.pkg.dev/slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3:latest" + } + ] + } + }, + "createTime": "2022-09-19T19:44:35.879537Z", + "envelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZSI6eyJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9jbG91ZGJ1aWxkLmdvb2dsZWFwaXMuY29tL0dvb2dsZUhvc3RlZFdvcmtlckB2MC4zIn0sIm1hdGVyaWFscyI6W3siZGlnZXN0Ijp7Im1kNSI6IjdkNDQ2N2I1Yjc0OTU5YzM5YzA4YjNjODU0NWZmYWMxIiwic2hhMjU2IjoiOWM0ZTdlMjhiZWY1YzlmZmFiZWExNzBhNDBiZmI2ZTI4NmUxNzAxMDBjMGViMDJkN2UwNmM3ZGQ4YzIyMzhlNCJ9LCJ1cmkiOiJnczovL3Nsc2EtdG9vbGluZ19jbG91ZGJ1aWxkL3NvdXJjZS8xNjYzNjE2NjMyLjA3ODM1My1mYzdkYjE0M2RjYzY0YjVmOWZlNzFkMDQ5NzEyNWNhMS50Z3ojMTY2MzYxNjYzNTEzNDg0NSJ9XSwibWV0YWRhdGEiOnsiYnVpbGRGaW5pc2hlZE9uIjoiMjAyMi0wOS0xOVQxOTo0NDozNC4xNTI0MjFaIiwiYnVpbGRJbnZvY2F0aW9uSWQiOiIzOTM3OTRkYy04MmVjLTQ5NWItOGY4NC02NTlkZWM4N2QzODEiLCJidWlsZFN0YXJ0ZWRPbiI6IjIwMjItMDktMTlUMTk6NDM6NTYuMDc2MTE0NjUxWiJ9LCJyZWNpcGUiOnsiYXJndW1lbnRzIjp7IkB0eXBlIjoidHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuZGV2dG9vbHMuY2xvdWRidWlsZC52MS5CdWlsZCIsImlkIjoiMzkzNzk0ZGMtODJlYy00OTViLThmODQtNjU5ZGVjODdkMzgxIiwibmFtZSI6InByb2plY3RzLzgxOTcyMDk1MzgxMi9sb2NhdGlvbnMvdXMtd2VzdDIvYnVpbGRzLzM5Mzc5NGRjLTgyZWMtNDk1Yi04Zjg0LTY1OWRlYzg3ZDM4MSIsIm9wdGlvbnMiOnsibG9nZ2luZyI6IkNMT1VEX0xPR0dJTkdfT05MWSIsInBvb2wiOnt9LCJyZXF1ZXN0ZWRWZXJpZnlPcHRpb24iOiJWRVJJRklFRCIsInNvdXJjZVByb3ZlbmFuY2VIYXNoIjpbIlNIQTI1NiJdfSwic291cmNlUHJvdmVuYW5jZSI6eyJmaWxlSGFzaGVzIjp7ImdzOi8vc2xzYS10b29saW5nX2Nsb3VkYnVpbGQvc291cmNlLzE2NjM2MTY2MzIuMDc4MzUzLWZjN2RiMTQzZGNjNjRiNWY5ZmU3MWQwNDk3MTI1Y2ExLnRneiMxNjYzNjE2NjM1MTM0ODQ1Ijp7ImZpbGVIYXNoIjpbeyJ0eXBlIjoiU0hBMjU2IiwidmFsdWUiOiJuRTUrS0w3MXlmK3I2aGNLUUwrMjRvYmhjQkFNRHJBdGZnYkgzWXdpT09RPSJ9LHsidHlwZSI6Ik1ENSIsInZhbHVlIjoiZlVSbnRiZEpXY09jQ0xQSVZGLzZ3UT09In1dfX0sInJlc29sdmVkU3RvcmFnZVNvdXJjZSI6eyJidWNrZXQiOiJzbHNhLXRvb2xpbmdfY2xvdWRidWlsZCIsImdlbmVyYXRpb24iOiIxNjYzNjE2NjM1MTM0ODQ1Iiwib2JqZWN0Ijoic291cmNlLzE2NjM2MTY2MzIuMDc4MzUzLWZjN2RiMTQzZGNjNjRiNWY5ZmU3MWQwNDk3MTI1Y2ExLnRneiJ9fSwic3RlcHMiOlt7ImFyZ3MiOlsiYnVpbGQiLCItdCIsInVzLXdlc3QyLWRvY2tlci5wa2cuZGV2L3Nsc2EtdG9vbGluZy9leGFtcGxlLXBhY2thZ2UtcmVwby9lMmUtZ2NiLXdvcmtmbG93X2Rpc3BhdGNoLW1haW4tY2xvdWRidWlsZC1zbHNhMyIsIi4iXSwibmFtZSI6Imdjci5pby9jbG91ZC1idWlsZGVycy9kb2NrZXIiLCJwdWxsVGltaW5nIjp7ImVuZFRpbWUiOiIyMDIyLTA5LTE5VDE5OjQ0OjAyLjMwMDQ3MDQ1OVoiLCJzdGFydFRpbWUiOiIyMDIyLTA5LTE5VDE5OjQ0OjAyLjI5NjU1ODQzOVoifSwic3RhdHVzIjoiU1VDQ0VTUyIsInRpbWluZyI6eyJlbmRUaW1lIjoiMjAyMi0wOS0xOVQxOTo0NDozMi4zMjk2OTY1MjVaIiwic3RhcnRUaW1lIjoiMjAyMi0wOS0xOVQxOTo0NDowMi4yOTY1NTg0MzlaIn19XSwic3Vic3RpdHV0aW9ucyI6eyJfSU1BR0VfTkFNRSI6InNsc2EtdG9vbGluZy9leGFtcGxlLXBhY2thZ2UtcmVwby9lMmUtZ2NiLXdvcmtmbG93X2Rpc3BhdGNoLW1haW4tY2xvdWRidWlsZC1zbHNhMyJ9fSwiZGVmaW5lZEluTWF0ZXJpYWwiOiItMSIsInR5cGUiOiJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vQ2xvdWRCdWlsZFN0ZXBzQHYwLjEifX0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL3Nsc2EuZGV2L3Byb3ZlbmFuY2UvdjAuMSIsInNsc2FQcm92ZW5hbmNlIjp7ImJ1aWxkZXIiOnsiaWQiOiJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vR29vZ2xlSG9zdGVkV29ya2VyQHYwLjMifSwibWF0ZXJpYWxzIjpbeyJkaWdlc3QiOnsibWQ1IjoiN2Q0NDY3YjViNzQ5NTljMzljMDhiM2M4NTQ1ZmZhYzEiLCJzaGEyNTYiOiI5YzRlN2UyOGJlZjVjOWZmYWJlYTE3MGE0MGJmYjZlMjg2ZTE3MDEwMGMwZWIwMmQ3ZTA2YzdkZDhjMjIzOGU0In0sInVyaSI6ImdzOi8vc2xzYS10b29saW5nX2Nsb3VkYnVpbGQvc291cmNlLzE2NjM2MTY2MzIuMDc4MzUzLWZjN2RiMTQzZGNjNjRiNWY5ZmU3MWQwNDk3MTI1Y2ExLnRneiMxNjYzNjE2NjM1MTM0ODQ1In1dLCJtZXRhZGF0YSI6eyJidWlsZEZpbmlzaGVkT24iOiIyMDIyLTA5LTE5VDE5OjQ0OjM0LjE1MjQyMVoiLCJidWlsZEludm9jYXRpb25JZCI6IjM5Mzc5NGRjLTgyZWMtNDk1Yi04Zjg0LTY1OWRlYzg3ZDM4MSIsImJ1aWxkU3RhcnRlZE9uIjoiMjAyMi0wOS0xOVQxOTo0Mzo1Ni4wNzYxMTQ2NTFaIn0sInJlY2lwZSI6eyJhcmd1bWVudHMiOnsiQHR5cGUiOiJ0eXBlLmdvb2dsZWFwaXMuY29tL2dvb2dsZS5kZXZ0b29scy5jbG91ZGJ1aWxkLnYxLkJ1aWxkIiwiaWQiOiIzOTM3OTRkYy04MmVjLTQ5NWItOGY4NC02NTlkZWM4N2QzODEiLCJuYW1lIjoicHJvamVjdHMvODE5NzIwOTUzODEyL2xvY2F0aW9ucy91cy13ZXN0Mi9idWlsZHMvMzkzNzk0ZGMtODJlYy00OTViLThmODQtNjU5ZGVjODdkMzgxIiwib3B0aW9ucyI6eyJsb2dnaW5nIjoiQ0xPVURfTE9HR0lOR19PTkxZIiwicG9vbCI6e30sInJlcXVlc3RlZFZlcmlmeU9wdGlvbiI6IlZFUklGSUVEIiwic291cmNlUHJvdmVuYW5jZUhhc2giOlsiU0hBMjU2Il19LCJzb3VyY2VQcm92ZW5hbmNlIjp7ImZpbGVIYXNoZXMiOnsiZ3M6Ly9zbHNhLXRvb2xpbmdfY2xvdWRidWlsZC9zb3VyY2UvMTY2MzYxNjYzMi4wNzgzNTMtZmM3ZGIxNDNkY2M2NGI1ZjlmZTcxZDA0OTcxMjVjYTEudGd6IzE2NjM2MTY2MzUxMzQ4NDUiOnsiZmlsZUhhc2giOlt7InR5cGUiOiJTSEEyNTYiLCJ2YWx1ZSI6Im5FNStLTDcxeWYrcjZoY0tRTCsyNG9iaGNCQU1EckF0ZmdiSDNZd2lPT1E9In0seyJ0eXBlIjoiTUQ1IiwidmFsdWUiOiJmVVJudGJkSldjT2NDTFBJVkYvNndRPT0ifV19fSwicmVzb2x2ZWRTdG9yYWdlU291cmNlIjp7ImJ1Y2tldCI6InNsc2EtdG9vbGluZ19jbG91ZGJ1aWxkIiwiZ2VuZXJhdGlvbiI6IjE2NjM2MTY2MzUxMzQ4NDUiLCJvYmplY3QiOiJzb3VyY2UvMTY2MzYxNjYzMi4wNzgzNTMtZmM3ZGIxNDNkY2M2NGI1ZjlmZTcxZDA0OTcxMjVjYTEudGd6In19LCJzdGVwcyI6W3siYXJncyI6WyJidWlsZCIsIi10IiwidXMtd2VzdDItZG9ja2VyLnBrZy5kZXYvc2xzYS10b29saW5nL2V4YW1wbGUtcGFja2FnZS1yZXBvL2UyZS1nY2Itd29ya2Zsb3dfZGlzcGF0Y2gtbWFpbi1jbG91ZGJ1aWxkLXNsc2EzIiwiLiJdLCJuYW1lIjoiZ2NyLmlvL2Nsb3VkLWJ1aWxkZXJzL2RvY2tlciIsInB1bGxUaW1pbmciOnsiZW5kVGltZSI6IjIwMjItMDktMTlUMTk6NDQ6MDIuMzAwNDcwNDU5WiIsInN0YXJ0VGltZSI6IjIwMjItMDktMTlUMTk6NDQ6MDIuMjk2NTU4NDM5WiJ9LCJzdGF0dXMiOiJTVUNDRVNTIiwidGltaW5nIjp7ImVuZFRpbWUiOiIyMDIyLTA5LTE5VDE5OjQ0OjMyLjMyOTY5NjUyNVoiLCJzdGFydFRpbWUiOiIyMDIyLTA5LTE5VDE5OjQ0OjAyLjI5NjU1ODQzOVoifX1dLCJzdWJzdGl0dXRpb25zIjp7Il9JTUFHRV9OQU1FIjoic2xzYS10b29saW5nL2V4YW1wbGUtcGFja2FnZS1yZXBvL2UyZS1nY2Itd29ya2Zsb3dfZGlzcGF0Y2gtbWFpbi1jbG91ZGJ1aWxkLXNsc2EzIn19LCJkZWZpbmVkSW5NYXRlcmlhbCI6Ii0xIiwidHlwZSI6Imh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9DbG91ZEJ1aWxkU3RlcHNAdjAuMSJ9fSwic3ViamVjdCI6W3siZGlnZXN0Ijp7InNoYTI1NiI6IjcxZWZjYmIzZjAzZjU5MTRkY2RlODdlMzhkMzgxM2Y2Mzk4MmI0NzVmNzY4ODdiYWJmMjVmNzZmZmY3NjBkM2MifSwibmFtZSI6Imh0dHBzOi8vdXMtd2VzdDItZG9ja2VyLnBrZy5kZXYvc2xzYS10b29saW5nL2V4YW1wbGUtcGFja2FnZS1yZXBvL2UyZS1nY2Itd29ya2Zsb3dfZGlzcGF0Y2gtbWFpbi1jbG91ZGJ1aWxkLXNsc2EzIn0seyJkaWdlc3QiOnsic2hhMjU2IjoiNzFlZmNiYjNmMDNmNTkxNGRjZGU4N2UzOGQzODEzZjYzOTgyYjQ3NWY3Njg4N2JhYmYyNWY3NmZmZjc2MGQzYyJ9LCJuYW1lIjoiaHR0cHM6Ly91cy13ZXN0Mi1kb2NrZXIucGtnLmRldi9zbHNhLXRvb2xpbmcvZXhhbXBsZS1wYWNrYWdlLXJlcG8vZTJlLWdjYi13b3JrZmxvd19kaXNwYXRjaC1tYWluLWNsb3VkYnVpbGQtc2xzYTM6bGF0ZXN0In1dfQ==", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "keyid": "projects/verified-builder/locations/us-west2/keyRings/attestor/cryptoKeys/builtByGCB/cryptoKeyVersions/1", + "sig": "MEYCIQDRCd9adBP6_ze18NxtlZ8yVqcETUGdpYQ8C1MKFEYNMAIhAMMFKpKDVbbUpxIcf_5vHUnuXEOMSybG9IS_43Glp6zH" + } + ] + }, + "kind": "BUILD", + "name": "projects/slsa-tooling/occurrences/970d284c-ab7a-4121-9a0e-52812595263b", + "noteName": "projects/verified-builder/notes/intoto_393794dc-82ec-495b-8f84-659dec87d381", + "resourceUri": "https://us-west2-docker.pkg.dev/slsa-tooling/example-package-repo/e2e-gcb-workflow_dispatch-main-cloudbuild-slsa3@sha256:71efcbb3f03f5914dcde87e38d3813f63982b475f76887babf25f76fff760d3c", + "updateTime": "2022-09-19T19:44:35.879537Z" + } + ] + } +} diff --git a/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs/index.json b/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs/index.json new file mode 100644 index 000000000..ecad9c134 --- /dev/null +++ b/cli/slsa-verifier/testdata/gcb_container/v0.3/gcloud-container-gcs/index.json @@ -0,0 +1,21 @@ +{ + "schemaVersion": 2, + "mediaType": "application/vnd.docker.distribution.manifest.v2+json", + "config": { + "mediaType": "application/vnd.docker.container.image.v1+json", + "size": 1748, + "digest": "sha256:40391da7d19c1900fc744f227bdcb229f9a14ce6390c1e76337361e2b7c7cc60" + }, + "layers": [ + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 804101, + "digest": "sha256:b9f88661235d25835ef747dab426861d51c4e9923b92623d422d7ac58eb123e9" + }, + { + "mediaType": "application/vnd.docker.image.rootfs.diff.tar.gzip", + "size": 608694, + "digest": "sha256:753a0c6a100dcd1b6ad628933020e3a9d388d7c05aa3f2dd8795196dd40f2a2e" + } + ] +} \ No newline at end of file diff --git a/verifiers/internal/gcb/intoto.go b/verifiers/internal/gcb/intoto.go new file mode 100644 index 000000000..de7c01541 --- /dev/null +++ b/verifiers/internal/gcb/intoto.go @@ -0,0 +1,68 @@ +package gcb + +// Copy of github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1 +// This holds an internal copy of in-toto-golang's structs for +// SLSA predicates to handle GCB's incompatibility with the +// published specification. Specifically, GCB provenance currently +// produces a string for ProvenancePredicate.Recipe.DefinedInMaterial +// rather than the compliant signed integer. + +import "time" + +const ( + // PredicateSLSAProvenance represents a build provenance for an artifact. + PredicateSLSAProvenance = "https://slsa.dev/provenance/v0.1" +) + +// ProvenancePredicate is the provenance predicate definition. +type ProvenancePredicate struct { + Builder ProvenanceBuilder `json:"builder"` + Recipe ProvenanceRecipe `json:"recipe"` + Metadata *ProvenanceMetadata `json:"metadata,omitempty"` + Materials []ProvenanceMaterial `json:"materials,omitempty"` +} + +// ProvenanceBuilder idenfifies the entity that executed the build steps. +type ProvenanceBuilder struct { + ID string `json:"id"` +} + +// ProvenanceRecipe describes the actions performed by the builder. +type ProvenanceRecipe struct { + Type string `json:"type"` + // DefinedInMaterial can be sent as the null pointer to indicate that + // the value is not present. + // DefinedInMaterial *int `json:"definedInMaterial,omitempty"` + EntryPoint string `json:"entryPoint"` + Arguments interface{} `json:"arguments,omitempty"` + Environment interface{} `json:"environment,omitempty"` +} + +// ProvenanceMetadata contains metadata for the built artifact. +type ProvenanceMetadata struct { + // Use pointer to make sure that the abscense of a time is not + // encoded as the Epoch time. + BuildStartedOn *time.Time `json:"buildStartedOn,omitempty"` + BuildFinishedOn *time.Time `json:"buildFinishedOn,omitempty"` + Completeness ProvenanceComplete `json:"completeness"` + Reproducible bool `json:"reproducible"` +} + +// ProvenanceMaterial defines the materials used to build an artifact. +type ProvenanceMaterial struct { + URI string `json:"uri"` + Digest DigestSet `json:"digest,omitempty"` +} + +// ProvenanceComplete indicates wheter the claims in build/recipe are complete. +// For in depth information refer to the specifictaion: +// https://github.com/in-toto/attestation/blob/v0.1.0/spec/predicates/provenance.md +type ProvenanceComplete struct { + Arguments bool `json:"arguments"` + Environment bool `json:"environment"` + Materials bool `json:"materials"` +} + +// DigestSet contains a set of digests. It is represented as a map from +// algorithm name to lowercase hex-encoded value. +type DigestSet map[string]string diff --git a/verifiers/internal/gcb/provenance.go b/verifiers/internal/gcb/provenance.go index 180a73d51..96514c54d 100644 --- a/verifiers/internal/gcb/provenance.go +++ b/verifiers/internal/gcb/provenance.go @@ -13,7 +13,6 @@ import ( "github.com/google/go-cmp/cmp" intoto "github.com/in-toto/in-toto-golang/in_toto" - slsa01 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.1" dsselib "github.com/secure-systems-lab/go-securesystemslib/dsse" serrors "github.com/slsa-framework/slsa-verifier/errors" @@ -29,7 +28,7 @@ var GCBBuilderIDs = []string{ type v01IntotoStatement struct { intoto.StatementHeader - Predicate slsa01.ProvenancePredicate `json:"predicate"` + Predicate ProvenancePredicate `json:"predicate"` } // The GCB provenance contains a human-readable version of the intoto @@ -38,7 +37,7 @@ type v01IntotoStatement struct { // by the GCB team. type v01GCBIntotoStatement struct { intoto.StatementHeader - SlsaProvenance slsa01.ProvenancePredicate `json:"slsaProvenance"` + SlsaProvenance ProvenancePredicate `json:"slsaProvenance"` } type provenance struct { @@ -72,7 +71,7 @@ func ProvenanceFromBytes(payload []byte) (*Provenance, error) { var prov gloudProvenance err := json.Unmarshal(payload, &prov) if err != nil { - return nil, fmt.Errorf("json.Unmarshal: %w", err) + return nil, fmt.Errorf("json.Unmarshal gcloud provenance: %w", err) } return &Provenance{ @@ -205,9 +204,9 @@ func (self *Provenance) VerifyIntotoHeaders() error { } // https://slsa.dev/provenance/v0.1 - if statement.StatementHeader.PredicateType != slsa01.PredicateSLSAProvenance { + if statement.StatementHeader.PredicateType != PredicateSLSAProvenance { return fmt.Errorf("%w: expected statement predicate type '%s', got '%s'", - serrors.ErrorInvalidDssePayload, slsa01.PredicateSLSAProvenance, statement.StatementHeader.PredicateType) + serrors.ErrorInvalidDssePayload, PredicateSLSAProvenance, statement.StatementHeader.PredicateType) } return nil @@ -357,7 +356,9 @@ func (self *Provenance) VerifySourceURI(expectedSourceURI string, builderID util return fmt.Errorf("%w: no materials", serrors.ErrorInvalidDssePayload) } uri := materials[0].URI - if !strings.HasPrefix(expectedSourceURI, "https://") { + + // It is possible that GCS builds at level 2 use GCS sources, prefixed by gs://. + if strings.HasPrefix(uri, "https://") && !strings.HasPrefix(expectedSourceURI, "https://") { expectedSourceURI = "https://" + expectedSourceURI } @@ -367,14 +368,17 @@ func (self *Provenance) VerifySourceURI(expectedSourceURI string, builderID util case "v0.2": // In v0.2, it uses format // `https://github.com/laurentsimon/gcb-tests/commit/01ce393d04eb6df2a7b2b3e95d4126e687afb7ae`. - if !strings.HasPrefix(uri, expectedSourceURI+"/commit/") { + if !strings.HasPrefix(uri, expectedSourceURI+"/commit/") && + !strings.HasPrefix(uri, expectedSourceURI+"#") { return fmt.Errorf("%w: expected '%s', got '%s'", serrors.ErrorMismatchSource, expectedSourceURI, uri) } // In v0.3, it uses the standard intoto and has the commit sha in its own // `digest.sha1` field. case "v0.3": - if uri != expectedSourceURI { + // The latter case is a versioned GCS source. + if uri != expectedSourceURI && + !strings.HasPrefix(uri, expectedSourceURI+"#") { return fmt.Errorf("%w: expected '%s', got '%s'", serrors.ErrorMismatchSource, expectedSourceURI, uri) } @@ -418,7 +422,7 @@ func decodeSignature(s string) ([]byte, []error) { } // verifySignatures iterates over all the signatures in the DSSE and verifies them. -// It succeeds if one of them can ne verified. +// It succeeds if one of them can be verified. func (self *Provenance) verifySignatures(prov *provenance) error { // Verify the envelope type. It should be an intoto type. if prov.Envelope.PayloadType != intoto.PayloadType { diff --git a/verifiers/internal/gcb/provenance_test.go b/verifiers/internal/gcb/provenance_test.go index 0d5ab9145..efb130541 100644 --- a/verifiers/internal/gcb/provenance_test.go +++ b/verifiers/internal/gcb/provenance_test.go @@ -44,6 +44,10 @@ func Test_VerifyIntotoHeaders(t *testing.T) { name: "valid gcb provenance", path: "./testdata/gcloud-container-github.json", }, + { + name: "valid gcb provenance gcs", + path: "./testdata/gcloud-container-gcs.json", + }, { name: "invalid intoto header", path: "./testdata/gcloud-container-invalid-intotoheader.json", @@ -95,6 +99,11 @@ func Test_VerifyBuilder(t *testing.T) { path: "./testdata/gcloud-container-github.json", builderID: "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.2", }, + { + name: "valid gcb provenance gcs", + path: "./testdata/gcloud-container-gcs.json", + builderID: "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.3", + }, { name: "mismatch builder.id version", path: "./testdata/gcloud-container-github.json", @@ -537,6 +546,11 @@ func Test_VerifySummary(t *testing.T) { path: "./testdata/gcloud-container-github.json", hash: "1a033b002f89ed2b8ea733162497fb70f1a4049a7f8602d6a33682b4ad9921fd", }, + { + name: "valid gcb provenance gcs", + path: "./testdata/gcloud-container-gcs.json", + hash: "9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe", + }, { name: "mismatch digest", path: "./testdata/gcloud-container-github.json", diff --git a/verifiers/internal/gcb/testdata/gcloud-container-gcs.json b/verifiers/internal/gcb/testdata/gcloud-container-gcs.json new file mode 100644 index 000000000..4e49856e9 --- /dev/null +++ b/verifiers/internal/gcb/testdata/gcloud-container-gcs.json @@ -0,0 +1,117 @@ +{ + "image_summary": { + "digest": "sha256:9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe", + "fully_qualified_digest": "us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend@sha256:9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe", + "registry": "us-central1-docker.pkg.dev", + "repository": "containers" + }, + "provenance_summary": { + "provenance": [ + { + "build": { + "intotoStatement": { + "_type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v0.1", + "slsaProvenance": { + "builder": { + "id": "https://cloudbuild.googleapis.com/GoogleHostedWorker@v0.3" + }, + "materials": [ + { + "digest": { + "md5": "6e9c2c03099262d534519d106fe04b08" + }, + "uri": "gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729" + } + ], + "metadata": { + "buildFinishedOn": "2022-10-07T17:57:16.595464Z", + "buildInvocationId": "565456b1-0394-4c71-8c18-79f9057483df", + "buildStartedOn": "2022-10-07T17:56:01.814040892Z" + }, + "recipe": { + "arguments": { + "@type": "type.googleapis.com/google.devtools.cloudbuild.v1.Build", + "id": "565456b1-0394-4c71-8c18-79f9057483df", + "name": "projects/171719165453/locations/us-central1/builds/565456b1-0394-4c71-8c18-79f9057483df", + "options": { + "dynamicSubstitutions": true, + "logging": "LEGACY", + "pool": {}, + "requestedVerifyOption": "VERIFIED" + }, + "sourceProvenance": { + "fileHashes": { + "gs://damith-sds_cloudbuild/source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz#1665165361152729": { + "fileHash": [ + { + "type": "MD5", + "value": "bpwsAwmSYtU0UZ0Qb+BLCA==" + } + ] + } + }, + "resolvedStorageSource": { + "bucket": "damith-sds_cloudbuild", + "generation": "1665165361152729", + "object": "source/1665165360.279777-955d1904741e4bbeb3461080299e929a.tgz" + } + }, + "steps": [ + { + "args": [ + "-c", + "docker build -t us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart .\ndocker push us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart\n" + ], + "entrypoint": "/bin/bash", + "id": "Build and Push Container Image: Backend", + "name": "gcr.io/cloud-builders/docker", + "pullTiming": { + "endTime": "2022-10-07T17:56:08.614390837Z", + "startTime": "2022-10-07T17:56:08.610302991Z" + }, + "status": "SUCCESS", + "timing": { + "endTime": "2022-10-07T17:57:15.136042514Z", + "startTime": "2022-10-07T17:56:08.610302991Z" + } + } + ], + "substitutions": { + "_BACKEND_IMAGE": "us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart" + } + }, + "definedInMaterial": "-1", + "type": "https://cloudbuild.googleapis.com/CloudBuildSteps@v0.1" + } + }, + "subject": [ + { + "digest": { + "sha256": "9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe" + }, + "name": "https://us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend:quickstart" + } + ] + } + }, + "createTime": "2022-10-07T17:57:17.438466Z", + "envelope": { + "payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZSI6eyJidWlsZGVyIjp7ImlkIjoiaHR0cHM6Ly9jbG91ZGJ1aWxkLmdvb2dsZWFwaXMuY29tL0dvb2dsZUhvc3RlZFdvcmtlckB2MC4zIn0sIm1hdGVyaWFscyI6W3siZGlnZXN0Ijp7Im1kNSI6IjZlOWMyYzAzMDk5MjYyZDUzNDUxOWQxMDZmZTA0YjA4In0sInVyaSI6ImdzOi8vZGFtaXRoLXNkc19jbG91ZGJ1aWxkL3NvdXJjZS8xNjY1MTY1MzYwLjI3OTc3Ny05NTVkMTkwNDc0MWU0YmJlYjM0NjEwODAyOTllOTI5YS50Z3ojMTY2NTE2NTM2MTE1MjcyOSJ9XSwibWV0YWRhdGEiOnsiYnVpbGRGaW5pc2hlZE9uIjoiMjAyMi0xMC0wN1QxNzo1NzoxNi41OTU0NjRaIiwiYnVpbGRJbnZvY2F0aW9uSWQiOiI1NjU0NTZiMS0wMzk0LTRjNzEtOGMxOC03OWY5MDU3NDgzZGYiLCJidWlsZFN0YXJ0ZWRPbiI6IjIwMjItMTAtMDdUMTc6NTY6MDEuODE0MDQwODkyWiJ9LCJyZWNpcGUiOnsiYXJndW1lbnRzIjp7IkB0eXBlIjoidHlwZS5nb29nbGVhcGlzLmNvbS9nb29nbGUuZGV2dG9vbHMuY2xvdWRidWlsZC52MS5CdWlsZCIsImlkIjoiNTY1NDU2YjEtMDM5NC00YzcxLThjMTgtNzlmOTA1NzQ4M2RmIiwibmFtZSI6InByb2plY3RzLzE3MTcxOTE2NTQ1My9sb2NhdGlvbnMvdXMtY2VudHJhbDEvYnVpbGRzLzU2NTQ1NmIxLTAzOTQtNGM3MS04YzE4LTc5ZjkwNTc0ODNkZiIsIm9wdGlvbnMiOnsiZHluYW1pY1N1YnN0aXR1dGlvbnMiOnRydWUsImxvZ2dpbmciOiJMRUdBQ1kiLCJwb29sIjp7fSwicmVxdWVzdGVkVmVyaWZ5T3B0aW9uIjoiVkVSSUZJRUQifSwic291cmNlUHJvdmVuYW5jZSI6eyJmaWxlSGFzaGVzIjp7ImdzOi8vZGFtaXRoLXNkc19jbG91ZGJ1aWxkL3NvdXJjZS8xNjY1MTY1MzYwLjI3OTc3Ny05NTVkMTkwNDc0MWU0YmJlYjM0NjEwODAyOTllOTI5YS50Z3ojMTY2NTE2NTM2MTE1MjcyOSI6eyJmaWxlSGFzaCI6W3sidHlwZSI6Ik1ENSIsInZhbHVlIjoiYnB3c0F3bVNZdFUwVVowUWIrQkxDQT09In1dfX0sInJlc29sdmVkU3RvcmFnZVNvdXJjZSI6eyJidWNrZXQiOiJkYW1pdGgtc2RzX2Nsb3VkYnVpbGQiLCJnZW5lcmF0aW9uIjoiMTY2NTE2NTM2MTE1MjcyOSIsIm9iamVjdCI6InNvdXJjZS8xNjY1MTY1MzYwLjI3OTc3Ny05NTVkMTkwNDc0MWU0YmJlYjM0NjEwODAyOTllOTI5YS50Z3oifX0sInN0ZXBzIjpbeyJhcmdzIjpbIi1jIiwiZG9ja2VyIGJ1aWxkIC10IHVzLWNlbnRyYWwxLWRvY2tlci5wa2cuZGV2L2RhbWl0aC1zZHMvY29udGFpbmVycy9qYXZhLWd1ZXN0Ym9vay1iYWNrZW5kOnF1aWNrc3RhcnQgLlxuZG9ja2VyIHB1c2ggdXMtY2VudHJhbDEtZG9ja2VyLnBrZy5kZXYvZGFtaXRoLXNkcy9jb250YWluZXJzL2phdmEtZ3Vlc3Rib29rLWJhY2tlbmQ6cXVpY2tzdGFydFxuIl0sImVudHJ5cG9pbnQiOiIvYmluL2Jhc2giLCJpZCI6IkJ1aWxkIGFuZCBQdXNoIENvbnRhaW5lciBJbWFnZTogQmFja2VuZCIsIm5hbWUiOiJnY3IuaW8vY2xvdWQtYnVpbGRlcnMvZG9ja2VyIiwicHVsbFRpbWluZyI6eyJlbmRUaW1lIjoiMjAyMi0xMC0wN1QxNzo1NjowOC42MTQzOTA4MzdaIiwic3RhcnRUaW1lIjoiMjAyMi0xMC0wN1QxNzo1NjowOC42MTAzMDI5OTFaIn0sInN0YXR1cyI6IlNVQ0NFU1MiLCJ0aW1pbmciOnsiZW5kVGltZSI6IjIwMjItMTAtMDdUMTc6NTc6MTUuMTM2MDQyNTE0WiIsInN0YXJ0VGltZSI6IjIwMjItMTAtMDdUMTc6NTY6MDguNjEwMzAyOTkxWiJ9fV0sInN1YnN0aXR1dGlvbnMiOnsiX0JBQ0tFTkRfSU1BR0UiOiJ1cy1jZW50cmFsMS1kb2NrZXIucGtnLmRldi9kYW1pdGgtc2RzL2NvbnRhaW5lcnMvamF2YS1ndWVzdGJvb2stYmFja2VuZDpxdWlja3N0YXJ0In19LCJkZWZpbmVkSW5NYXRlcmlhbCI6Ii0xIiwidHlwZSI6Imh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9DbG91ZEJ1aWxkU3RlcHNAdjAuMSJ9fSwicHJlZGljYXRlVHlwZSI6Imh0dHBzOi8vc2xzYS5kZXYvcHJvdmVuYW5jZS92MC4xIiwic2xzYVByb3ZlbmFuY2UiOnsiYnVpbGRlciI6eyJpZCI6Imh0dHBzOi8vY2xvdWRidWlsZC5nb29nbGVhcGlzLmNvbS9Hb29nbGVIb3N0ZWRXb3JrZXJAdjAuMyJ9LCJtYXRlcmlhbHMiOlt7ImRpZ2VzdCI6eyJtZDUiOiI2ZTljMmMwMzA5OTI2MmQ1MzQ1MTlkMTA2ZmUwNGIwOCJ9LCJ1cmkiOiJnczovL2RhbWl0aC1zZHNfY2xvdWRidWlsZC9zb3VyY2UvMTY2NTE2NTM2MC4yNzk3NzctOTU1ZDE5MDQ3NDFlNGJiZWIzNDYxMDgwMjk5ZTkyOWEudGd6IzE2NjUxNjUzNjExNTI3MjkifV0sIm1ldGFkYXRhIjp7ImJ1aWxkRmluaXNoZWRPbiI6IjIwMjItMTAtMDdUMTc6NTc6MTYuNTk1NDY0WiIsImJ1aWxkSW52b2NhdGlvbklkIjoiNTY1NDU2YjEtMDM5NC00YzcxLThjMTgtNzlmOTA1NzQ4M2RmIiwiYnVpbGRTdGFydGVkT24iOiIyMDIyLTEwLTA3VDE3OjU2OjAxLjgxNDA0MDg5MloifSwicmVjaXBlIjp7ImFyZ3VtZW50cyI6eyJAdHlwZSI6InR5cGUuZ29vZ2xlYXBpcy5jb20vZ29vZ2xlLmRldnRvb2xzLmNsb3VkYnVpbGQudjEuQnVpbGQiLCJpZCI6IjU2NTQ1NmIxLTAzOTQtNGM3MS04YzE4LTc5ZjkwNTc0ODNkZiIsIm5hbWUiOiJwcm9qZWN0cy8xNzE3MTkxNjU0NTMvbG9jYXRpb25zL3VzLWNlbnRyYWwxL2J1aWxkcy81NjU0NTZiMS0wMzk0LTRjNzEtOGMxOC03OWY5MDU3NDgzZGYiLCJvcHRpb25zIjp7ImR5bmFtaWNTdWJzdGl0dXRpb25zIjp0cnVlLCJsb2dnaW5nIjoiTEVHQUNZIiwicG9vbCI6e30sInJlcXVlc3RlZFZlcmlmeU9wdGlvbiI6IlZFUklGSUVEIn0sInNvdXJjZVByb3ZlbmFuY2UiOnsiZmlsZUhhc2hlcyI6eyJnczovL2RhbWl0aC1zZHNfY2xvdWRidWlsZC9zb3VyY2UvMTY2NTE2NTM2MC4yNzk3NzctOTU1ZDE5MDQ3NDFlNGJiZWIzNDYxMDgwMjk5ZTkyOWEudGd6IzE2NjUxNjUzNjExNTI3MjkiOnsiZmlsZUhhc2giOlt7InR5cGUiOiJNRDUiLCJ2YWx1ZSI6ImJwd3NBd21TWXRVMFVaMFFiK0JMQ0E9PSJ9XX19LCJyZXNvbHZlZFN0b3JhZ2VTb3VyY2UiOnsiYnVja2V0IjoiZGFtaXRoLXNkc19jbG91ZGJ1aWxkIiwiZ2VuZXJhdGlvbiI6IjE2NjUxNjUzNjExNTI3MjkiLCJvYmplY3QiOiJzb3VyY2UvMTY2NTE2NTM2MC4yNzk3NzctOTU1ZDE5MDQ3NDFlNGJiZWIzNDYxMDgwMjk5ZTkyOWEudGd6In19LCJzdGVwcyI6W3siYXJncyI6WyItYyIsImRvY2tlciBidWlsZCAtdCB1cy1jZW50cmFsMS1kb2NrZXIucGtnLmRldi9kYW1pdGgtc2RzL2NvbnRhaW5lcnMvamF2YS1ndWVzdGJvb2stYmFja2VuZDpxdWlja3N0YXJ0IC5cbmRvY2tlciBwdXNoIHVzLWNlbnRyYWwxLWRvY2tlci5wa2cuZGV2L2RhbWl0aC1zZHMvY29udGFpbmVycy9qYXZhLWd1ZXN0Ym9vay1iYWNrZW5kOnF1aWNrc3RhcnRcbiJdLCJlbnRyeXBvaW50IjoiL2Jpbi9iYXNoIiwiaWQiOiJCdWlsZCBhbmQgUHVzaCBDb250YWluZXIgSW1hZ2U6IEJhY2tlbmQiLCJuYW1lIjoiZ2NyLmlvL2Nsb3VkLWJ1aWxkZXJzL2RvY2tlciIsInB1bGxUaW1pbmciOnsiZW5kVGltZSI6IjIwMjItMTAtMDdUMTc6NTY6MDguNjE0MzkwODM3WiIsInN0YXJ0VGltZSI6IjIwMjItMTAtMDdUMTc6NTY6MDguNjEwMzAyOTkxWiJ9LCJzdGF0dXMiOiJTVUNDRVNTIiwidGltaW5nIjp7ImVuZFRpbWUiOiIyMDIyLTEwLTA3VDE3OjU3OjE1LjEzNjA0MjUxNFoiLCJzdGFydFRpbWUiOiIyMDIyLTEwLTA3VDE3OjU2OjA4LjYxMDMwMjk5MVoifX1dLCJzdWJzdGl0dXRpb25zIjp7Il9CQUNLRU5EX0lNQUdFIjoidXMtY2VudHJhbDEtZG9ja2VyLnBrZy5kZXYvZGFtaXRoLXNkcy9jb250YWluZXJzL2phdmEtZ3Vlc3Rib29rLWJhY2tlbmQ6cXVpY2tzdGFydCJ9fSwiZGVmaW5lZEluTWF0ZXJpYWwiOiItMSIsInR5cGUiOiJodHRwczovL2Nsb3VkYnVpbGQuZ29vZ2xlYXBpcy5jb20vQ2xvdWRCdWlsZFN0ZXBzQHYwLjEifX0sInN1YmplY3QiOlt7ImRpZ2VzdCI6eyJzaGEyNTYiOiI5ZGNmYWNjNDk3YjYxYzRkMmZmNTcwOGU2NDRjMDYwNzI2NzgxZmFlNTE0ZGM4YmE3MWM0OWRjZWQ2NzViY2JlIn0sIm5hbWUiOiJodHRwczovL3VzLWNlbnRyYWwxLWRvY2tlci5wa2cuZGV2L2RhbWl0aC1zZHMvY29udGFpbmVycy9qYXZhLWd1ZXN0Ym9vay1iYWNrZW5kOnF1aWNrc3RhcnQifV19", + "payloadType": "application/vnd.in-toto+json", + "signatures": [ + { + "keyid": "projects/verified-builder/locations/us-central1/keyRings/attestor/cryptoKeys/builtByGCB/cryptoKeyVersions/1", + "sig": "MEUCIDfWuzwDVjmMmKdI_GE8s2eVjpKD80Bb5I9NYMxPc0tVAiEAvKVLpYl-uekpYq8ittlLN_5yMo5dle1GXVPvS5InAVw=" + } + ] + }, + "kind": "BUILD", + "name": "projects/damith-sds/occurrences/26129661-b842-42ff-9b67-7198ac893395", + "noteName": "projects/verified-builder/notes/intoto_565456b1-0394-4c71-8c18-79f9057483df", + "resourceUri": "https://us-central1-docker.pkg.dev/damith-sds/containers/java-guestbook-backend@sha256:9dcfacc497b61c4d2ff5708e644c060726781fae514dc8ba71c49dced675bcbe", + "updateTime": "2022-10-07T17:57:17.438466Z" + } + ] + } +}