Resolve "Create Attestations List API"
What does this MR do and why?
This MR and the involved changes
This MR creates a new endpoint that allows users to retrieve a list of SupplyChain::Attestation objects for a project. There may be more than one per subject_digest if multiple attestations are created against a single artifact. Access to the API endpoint is restricted by FF.
A subsequent MR will allow for the retrieval of Sigstore bundles from object storage.
What are "SLSA provenance statements"?
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, which is called a provenance attestation, stored as a "Sigstore Bundle" blob. This is a highly sought-after feature, particularly for our GitLab Ultimate customers.
Security Considerations
Attestations can be retrieved if a user has access to a project. This is further discussed in Create Attestations List API.
References
- Create Attestations List API
- SLSA Level 3 Provenance Attestations
- [Discussion] UX to enable SLSA provenance generation
- [FF]
slsa_provenance_statement-- Roll out feature flag to publish SLSA provenance statements
Testing
To test access, I'm performing testing with three levels of access: unauthenticated access, access with a PAT belonging to a user "lowleveluser" and a PAT belonging to "root"
There are three projects (all with enabled FF)
private project, belonging to lowleveluser. ID: 19
private project, belonging to another user ID: 8
public project: ID: 12
I've created an attestation for each:
[19, 8, 12].each do |project_id|
Feature.enable(:slsa_provenance_statement, Project.find(project_id))
a = SupplyChain::Attestation.new do |a|
a.subject_digest = "5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"
a.predicate_type = "awfawfaf"
a.file = Tempfile.new
a.project_id = project_id
end
a.save
end
Unauthenticated access:
% curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/19/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"
{"message":"404 Project Not Found"}
% curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/8/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"
{"message":"404 Project Not Found"}
% curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/12/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"
[{"id":7,"created_at":"2025-09-25T22:46:26.048Z","updated_at":"2025-09-25T22:46:26.048Z","expire_at":null,"project_id":12,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]%
Low level user
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/8/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN_USER"
{"message":"404 Project Not Found"}%
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/12/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN_USER"
[{"id":7,"created_at":"2025-09-25T22:46:26.048Z","updated_at":"2025-09-25T22:46:26.048Z","expire_at":null,"project_id":12,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]%
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/19/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN_USER"
[{"id":2,"created_at":"2025-09-25T22:43:00.846Z","updated_at":"2025-09-25T22:43:00.846Z","expire_at":null,"project_id":19,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]
root
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/19/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN"
[{"id":2,"created_at":"2025-09-25T22:43:00.846Z","updated_at":"2025-09-25T22:43:00.846Z","expire_at":null,"project_id":19,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]%
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/12/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN"
[{"id":7,"created_at":"2025-09-25T22:46:26.048Z","updated_at":"2025-09-25T22:46:26.048Z","expire_at":null,"project_id":12,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]%
~/code/gdk/gitlab % curl --location --output - -q --url "http://gdk.test:3000/api/v4/projects/8/attestations/5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa?private_token=PRIVATE_TOKEN"
[{"id":3,"created_at":"2025-09-25T22:43:00.851Z","updated_at":"2025-09-25T22:43:00.851Z","expire_at":null,"project_id":8,"build_id":null,"status":"success","predicate_kind":"provenance","predicate_type":"awfawfaf","subject_digest":"5db1fee4b5703808c48078a76768b155b421b210c0761cd6a5d223f4d99f1eaa"}]
MR acceptance checklist
Evaluate this MR against the MR acceptance checklist. It helps you analyse changes to reduce risks in quality, performance, reliability, security, and maintainability.
Related to #566580 (closed)