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

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)

Edited by Sam Roque-Worcel

Merge request reports

Loading