feat(attestation): implement attestation verification in glab
Description
This MR does the following:
- Calculate a SHA-256 hash of a file.
- Retrieve the Provenance attestation using the new attestations API (Introduced by Add attestation download API endpoint, add download_url)
- Performing a new provenance verification using the sigstore/sigstore-go go package.
In order to achieve this, two changes to dependencies have been introduced. These are:
- The installation of sigstore/sigstore-go
- Upgrading
client-goto 1.4.0. This is required so we can leverage the changes provided in feat(integrations): Add attestations integrations
What are "Sigstore bundles"?
The grouppipeline security group is working towards providing users with SLSA Level 3 Provenance Attestations. Quoting from the SLSA documentation, it states that attestations are:
It’s the verifiable information about software artifacts describing where, when, and how something was produced. For higher SLSA levels and more resilient integrity guarantees, provenance requirements are stricter and need a deeper, more technical understanding of the predicate. Describe how an artifact or set of artifacts was produced so that:
- Consumers of the provenance can verify that the artifact was built according to expectations.
- Others can rebuild the artifact, if desired.
As a simplified TL;DR, in the context of GitLab, a provenance statement is a JSON document that correlates the SHA-256 sum of an artifact with the build information. A worker then performs a digital signature, called a provenance attestation, stored as a "Sigstore Bundle" blob. This is a highly sought-after feature, particularly for our GitLab Ultimate customers.
Related Issues
- Issue: Attestation verification via glab CLI
- Add new API to client-go MR: Draft: Add new "attestation verify" command
How has this been tested?
This can be tested against the production environment APIs with the following command. Note the commands below don't require authentication because they're against a public project.
~/code/cli % make && ./bin/glab attestation verify ~/ATTEST_ME.txt -p sroque-worcel/test-slsa-worker
go build -trimpath -ldflags "-X main.commit=f1fdfa89 -X main.version=v1.77.0 -X main.debugMode=false -w -s" -o ./bin/glab gitlab.com/gitlab-org/cli/cmd/glab
Artifact provenance successfully verified. Signatures confirm /Users/samroque-worcel/ATTEST_ME.txt was attested by sroque-worcel/test-slsa-worker%
~/code/cli % make && ./bin/glab attestation verify ~/json-pretty.txt -p sroque-worcel/test-slsa-worker
go build -trimpath -ldflags "-X main.commit=f1fdfa89 -X main.version=v1.77.0 -X main.debugMode=false -w -s" -o ./bin/glab gitlab.com/gitlab-org/cli/cmd/glab
ERROR
Unable to find a provenance statement for 1f9e5808a340916aa5618ee13a893dcf9d4f7e2d42a254be0f7eb06a094ab8ea.
More verbose output:
make && GLAB_DEBUG_HTTP=1 ./bin/glab attestation verify ~/ATTEST_ME.txt -p sroque-worcel/test-slsa-worker
go build -trimpath -ldflags "-X main.commit=f1fdfa89 -X main.version=v1.77.0 -X main.debugMode=false -w -s" -o ./bin/glab gitlab.com/gitlab-org/cli/cmd/glab
REQUEST:
GET /api/v4/projects/sroque-worcel%2Ftest-slsa-worker/attestations/76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c HTTP/1.1
Host: gitlab.com
User-Agent: glab/v1.77.0 (darwin, arm64)
Accept: application/json
Authorization: [REDACTED]
Accept-Encoding: gzip
RESPONSE:
HTTP/2.0 200 OK
Cache-Control: max-age=0, private, must-revalidate
Cf-Cache-Status: MISS
Cf-Ray: 9a456b06cc54d9b2-AKL
Content-Security-Policy: default-src 'none'
Content-Type: application/json
Date: Wed, 26 Nov 2025 00:55:03 GMT
Etag: W/"78e2ae9db0dee4de9358bd533d77b4d8"
Gitlab-Lb: haproxy-main-56-lb-gprd
Gitlab-Sv: api-gke-us-east1-c
Link: <https://gitlab.com/api/v4/projects/sroque-worcel%2Ftest-slsa-worker/attestations/76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c?id=sroque-worcel%2Ftest-slsa-worker&page=1&per_page=20&subject_digest=76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c>; rel="first", <https://gitlab.com/api/v4/projects/sroque-worcel%2Ftest-slsa-worker/attestations/76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c?id=sroque-worcel%2Ftest-slsa-worker&page=1&per_page=20&subject_digest=76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c>; rel="last"
Nel: {"max_age": 0}
Ratelimit-Limit: 2000
Ratelimit-Name: throttle_authenticated_api
Ratelimit-Observed: 1
Ratelimit-Remaining: 1999
Ratelimit-Reset: 1764118560
Referrer-Policy: strict-origin-when-cross-origin
Server: cloudflare
Set-Cookie: __cf_bm=ZIvsIHpU.j1ZTTCmS85qRVQNTRU1GziCxZV5X5U_Wcs-1764118503-1.0.1.1-oWNgXahHszCpaDDyZtu7pmKyjkY5MEzLTPFLhKtz8wQHwAp.Wm2YFmQFA6diYa4vVAn30C9TAf6P6cwrZBPfKZtaOwtntSNhz_DgCr8B_fE; path=/; expires=Wed, 26-Nov-25 01:25:03 GMT; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Set-Cookie: _cfuvid=ZaVDpXY6tG6EO6D56OBfcV5htF48MxCIvzTPQxogBWw-1764118503923-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Strict-Transport-Security: max-age=31536000
Vary: Origin, Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"9a456b0721abd9b2-ATL","version":"1"}
X-Next-Page:
X-Page: 1
X-Per-Page: 20
X-Prev-Page:
X-Request-Id: 9a456b0721abd9b2-ATL
X-Runtime: 0.108493
X-Total: 1
X-Total-Pages: 1
[{"id":1,"iid":1,"created_at":"2025-10-07T20:59:27.085Z","updated_at":"2025-10-07T20:59:27.085Z","expire_at":"2027-10-07T20:59:26.967Z","project_id":72356192,"build_id":11637492236,"status":"success","predicate_kind":"provenance","predicate_type":"https://slsa.dev/provenance/v1","subject_digest":"76c34666f719ef14bd2b124a7db51e9c05e4db2e12a84800296d559064eebe2c","download_url":"https://gitlab.com/api/v4/projects/72356192/attestations/1/download"}]
REQUEST:
GET /api/v4/projects/sroque-worcel%2Ftest-slsa-worker/attestations/1/download HTTP/1.1
Host: gitlab.com
User-Agent: glab/v1.77.0 (darwin, arm64)
Accept: application/json
Authorization: [REDACTED]
Accept-Encoding: gzip
RESPONSE:
HTTP/2.0 200 OK
Content-Length: 21655
Accept-Ranges: bytes
Cache-Control: max-age=0, private, must-revalidate
Cf-Cache-Status: MISS
Cf-Ray: 9a456b099eb1d9b2-AKL
Content-Security-Policy: default-src 'none'
Content-Type: text/json
Date: Wed, 26 Nov 2025 00:55:04 GMT
Etag: W/"23548cc023df78433bc5fcceeccc2d79"
Gitlab-Lb: haproxy-main-02-lb-gprd
Gitlab-Sv: api-gke-us-east1-c
Nel: {"max_age": 0}
Ratelimit-Limit: 2000
Ratelimit-Name: throttle_authenticated_api
Ratelimit-Observed: 2
Ratelimit-Remaining: 1998
Ratelimit-Reset: 1764118560
Referrer-Policy: strict-origin-when-cross-origin
Server: cloudflare
Set-Cookie: __cf_bm=hhjbxYi69XbPh1wJwq1wS1GujJ8Yp.mg3wcqHGbkPDY-1764118504-1.0.1.1-OrGalC.IVcdFGbKzqQ5jH1DV3iHoUPkNFP5WeHp3cnhST6TZLQ3_ac79IuCFO_XR_rC.BjKRl1C9c0qT2ugd7cisbwjJAyqaaZGzPSo9_B4; path=/; expires=Wed, 26-Nov-25 01:25:04 GMT; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Set-Cookie: _cfuvid=c2ZhuzvdxDEfnu6h0l2iABG45b8VKF3c9FkLWETSQGk-1764118504504-0.0.1.1-604800000; path=/; domain=.gitlab.com; HttpOnly; Secure; SameSite=None
Strict-Transport-Security: max-age=31536000
Vary: Origin, Accept-Encoding
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
X-Gitlab-Meta: {"correlation_id":"9a456b09b60cd9b2-ATL","version":"1"}
X-Request-Id: 9a456b09b60cd9b2-ATL
X-Runtime: 0.282901
{"mediaType":"application/vnd.dev.sigstore.bundle.v0.3+json", "verificationMaterial":{"certificate":{"rawBytes":"MIIF2zCCBWCgAwIBAgIUaQ+U+6Yen7x8ggsePuCDB6iRtgEwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUxMDA3MjA1OTI2WhcNMjUxMDA3MjEwOTI2WjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFgkUqRg2+hKTDgEu4mkQwyzegHzvnGTgvh2MGngNiudMipGLSufnW4U9P+cWIKdUqYVbSwiZOFKBhq9kexdJGqOCBH8wggR7MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUOJj1iTs/i1/ALaREFVdIdHjIbSgwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wXwYDVR0RAQH/BFUwU4ZRaHR0cHM6Ly9naXRsYWIuY29tL3Nyb3F1ZS13b3JjZWwvdGVzdC1zbHNhLXdvcmtlci8vLmdpdGxhYi1jaS55bWxAcmVmcy9oZWFkcy9tYWluMCAGCisGAQQBg78wAQEEEmh0dHBzOi8vZ2l0bGFiLmNvbTAiBgorBgEEAYO/MAEIBBQMEmh0dHBzOi8vZ2l0bGFiLmNvbTBhBgorBgEEAYO/MAEJBFMMUWh0dHBzOi8vZ2l0bGFiLmNvbS9zcm9xdWUtd29yY2VsL3Rlc3Qtc2xzYS13b3JrZXIvLy5naXRsYWItY2kueW1sQHJlZnMvaGVhZHMvbWFpbjA4BgorBgEEAYO/MAEKBCoMKGVhZmEwYTY4MjBiN[...]
Artifact provenance successfully verified. Signatures confirm /Users/samroque-worcel/ATTEST_ME.txt was attested by sroque-worcel/test-slsa-worker%
