42 Commits

Author SHA1 Message Date
James Carnegie
da667de610 feat: support arbitrary rego input parameters (#196)
* feat: support arbitrary rego input parameters
2024-10-15 16:07:26 +01:00
Joel Kamp
7027d2d054 Merge pull request #188 from docker/dependabot/go_modules/github.com/sigstore/cosign/v2-2.4.1
feat(deps): bump github.com/sigstore/cosign/v2 from 2.4.0 to 2.4.1
2024-10-15 09:37:02 -05:00
mrjoelkamp
163c1828e3 chore: go mod tidy 2024-10-15 09:28:32 -05:00
dependabot[bot]
168a574c15 feat(deps): bump github.com/sigstore/cosign/v2 from 2.4.0 to 2.4.1
Bumps [github.com/sigstore/cosign/v2](https://github.com/sigstore/cosign) from 2.4.0 to 2.4.1.
- [Release notes](https://github.com/sigstore/cosign/releases)
- [Changelog](https://github.com/sigstore/cosign/blob/main/CHANGELOG.md)
- [Commits](https://github.com/sigstore/cosign/compare/v2.4.0...v2.4.1)

---
updated-dependencies:
- dependency-name: github.com/sigstore/cosign/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 14:27:14 +00:00
Joel Kamp
ad2f8befa2 Merge pull request #195 from docker/dependabot/go_modules/google.golang.org/api-0.200.0
feat(deps): bump google.golang.org/api from 0.199.0 to 0.200.0
2024-10-15 08:53:56 -05:00
dependabot[bot]
8460357880 feat(deps): bump google.golang.org/api from 0.199.0 to 0.200.0
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.199.0 to 0.200.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.199.0...v0.200.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-15 13:44:46 +00:00
Joel Kamp
994240018e Merge pull request #187 from docker/dependabot/go_modules/github.com/containerd/containerd/v2-2.0.0-rc.5
feat(deps): bump github.com/containerd/containerd/v2 from 2.0.0-rc.4 to 2.0.0-rc.5
2024-10-15 08:42:03 -05:00
Joel Kamp
5c51ee7c19 Merge pull request #194 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.43
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.39 to 1.27.43
2024-10-15 08:36:49 -05:00
Joel Kamp
8ae43ba5e9 Merge branch 'main' into dependabot/go_modules/github.com/containerd/containerd/v2-2.0.0-rc.5 2024-10-15 08:33:48 -05:00
dependabot[bot]
ec659e62cd feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.39 to 1.27.43.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.39...config/v1.27.43)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-09 08:42:48 +00:00
Joel Kamp
2d7f6cae3c Merge pull request #191 from docker/feat-vsa-input-attestations
feat: vsa input attestations
2024-10-08 08:30:06 -05:00
mrjoelkamp
a686de72fd feat: add input atts to result summary 2024-10-07 15:07:21 -05:00
mrjoelkamp
d58ce0c600 feat: add reference wrapper for envelope 2024-10-07 13:34:04 -05:00
dependabot[bot]
bf33de5b48 feat(deps): bump github.com/theupdateframework/go-tuf/v2 (#186)
Bumps [github.com/theupdateframework/go-tuf/v2](https://github.com/theupdateframework/go-tuf) from 2.0.1 to 2.0.2.
- [Release notes](https://github.com/theupdateframework/go-tuf/releases)
- [Changelog](https://github.com/theupdateframework/go-tuf/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/theupdateframework/go-tuf/compare/v2.0.1...v2.0.2)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/go-tuf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-10-02 10:05:46 +01:00
dependabot[bot]
b8ca85152d feat(deps): bump github.com/containerd/containerd/v2
Bumps [github.com/containerd/containerd/v2](https://github.com/containerd/containerd) from 2.0.0-rc.4 to 2.0.0-rc.5.
- [Release notes](https://github.com/containerd/containerd/releases)
- [Changelog](https://github.com/containerd/containerd/blob/main/RELEASES.md)
- [Commits](https://github.com/containerd/containerd/compare/v2.0.0-rc.4...v2.0.0-rc.5)

---
updated-dependencies:
- dependency-name: github.com/containerd/containerd/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-02 08:36:57 +00:00
Joel Kamp
e06d8736df Merge pull request #182 from docker/dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.39
feat(deps): bump github.com/aws/aws-sdk-go-v2/config from 1.27.38 to 1.27.39
2024-10-01 16:02:00 -05:00
Joel Kamp
fcf98ebc3f Merge branch 'main' into dependabot/go_modules/github.com/aws/aws-sdk-go-v2/config-1.27.39 2024-10-01 15:46:46 -05:00
Joel Kamp
acd8d427a1 Merge pull request #185 from docker/dependabot/go_modules/github.com/open-policy-agent/opa-0.69.0
feat(deps): bump github.com/open-policy-agent/opa from 0.68.0 to 0.69.0
2024-10-01 15:46:34 -05:00
Joel Kamp
f2f13933df Merge branch 'main' into dependabot/go_modules/github.com/open-policy-agent/opa-0.69.0 2024-10-01 15:42:13 -05:00
Joel Kamp
503410bb7b Merge pull request #184 from docker/dependabot/go_modules/github.com/theupdateframework/go-tuf/v2-2.0.1
feat(deps): bump github.com/theupdateframework/go-tuf/v2 from 2.0.0 to 2.0.1
2024-10-01 15:41:54 -05:00
dependabot[bot]
ac04e8a9ea feat(deps): bump github.com/open-policy-agent/opa from 0.68.0 to 0.69.0
Bumps [github.com/open-policy-agent/opa](https://github.com/open-policy-agent/opa) from 0.68.0 to 0.69.0.
- [Release notes](https://github.com/open-policy-agent/opa/releases)
- [Changelog](https://github.com/open-policy-agent/opa/blob/main/CHANGELOG.md)
- [Commits](https://github.com/open-policy-agent/opa/compare/v0.68.0...v0.69.0)

---
updated-dependencies:
- dependency-name: github.com/open-policy-agent/opa
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 08:30:48 +00:00
dependabot[bot]
e3927acf17 feat(deps): bump github.com/theupdateframework/go-tuf/v2
Bumps [github.com/theupdateframework/go-tuf/v2](https://github.com/theupdateframework/go-tuf) from 2.0.0 to 2.0.1.
- [Release notes](https://github.com/theupdateframework/go-tuf/releases)
- [Changelog](https://github.com/theupdateframework/go-tuf/blob/master/.goreleaser.yaml)
- [Commits](https://github.com/theupdateframework/go-tuf/compare/v2.0.0...v2.0.1)

---
updated-dependencies:
- dependency-name: github.com/theupdateframework/go-tuf/v2
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-10-01 08:30:33 +00:00
James Carnegie
c0510fb76c Support images as well as indexes in ImageDetailResolvers (#183)
* build: Generate test data for unsigned and no provenance image indexes
* feat: Add function to build index without SBOM or provenance for linux/amd64 platform
* feat: add build_image function to build image without SBOM or provenance for linux/amd64
* feat: Rename NO_SBOM_NO_PROVENANCE_INDEX_DIR to UNSIGNED_IMAGE_DIR
* feat: support images in details resolvers
2024-09-30 20:53:13 +01:00
dependabot[bot]
251506fd9b feat(deps): bump github.com/aws/aws-sdk-go-v2/config
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.38 to 1.27.39.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.38...config/v1.27.39)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 08:47:36 +00:00
dependabot[bot]
5e16b97e02 feat(deps): bump google.golang.org/api from 0.198.0 to 0.199.0 (#181)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.198.0 to 0.199.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.198.0...v0.199.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 15:11:28 +01:00
dependabot[bot]
0ff28b2deb feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#180)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.35 to 1.27.38.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.35...config/v1.27.38)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-27 15:05:53 +01:00
Jonny Stoten
4ca962b70c Add function for parsing DOI definition files (#172)
Add a Rego builtin called `attest.internals.parse_library_definition`
for parsing the DOI definition files in
https://github.com/docker-library/official-images/tree/master/library.
This will allow us to verify DOI provenance fields against these files
which are the source of truth for DOI images.

This function just defers to
https://github.com/docker-library/bashbrew/blob/master/manifest/rfc2822.go.
2024-09-27 12:32:24 +01:00
Joel Kamp
2a4bef091e Merge pull request #179 from docker/fix-sign-unsigned-statements
fix: only sign statements
2024-09-26 10:02:41 -05:00
mrjoelkamp
bb0843cd51 fix: only sign statements 2024-09-24 15:12:46 -05:00
David Dooling
203577e965 Remove long-term aspiration from README (#174) 2024-09-20 09:06:02 -05:00
James Carnegie
a98604bdd5 chore: add rekor prod TUF system test (#176) 2024-09-20 11:02:36 +01:00
dependabot[bot]
02b8063d71 feat(deps): bump google.golang.org/api from 0.197.0 to 0.198.0 (#175)
Bumps [google.golang.org/api](https://github.com/googleapis/google-api-go-client) from 0.197.0 to 0.198.0.
- [Release notes](https://github.com/googleapis/google-api-go-client/releases)
- [Changelog](https://github.com/googleapis/google-api-go-client/blob/main/CHANGES.md)
- [Commits](https://github.com/googleapis/google-api-go-client/compare/v0.197.0...v0.198.0)

---
updated-dependencies:
- dependency-name: google.golang.org/api
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-20 10:01:47 +01:00
Joel Kamp
dcf5c578dd Merge pull request #173 from docker/feat-support-containerd-subject-annotations
feat: support containerd subject annotations
2024-09-19 16:03:32 -05:00
mrjoelkamp
0378c94226 test: make test layouts smaller 2024-09-19 15:36:20 -05:00
mrjoelkamp
fd4e741a1f feat: support containerd subject annotations 2024-09-19 15:10:56 -05:00
James Carnegie
2ace988b1c chore: add test for RegoFnOpts (#171) 2024-09-19 13:54:10 +01:00
dependabot[bot]
be7a17f214 feat(deps): bump github.com/sigstore/sigstore from 1.8.8 to 1.8.9 (#169)
Bumps [github.com/sigstore/sigstore](https://github.com/sigstore/sigstore) from 1.8.8 to 1.8.9.
- [Release notes](https://github.com/sigstore/sigstore/releases)
- [Commits](https://github.com/sigstore/sigstore/compare/v1.8.8...v1.8.9)

---
updated-dependencies:
- dependency-name: github.com/sigstore/sigstore
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 11:24:30 +01:00
dependabot[bot]
1a49b5c068 chore(deps): bump actions/create-github-app-token from 1.10.4 to 1.11.0 (#164)
Bumps [actions/create-github-app-token](https://github.com/actions/create-github-app-token) from 1.10.4 to 1.11.0.
- [Release notes](https://github.com/actions/create-github-app-token/releases)
- [Commits](3378cda945...5d869da34e)

---
updated-dependencies:
- dependency-name: actions/create-github-app-token
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-19 11:18:57 +01:00
James Carnegie
3e82338649 refactor: remove explicit closures. expose rego fns (#170) 2024-09-19 11:04:00 +01:00
James Carnegie
4a70e5ae36 Add platform filtering support to mapping.yml (#167)
* chore!: rename package config -> mapping
* feat: add platform filtering support to mapping.yml
2024-09-18 21:11:55 +01:00
James Carnegie
05caa959c4 Use a Factory to create signature verifiers at policy evaluation time (#165)
* Make verifiers composable

* fix: remove unused code and improve signature verification logic

* fix: simplify abstractions and renamed some things

* fix: improve tl interface.

* fix: sort out signer/verifier
2024-09-18 13:34:10 +01:00
dependabot[bot]
5335a56da1 feat(deps): bump github.com/aws/aws-sdk-go-v2/config (#168)
Bumps [github.com/aws/aws-sdk-go-v2/config](https://github.com/aws/aws-sdk-go-v2) from 1.27.33 to 1.27.35.
- [Release notes](https://github.com/aws/aws-sdk-go-v2/releases)
- [Commits](https://github.com/aws/aws-sdk-go-v2/compare/config/v1.27.33...config/v1.27.35)

---
updated-dependencies:
- dependency-name: github.com/aws/aws-sdk-go-v2/config
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-09-18 09:43:50 +01:00
156 changed files with 2331 additions and 1195 deletions

View File

@@ -10,7 +10,7 @@ jobs:
steps:
- name: Generate GitHub App Token
id: app-token
uses: actions/create-github-app-token@3378cda945da322a8db4b193e19d46352ebe2de5 # v1.10.4
uses: actions/create-github-app-token@5d869da34e18e7287c1daad50e0b8ea0f506ce69 # v1.11.0
with:
app-id: ${{ vars.ATTEST_RELEASE_APP_ID }}
private-key: ${{ secrets.ATTEST_RELEASE_APP_PRIVATE_KEY }}

View File

@@ -40,9 +40,6 @@ Examples of attestations include statements about the provenance and SBOM of an
This library can be used to verify these attestations using Rego policy.
Policy can be used to check whether an attestation is correctly signed, and that the contents of the attestation are correct.
Our overall goal with this project is adoption of the ideas into other open-source projects, rather than to create another standalone tool.
It would be a great outcome if this library was no longer needed because the functionality was built into other tools.
# Features
- Sign in-toto attestations
@@ -203,8 +200,14 @@ rules:
- pattern: "^docker[.]io/library/(.*)$"
policy-id: docker-official-images
- pattern: "^public[.]ecr[.]aws/docker/library/(.*)$"
platforms: ["linux/amd64"] # optional: restrict image platforms for matching policies (default: all)
rewrite: docker.io/library/$1
```
`platforms` in the second rule above is optional and can be used to restrict the platforms for which the policy
is evaluated. If the `platforms` field is not present, the policy will be applied to all platforms.
It's important to note that the `platforms` field is a filter, and is applied before the `pattern`
field is processed, so both `platforms` and `pattern` need to match in order for the policy to be selected
(or the rewrite to be processed if present).
As before, any repository in the `docker.io/library` namespace will be evaluated against the policy in `doi/policy.rego`.
The second rule will rewrite any repository in the `public.ecr.aws/docker/library` namespace to `docker.io/library`.

View File

@@ -96,8 +96,9 @@ func layersFromImage(image v1.Image) ([]*Layer, error) {
// copy original annotations
ann := maps.Clone(layerDesc.Annotations)
// only decode intoto statements
stmt := new(intoto.Statement)
var stmt *intoto.Statement
if mt == types.MediaType(intoto.PayloadType) {
stmt = new(intoto.Statement)
err = json.NewDecoder(r).Decode(&stmt)
if err != nil {
return nil, fmt.Errorf("failed to decode statement layer contents: %w", err)
@@ -315,8 +316,8 @@ func buildImageFromLayers(layers []*Layer, manifest *v1.Descriptor, subject *v1.
return newImg, nil
}
func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*Envelope, error) {
var envs []*Envelope
func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*EnvelopeReference, error) {
var envs []*EnvelopeReference
dsseMediaType, err := DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
@@ -332,11 +333,20 @@ func ExtractEnvelopes(manifest *Manifest, predicateType string) ([]*Envelope, er
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer reader.Close()
env := new(Envelope)
env := new(EnvelopeReference)
err = json.NewDecoder(reader).Decode(&env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
var uri string
if len(manifest.OriginalDescriptor.URLs) > 0 {
uri = manifest.OriginalDescriptor.URLs[0]
}
env.ResourceDescriptor = &ResourceDescriptor{
MediaType: string(mt),
Digest: map[string]string{manifest.OriginalDescriptor.Digest.Algorithm: manifest.OriginalDescriptor.Digest.Hex},
URI: uri,
}
envs = append(envs, env)
}
}

View File

@@ -12,7 +12,7 @@ import (
const ExpectedStatements = 4
func TestExtractAnnotatedStatements(t *testing.T) {
statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestImage(".."), intoto.PayloadType)
statements, err := attestation.ExtractAnnotatedStatements(test.UnsignedTestIndex(".."), intoto.PayloadType)
assert.NoError(t, err)
assert.Equalf(t, len(statements), ExpectedStatements, "expected %d statement, got %d", ExpectedStatements, len(statements))
}

View File

@@ -26,12 +26,12 @@ func ExampleManifest() {
// configure signing options
opts := &attestation.SigningOptions{
SkipTL: true, // skip trust logging to a transparency log
TransparencyLog: nil, // set this to log to a transparency log
}
ref := "docker/image-signer-verifier:latest"
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
digest, err := v1.NewHash("sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390")
if err != nil {
panic(err)
}

View File

@@ -5,12 +5,18 @@ import (
"encoding/json"
"fmt"
containerd "github.com/containerd/containerd/v2/core/images"
"github.com/distribution/reference"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/layout"
ocispec "github.com/opencontainers/image-spec/specs-go/v1"
)
// implementation of Resolver that closes over attestations from an oci layout.
var _ Resolver = (*LayoutResolver)(nil)
type LayoutResolver struct {
*Manifest
*oci.ImageSpec
@@ -39,8 +45,8 @@ func (r *LayoutResolver) fetchManifest() (*Manifest, error) {
return r.Manifest, nil
}
func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*Envelope, error) {
var envs []*Envelope
func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) ([]*EnvelopeReference, error) {
var envs []*EnvelopeReference
dsseMediaType, err := DSSEMediaType(predicateType)
if err != nil {
return nil, fmt.Errorf("failed to get DSSE media type for predicate '%s': %w", predicateType, err)
@@ -54,17 +60,26 @@ func (r *LayoutResolver) Attestations(_ context.Context, predicateType string) (
if mts != dsseMediaType {
continue
}
env := new(Envelope)
env := new(EnvelopeReference)
// parse layer blob as json
r, err := attestationLayer.Layer.Uncompressed()
layer, err := attestationLayer.Layer.Uncompressed()
if err != nil {
return nil, fmt.Errorf("failed to get layer contents: %w", err)
}
defer r.Close()
err = json.NewDecoder(r).Decode(env)
defer layer.Close()
err = json.NewDecoder(layer).Decode(env)
if err != nil {
return nil, fmt.Errorf("failed to decode envelope: %w", err)
}
var uri string
if len(r.Manifest.OriginalDescriptor.URLs) > 0 {
uri = r.Manifest.OriginalDescriptor.URLs[0]
}
env.ResourceDescriptor = &ResourceDescriptor{
MediaType: string(mt),
Digest: map[string]string{r.Manifest.OriginalDescriptor.Digest.Algorithm: r.Manifest.OriginalDescriptor.Digest.Hex},
URI: uri,
}
envs = append(envs, env)
}
return envs, nil
@@ -83,30 +98,49 @@ func (r *LayoutResolver) ImagePlatform(_ context.Context) (*v1.Platform, error)
}
func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error) {
idx, err := layout.ImageIndexFromPath(path)
layoutIndex, err := layout.ImageIndexFromPath(path)
if err != nil {
return nil, err
}
idxm, err := idx.IndexManifest()
layoutIndexManifest, err := layoutIndex.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to get digest: %w", err)
}
idxDescriptor := idxm.Manifests[0]
idxDigest := idxDescriptor.Digest
mfs, err := idx.ImageIndex(idxDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", idxDigest.String(), err)
layoutDescriptor := layoutIndexManifest.Manifests[0]
layoutDescriptorDigest := layoutDescriptor.Digest
subjectName := layoutDescriptor.Annotations[ocispec.AnnotationRefName]
if _, err := reference.ParseNamed(subjectName); err != nil {
// try the containerd annotation if the org.opencontainers.image.ref.name is not a full name
subjectName = layoutDescriptor.Annotations[containerd.AnnotationImageName]
if _, err := reference.ParseNamed(subjectName); err != nil {
return nil, fmt.Errorf("failed to find subject name in annotations")
}
}
mfs2, err := mfs.IndexManifest()
// check if digest refers to an image or an index
_, err = layoutIndex.Image(layoutDescriptorDigest)
if err == nil {
return &Manifest{
OriginalLayers: nil,
OriginalDescriptor: nil,
SubjectName: subjectName,
SubjectDescriptor: &layoutDescriptor,
}, nil
}
subjectIndex, err := layoutIndex.ImageIndex(layoutDescriptorDigest)
if err != nil {
return nil, fmt.Errorf("failed to extract ImageIndex for digest %s: %w", layoutDescriptorDigest.String(), err)
}
subjectIndexManifest, err := subjectIndex.IndexManifest()
if err != nil {
return nil, fmt.Errorf("failed to extract IndexManifest from ImageIndex: %w", err)
}
var subjectDescriptor *v1.Descriptor
for i := range mfs2.Manifests {
manifest := &mfs2.Manifests[i]
for i := range subjectIndexManifest.Manifests {
manifest := &subjectIndexManifest.Manifests[i]
if manifest.Platform != nil {
if manifest.Platform.Equals(*platform) {
subjectDescriptor = manifest
@@ -117,8 +151,8 @@ func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error
if subjectDescriptor == nil {
return nil, fmt.Errorf("platform not found in index")
}
for i := range mfs2.Manifests {
mf := &mfs2.Manifests[i]
for i := range subjectIndexManifest.Manifests {
mf := &subjectIndexManifest.Manifests[i]
if mf.Annotations[DockerReferenceType] != AttestationManifestType {
continue
}
@@ -127,7 +161,7 @@ func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error
continue
}
attestationImage, err := mfs.Image(mf.Digest)
attestationImage, err := subjectIndex.Image(mf.Digest)
if err != nil {
return nil, fmt.Errorf("failed to extract attestation image with digest %s: %w", mf.Digest.String(), err)
}
@@ -138,7 +172,7 @@ func manifestFromOCILayout(path string, platform *v1.Platform) (*Manifest, error
attest := &Manifest{
OriginalLayers: layers,
OriginalDescriptor: mf,
SubjectName: idxDescriptor.Annotations["org.opencontainers.image.ref.name"],
SubjectName: subjectName,
SubjectDescriptor: subjectDescriptor,
}
return attest, nil

View File

@@ -1,6 +1,8 @@
package attestation_test
import (
"context"
"path/filepath"
"strings"
"testing"
@@ -24,7 +26,7 @@ func TestAttestationFromOCILayout(t *testing.T) {
}
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
@@ -33,7 +35,7 @@ func TestAttestationFromOCILayout(t *testing.T) {
require.NoError(t, err)
spec, err := oci.ParseImageSpec(oci.LocalPrefix + outputLayout)
require.NoError(t, err)
err = oci.SaveIndex(ctx, []*oci.ImageSpec{spec}, signedIndex, outputLayout)
err = oci.SaveIndex(ctx, []*oci.ImageSpec{spec}, signedIndex, "docker.io/library/test-image:test")
require.NoError(t, err)
testCases := []struct {
@@ -66,3 +68,40 @@ func TestAttestationFromOCILayout(t *testing.T) {
})
}
}
func TestSubjectNameAnnotations(t *testing.T) {
testCases := []struct {
name string
ociLayoutPath string
errorStr string
}{
{name: "oci annotation", ociLayoutPath: test.UnsignedTestIndex("..")},
{name: "containerd annotation", ociLayoutPath: filepath.Join("..", "test", "testdata", "containerd-subject-layout")},
{name: "missing subject name", ociLayoutPath: filepath.Join("..", "test", "testdata", "missing-subject-layout"), errorStr: "failed to find subject name in annotations"},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
spec, err := oci.ParseImageSpec(oci.LocalPrefix+tc.ociLayoutPath, oci.WithPlatform("linux/arm64"))
require.NoError(t, err)
_, err = policy.CreateImageDetailsResolver(spec)
if tc.errorStr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.errorStr)
return
}
require.NoError(t, err)
})
}
}
func TestImageDetailsFromImageLayout(t *testing.T) {
spec, err := oci.ParseImageSpec(oci.LocalPrefix+test.UnsignedTestImage(".."), oci.WithPlatform("linux/arm64"))
require.NoError(t, err)
resolver, err := policy.CreateImageDetailsResolver(spec)
require.NoError(t, err)
desc, err := resolver.ImageDescriptor(context.Background())
require.NoError(t, err)
digest := desc.Digest.String()
assert.Equal(t, "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390", digest)
}

View File

@@ -3,6 +3,7 @@ package attestation
import (
"context"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
@@ -11,14 +12,14 @@ import (
var _ oci.ImageDetailsResolver = MockResolver{}
type MockResolver struct {
Envs []*Envelope
Envs []*EnvelopeReference
Image string
PlatformFn func() (*v1.Platform, error)
DescriptorFn func() (*v1.Descriptor, error)
ImangeNameFn func() (string, error)
}
func (r MockResolver) Attestations(_ context.Context, _ string) ([]*Envelope, error) {
func (r MockResolver) Attestations(_ context.Context, _ string) ([]*EnvelopeReference, error) {
return r.Envs, nil
}
@@ -36,7 +37,7 @@ func (r MockResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error)
if r.DescriptorFn != nil {
return r.DescriptorFn()
}
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
if err != nil {
return nil, err
}

View File

@@ -109,12 +109,12 @@ func (r *ReferrersResolver) resolveAttestations(ctx context.Context, predicateTy
return aManifests, nil
}
func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*Envelope, error) {
func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) {
manifests, err := r.resolveAttestations(ctx, predicateType)
if err != nil {
return nil, fmt.Errorf("failed to resolve attestations: %w", err)
}
var envs []*Envelope
var envs []*EnvelopeReference
for _, attest := range manifests {
es, err := ExtractEnvelopes(attest, predicateType)
if err != nil {

View File

@@ -9,8 +9,8 @@ import (
"github.com/docker/attest"
"github.com/docker/attest/attestation"
"github.com/docker/attest/config"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/mapping"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
"github.com/google/go-containerregistry/pkg/name"
@@ -39,7 +39,7 @@ func TestAttestationReferenceTypes(t *testing.T) {
referrersServer *httptest.Server
useDigest bool
referrersRepo string
attestationSource config.AttestationStyle
attestationSource mapping.AttestationStyle
expectFailure bool
}{
{
@@ -55,26 +55,26 @@ func TestAttestationReferenceTypes(t *testing.T) {
name: "attached attestations, referrers repo (mismatched args)",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
expectFailure: true, // mismatched args
attestationSource: config.AttestationStyleAttached,
attestationSource: mapping.AttestationStyleAttached,
referrersRepo: "referrers",
},
{
name: "referrers attestations, referrers repo (no policy)",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
expectFailure: true, // no policy
attestationSource: config.AttestationStyleReferrers,
attestationSource: mapping.AttestationStyleReferrers,
referrersRepo: "referrers",
},
{
name: "referrers attestations",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
attestationSource: config.AttestationStyleReferrers,
attestationSource: mapping.AttestationStyleReferrers,
},
{
name: "referrers attestations, no referrers support on server",
server: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(false)),
attestationSource: config.AttestationStyleReferrers,
attestationSource: mapping.AttestationStyleReferrers,
referrersServer: test.NewLocalRegistry(ctx, registry.WithReferrersSupport(true)),
},
} {
@@ -88,10 +88,8 @@ func TestAttestationReferenceTypes(t *testing.T) {
u, err := url.Parse(s.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{
SkipTL: true,
}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/repo:root", u.Host)
@@ -210,10 +208,8 @@ func TestReferencesInDifferentRepo(t *testing.T) {
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{
SkipTL: true,
}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
@@ -236,10 +232,8 @@ func TestReferencesInDifferentRepo(t *testing.T) {
refServerURL, err := url.Parse(refServer.URL)
require.NoError(t, err)
opts := &attestation.SigningOptions{
SkipTL: true,
}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)
@@ -291,10 +285,8 @@ func TestCorrectArtifactTypeInTagFallback(t *testing.T) {
repoName := "repo"
opts := &attestation.SigningOptions{
SkipTL: true,
}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
indexName := fmt.Sprintf("%s/%s:latest", serverURL.Host, repoName)

View File

@@ -24,7 +24,7 @@ func NewRegistryResolver(src *oci.RegistryImageDetailsResolver) (*RegistryResolv
}, nil
}
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*Envelope, error) {
func (r *RegistryResolver) Attestations(ctx context.Context, predicateType string) ([]*EnvelopeReference, error) {
if r.Manifest == nil {
attest, err := FetchManifest(ctx, r.Identifier, r.ImageSpec.Platform)
if err != nil {

View File

@@ -24,7 +24,7 @@ func TestRegistry(t *testing.T) {
require.NoError(t, err)
opts := &attestation.SigningOptions{}
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
signedManifests, err := attest.SignStatements(ctx, attIdx.Index, signer, opts)
require.NoError(t, err)
@@ -46,4 +46,14 @@ func TestRegistry(t *testing.T) {
require.NoError(t, err)
digest := desc.Digest.String()
assert.True(t, strings.Contains(digest, "sha256:"))
// resolver also works with platform specific digest
spec, err = oci.ParseImageSpec(fmt.Sprintf("%s@%s", indexName, digest))
require.NoError(t, err)
resolver, err = policy.CreateImageDetailsResolver(spec)
require.NoError(t, err)
desc, err = resolver.ImageDescriptor(ctx)
require.NoError(t, err)
assert.Equal(t, desc.Digest.String(), digest)
}

View File

@@ -8,5 +8,5 @@ import (
type Resolver interface {
oci.ImageDetailsResolver
Attestations(ctx context.Context, mediaType string) ([]*Envelope, error)
Attestations(ctx context.Context, mediaType string) ([]*EnvelopeReference, error)
}

View File

@@ -37,10 +37,10 @@ func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, o
KeyID: keyID,
Sig: base64Encoding.EncodeToString(sig),
}
if !opts.SkipTL {
ext, err := logSignature(ctx, tlog.GetTL(ctx), &sig, &encPayload, signer)
if opts.TransparencyLog != nil {
ext, err := logSignature(ctx, opts.TransparencyLog, sig, encPayload, signer)
if err != nil {
return nil, fmt.Errorf("failed to log to rekor: %w", err)
return nil, fmt.Errorf("failed to log signature: %w", err)
}
dsseSig.Extension = ext
}
@@ -51,27 +51,21 @@ func SignDSSE(ctx context.Context, payload []byte, signer dsse.SignerVerifier, o
}
// returns a new envelope with the transparency log entry added to the signature extension.
func logSignature(ctx context.Context, t tlog.TL, sig *[]byte, encPayload *[]byte, signer dsse.SignerVerifier) (*Extension, error) {
func logSignature(ctx context.Context, t tlog.TransparencyLog, sig []byte, encPayload []byte, signer dsse.SignerVerifier) (*Extension, error) {
// get Key ID from signer
keyID, err := signer.KeyID()
if err != nil {
return nil, fmt.Errorf("error getting public key ID: %w", err)
}
entry, err := t.UploadLogEntry(ctx, keyID, *encPayload, *sig, signer)
entry, err := t.UploadEntry(ctx, keyID, encPayload, sig, signer)
if err != nil {
return nil, fmt.Errorf("error uploading TL entry: %w", err)
}
entryObj, err := t.UnmarshalEntry(entry)
if err != nil {
return nil, fmt.Errorf("error unmarshaling tl entry: %w", err)
}
return &Extension{
Kind: DockerDSSEExtKind,
Ext: &DockerDSSEExtension{
TL: &DockerTLExtension{
Kind: RekorTLExtKind,
Data: entryObj, // transparency log entry metadata
},
TL: entry,
},
}, nil
}

View File

@@ -1,6 +1,7 @@
package attestation_test
import (
"context"
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
@@ -14,6 +15,7 @@ import (
"github.com/docker/attest/internal/test"
"github.com/docker/attest/oci"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
"github.com/google/go-containerregistry/pkg/registry"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/static"
@@ -35,7 +37,10 @@ func TestSignVerifyAttestation(t *testing.T) {
payload, err := json.Marshal(stmt)
require.NoError(t, err)
opts := &attestation.SigningOptions{}
tl := tlog.GetMockTL()
opts := &attestation.SigningOptions{
TransparencyLog: tl,
}
env, err := attestation.SignDSSE(ctx, payload, signer, opts)
require.NoError(t, err)
@@ -146,8 +151,17 @@ func TestSignVerifyAttestation(t *testing.T) {
opts := &attestation.VerifyOptions{
Keys: attestation.Keys{keyMeta},
}
_, err = attestation.VerifyDSSE(ctx, deserializedEnv, opts)
getTL := func(_ context.Context, opts *attestation.VerifyOptions) (tlog.TransparencyLog, error) {
if opts.SkipTL {
return nil, nil
}
return tl, nil
}
verifier, err := attestation.NewVerfier(attestation.WithLogVerifierFactory(getTL))
require.NoError(t, err)
_, err = attestation.VerifyDSSE(ctx, verifier, deserializedEnv, opts)
if tc.expectedError != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.expectedError)
} else {
assert.NoError(t, err)
@@ -222,7 +236,6 @@ func TestSimpleStatementSigning(t *testing.T) {
{"replaced", true},
{"not replaced", false},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
opts := &attestation.SigningOptions{}
@@ -236,7 +249,7 @@ func TestSimpleStatementSigning(t *testing.T) {
PredicateType: attestation.VSAPredicateType,
},
}
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
require.NoError(t, err)
subject := &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",

View File

@@ -1,9 +1,12 @@
package attestation
import (
"crypto"
"encoding/base64"
"fmt"
"time"
"github.com/docker/attest/tlog"
v1 "github.com/google/go-containerregistry/pkg/v1"
intoto "github.com/in-toto/in-toto-golang/in_toto"
v02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
@@ -17,7 +20,6 @@ const (
InTotoPredicateType = "in-toto.io/predicate-type"
DockerReferenceDigest = "vnd.docker.reference.digest"
DockerDSSEExtKind = "application/vnd.docker.attestation-verification.v1+json"
RekorTLExtKind = "Rekor"
OCIDescriptorDSSEMediaType = ociv1.MediaTypeDescriptor + "+dsse"
InTotoReferenceLifecycleStage = "vnd.docker.lifecycle-stage"
LifecycleStageExperimental = "experimental"
@@ -65,6 +67,17 @@ type Extension struct {
Ext *DockerDSSEExtension `json:"ext"`
}
type EnvelopeReference struct {
*Envelope
ResourceDescriptor *ResourceDescriptor `json:"resourceDescriptor"`
}
type ResourceDescriptor struct {
MediaType string `json:"mediaType"`
Digest map[string]string `json:"digest"`
URI string `json:"uri,omitempty"`
}
type AnnotatedStatement struct {
OCIDescriptor *v1.Descriptor
InTotoStatement *intoto.Statement
@@ -72,22 +85,40 @@ type AnnotatedStatement struct {
}
type DockerDSSEExtension struct {
TL *DockerTLExtension `json:"tl"`
TL *tlog.DockerTLExtension `json:"tl"`
}
type DockerTLExtension struct {
Kind string `json:"kind"`
Data any `json:"data"`
}
type TransparencyLogKind string
const (
RekorTransparencyLogKind = "rekor"
)
type VerifyOptions struct {
Keys []*KeyMetadata `json:"keys"`
SkipTL bool `json:"skip_tl"`
Keys []*KeyMetadata `json:"keys"`
SkipTL bool `json:"skip_tl"`
TransparencyLog TransparencyLogKind `json:"tl"`
}
type KeyMetadata struct {
ID string `json:"id"`
PEM string `json:"key"`
From time.Time `json:"from"`
To *time.Time `json:"to"`
Status string `json:"status"`
SigningFormat string `json:"signing-format"`
Distrust bool `json:"distrust,omitempty"`
publicKey crypto.PublicKey
}
type (
Keys []*KeyMetadata
KeysMap map[string]*KeyMetadata
)
type SigningOptions struct {
// don't log to the configured transparency log
SkipTL bool
// set this in order to log to a transparency log
TransparencyLog tlog.TransparencyLog
}
type Options struct {

143
attestation/verifier.go Normal file
View File

@@ -0,0 +1,143 @@
package attestation
import (
"context"
"crypto"
"crypto/x509"
"fmt"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
"github.com/docker/attest/tuf"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
func WithTUFDownloader(tufDownloader tuf.Downloader) func(*verifier) {
return func(r *verifier) {
r.tufDownloader = tufDownloader
}
}
type SignatureVerifierFactory func(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error)
func WithSignatureVerifierFactory(factory SignatureVerifierFactory) func(*verifier) {
return func(r *verifier) {
r.signatureVerifierFactory = factory
}
}
func WithLogVerifierFactory(factory LogVerifierFactory) func(*verifier) {
return func(r *verifier) {
r.logVerifierFactory = factory
}
}
type LogVerifierFactory func(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error)
func NewVerfier(options ...func(*verifier)) (Verifier, error) {
verifier := &verifier{}
for _, opt := range options {
opt(verifier)
}
return verifier, nil
}
type Verifier interface {
GetSignatureVerifier(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error)
GetLogVerifier(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error)
VerifySignature(ctx context.Context, publicKey crypto.PublicKey, data []byte, signature []byte, opts *VerifyOptions) error
VerifyLog(ctx context.Context, keyMeta *KeyMetadata, data []byte, sig *Signature, opts *VerifyOptions) error
}
// ensure it has all the necessary methods.
var _ Verifier = (*verifier)(nil)
type verifier struct {
tufDownloader tuf.Downloader
signatureVerifierFactory SignatureVerifierFactory
logVerifierFactory LogVerifierFactory
}
// GetLogVerifier implements Verifier.
func (v *verifier) GetLogVerifier(ctx context.Context, opts *VerifyOptions) (tlog.TransparencyLog, error) {
if v.logVerifierFactory != nil {
return v.logVerifierFactory(ctx, opts)
}
if opts.SkipTL {
return nil, nil
}
// TODO support other transparency logs
var transparencyLog tlog.TransparencyLog
switch opts.TransparencyLog {
case "", RekorTransparencyLogKind:
var err error
transparencyLog, err = tlog.NewRekorLog(tlog.WithTUFDownloader(v.tufDownloader))
if err != nil {
return nil, fmt.Errorf("error failed to create rekor verifier: %w", err)
}
default:
return nil, fmt.Errorf("unsupported transparency log: %s", opts.TransparencyLog)
}
return transparencyLog, nil
}
// GetSignatureVerifier implements Verifier.
func (v *verifier) GetSignatureVerifier(ctx context.Context, publicKey crypto.PublicKey, opts *VerifyOptions) (dsse.Verifier, error) {
if v.signatureVerifierFactory != nil {
return v.signatureVerifierFactory(ctx, publicKey, opts)
}
// TODO: use details from opts to decide which algorithm to use here
ecdsaVerifier, err := signerverifier.NewECDSAVerifier(publicKey)
if err != nil {
return nil, fmt.Errorf("error failed to create ecdsa verifier: %w", err)
}
return ecdsaVerifier, nil
}
func (v *verifier) VerifySignature(ctx context.Context, publicKey crypto.PublicKey, data []byte, signature []byte, opts *VerifyOptions) error {
sigVerifier, err := v.GetSignatureVerifier(ctx, publicKey, opts)
if err != nil {
return fmt.Errorf("error failed to get verifier: %w", err)
}
return sigVerifier.Verify(ctx, data, signature)
}
func (v *verifier) VerifyLog(ctx context.Context, keyMeta *KeyMetadata, encPayload []byte, sig *Signature, opts *VerifyOptions) error {
if opts.SkipTL {
return nil
}
if sig.Extension == nil || sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension")
}
if sig.Extension.Kind != DockerDSSEExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
transparencyLog, err := v.GetLogVerifier(ctx, opts)
if err != nil {
return fmt.Errorf("error failed to get transparency log verifier: %w", err)
}
if transparencyLog == nil {
return fmt.Errorf("error missing transparency log verifier")
}
// verify TL entry payload
publicKey, err := keyMeta.ParsedKey()
if err != nil {
return fmt.Errorf("error failed to parse public key: %w", err)
}
encodedPub, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return fmt.Errorf("error failed to marshal public key: %w", err)
}
integratedTime, err := transparencyLog.VerifyEntry(ctx, sig.Extension.Ext.TL, encPayload, encodedPub)
if err != nil {
return fmt.Errorf("TL entry failed verification: %w", err)
}
if integratedTime.Before(keyMeta.From) {
return fmt.Errorf("key %s was not yet valid at TL log time %s (key valid from %s)", keyMeta.ID, integratedTime, keyMeta.From)
}
if keyMeta.To != nil && !integratedTime.Before(*keyMeta.To) {
return fmt.Errorf("key %s was already %s at TL log time %s (key %s at %s)", keyMeta.ID, keyMeta.Status, integratedTime, keyMeta.Status, *keyMeta.To)
}
return nil
}

View File

@@ -0,0 +1,55 @@
package attestation
import (
"context"
"reflect"
"testing"
"github.com/docker/attest/tlog"
"github.com/docker/attest/tuf"
"github.com/stretchr/testify/require"
)
func Test_verifier_GetLogVerifier(t *testing.T) {
type fields struct {
tufDownloader tuf.Downloader
signatureVerifierFactory SignatureVerifierFactory
logVerifierFactory LogVerifierFactory
}
type args struct {
ctx context.Context
opts *VerifyOptions
}
rekor, err := tlog.NewRekorLog()
require.NoError(t, err)
tests := []struct {
name string
fields fields
args args
want tlog.TransparencyLog
wantErr bool
}{
{name: "skip_tl true", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: true}}},
{name: "skip_tl false", fields: fields{}, args: args{ctx: context.Background(), opts: &VerifyOptions{SkipTL: false}}, want: rekor},
{name: "tl: rekor", fields: fields{logVerifierFactory: func(_ context.Context, _ *VerifyOptions) (tlog.TransparencyLog, error) {
return &tlog.Rekor{}, nil
}}, args: args{ctx: context.Background(), opts: &VerifyOptions{}}, want: &tlog.Rekor{}},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
v := &verifier{
tufDownloader: tt.fields.tufDownloader,
signatureVerifierFactory: tt.fields.signatureVerifierFactory,
logVerifierFactory: tt.fields.logVerifierFactory,
}
got, err := v.GetLogVerifier(tt.args.ctx, tt.args.opts)
if (err != nil) != tt.wantErr {
t.Errorf("verifier.GetLogVerifier() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("verifier.GetLogVerifier() = %v, want %v", got, tt.want)
}
})
}
}

View File

@@ -2,37 +2,17 @@ package attestation
import (
"context"
"crypto/ecdsa"
"crypto/x509"
"crypto"
"encoding/base64"
"encoding/json"
"fmt"
"time"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
intoto "github.com/in-toto/in-toto-golang/in_toto"
ociv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type KeyMetadata struct {
ID string `json:"id"`
PEM string `json:"key"`
From time.Time `json:"from"`
To *time.Time `json:"to"`
Status string `json:"status"`
SigningFormat string `json:"signing-format"`
Distrust bool `json:"distrust,omitempty"`
}
type (
Keys []*KeyMetadata
KeysMap map[string]*KeyMetadata
)
func VerifyDSSE(ctx context.Context, env *Envelope, opts *VerifyOptions) ([]byte, error) {
func VerifyDSSE(ctx context.Context, verifier Verifier, env *Envelope, opts *VerifyOptions) ([]byte, error) {
// enforce payload type
if !ValidPayloadType(env.PayloadType) {
return nil, fmt.Errorf("unsupported payload type %s", env.PayloadType)
@@ -42,97 +22,62 @@ func VerifyDSSE(ctx context.Context, env *Envelope, opts *VerifyOptions) ([]byte
return nil, fmt.Errorf("no signatures found")
}
keys := make(map[string]*KeyMetadata, len(opts.Keys))
for _, key := range opts.Keys {
keys[key.ID] = key
}
payload, err := base64Encoding.DecodeString(env.Payload)
if err != nil {
return nil, fmt.Errorf("error failed to decode payload: %w", err)
}
encPayload := dsse.PAE(env.PayloadType, payload)
// verify signatures and transparency log entry
for _, sig := range env.Signatures {
err := verifySignature(ctx, sig, encPayload, opts)
// resolve public key used to sign
keyMeta, ok := keys[sig.KeyID]
if !ok {
return nil, fmt.Errorf("error key not found: %s", sig.KeyID)
}
if keyMeta.Distrust {
return nil, fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
publicKey, err := keyMeta.ParsedKey()
if err != nil {
return nil, err
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
// decode signature
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
if err != nil {
return nil, fmt.Errorf("error failed to decode signature: %w", err)
}
err = verifier.VerifySignature(ctx, publicKey, encPayload, signature, opts)
if err != nil {
return nil, fmt.Errorf("error failed to verify signature: %w", err)
}
if err := verifier.VerifyLog(ctx, keyMeta, encPayload, sig, opts); err != nil {
return nil, fmt.Errorf("error failed to verify transparency log entry: %w", err)
}
}
return payload, nil
}
func verifySignature(ctx context.Context, sig *Signature, payload []byte, opts *VerifyOptions) error {
keys := make(map[string]*KeyMetadata, len(opts.Keys))
for _, key := range opts.Keys {
keys[key.ID] = key
}
keyMeta, ok := keys[sig.KeyID]
if !ok {
return fmt.Errorf("error key not found: %s", sig.KeyID)
}
if keyMeta.Distrust {
return fmt.Errorf("key %s is distrusted", keyMeta.ID)
}
// TODO: this is unmarshalling with MarshalPKIXPublicKey only for us to marshal it again
publicKey, err := signerverifier.ParsePublicKey([]byte(keyMeta.PEM))
if err != nil {
return fmt.Errorf("failed to parse public key: %w", err)
}
if !opts.SkipTL {
t := tlog.GetTL(ctx)
if sig.Extension == nil || sig.Extension.Kind == "" {
return fmt.Errorf("error missing signature extension")
}
if sig.Extension.Kind != DockerDSSEExtKind {
return fmt.Errorf("error unsupported signature extension kind: %s", sig.Extension.Kind)
}
// verify TL entry
if sig.Extension.Ext.TL.Kind != RekorTLExtKind {
return fmt.Errorf("error unsupported TL extension kind: %s", sig.Extension.Ext.TL.Kind)
}
entry := sig.Extension.Ext.TL.Data
entryBytes, err := json.Marshal(entry)
if err != nil {
return fmt.Errorf("failed to marshal TL entry: %w", err)
}
integratedTime, err := t.VerifyLogEntry(ctx, entryBytes)
if err != nil {
return fmt.Errorf("TL entry failed verification: %w", err)
}
if integratedTime.Before(keyMeta.From) {
return fmt.Errorf("key %s was not yet valid at TL log time %s (key valid from %s)", keyMeta.ID, integratedTime, keyMeta.From)
}
if keyMeta.To != nil && !integratedTime.Before(*keyMeta.To) {
return fmt.Errorf("key %s was already %s at TL log time %s (key %s at %s)", keyMeta.ID, keyMeta.Status, integratedTime, keyMeta.Status, *keyMeta.To)
}
// verify TL entry payload
encodedPub, err := x509.MarshalPKIXPublicKey(publicKey)
if err != nil {
return fmt.Errorf("error failed to marshal public key: %w", err)
}
err = t.VerifyEntryPayload(entryBytes, payload, encodedPub)
if err != nil {
return fmt.Errorf("TL entry failed payload verification: %w", err)
}
}
// decode signature
signature, err := base64.StdEncoding.Strict().DecodeString(sig.Sig)
if err != nil {
return fmt.Errorf("error failed to decode signature: %w", err)
}
// verify payload ecdsa signature
ok = ecdsa.VerifyASN1(publicKey, util.SHA256(payload), signature)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func ValidPayloadType(payloadType string) bool {
return payloadType == intoto.PayloadType || payloadType == ociv1.MediaTypeDescriptor
}
func (km *KeyMetadata) ParsedKey() (crypto.PublicKey, error) {
if km.publicKey != nil {
return km.publicKey, nil
}
publicKey, err := signerverifier.ParsePublicKey([]byte(km.PEM))
if err != nil {
return nil, fmt.Errorf("failed to parse public key: %w", err)
}
km.publicKey = publicKey
return publicKey, nil
}

View File

@@ -42,8 +42,7 @@ func TestVerifyUnsignedAttestation(t *testing.T) {
opts := &attestation.VerifyOptions{
Keys: attestation.Keys{},
}
_, err := attestation.VerifyDSSE(ctx, env, opts)
_, err := attestation.VerifyDSSE(ctx, nil, env, opts)
assert.Error(t, err)
assert.Contains(t, err.Error(), "no signatures")
}

View File

@@ -12,13 +12,13 @@ const (
)
type VSAPredicate struct {
Verifier VSAVerifier `json:"verifier"`
TimeVerified string `json:"timeVerified"`
ResourceURI string `json:"resourceUri"`
Policy VSAPolicy `json:"policy"`
InputAttestations []VSAInputAttestation `json:"inputAttestations,omitempty"`
VerificationResult string `json:"verificationResult"`
VerifiedLevels []string `json:"verifiedLevels"`
Verifier VSAVerifier `json:"verifier"`
TimeVerified string `json:"timeVerified"`
ResourceURI string `json:"resourceUri"`
Policy VSAPolicy `json:"policy"`
InputAttestations []ResourceDescriptor `json:"inputAttestations,omitempty"`
VerificationResult string `json:"verificationResult"`
VerifiedLevels []string `json:"verifiedLevels"`
}
type VSAVerifier struct {
@@ -31,11 +31,6 @@ type VSAPolicy struct {
DownloadLocation string `json:"downloadLocation,omitempty"`
}
type VSAInputAttestation struct {
Digest map[string]string `json:"digest"`
MediaType string `json:"mediaType"`
}
func ToVSAResourceURI(sub intoto.Subject) (string, error) {
// parse purl
purl, err := packageurl.FromString(sub.Name)

View File

@@ -7,6 +7,7 @@ import (
"github.com/docker/attest/attestation"
"github.com/docker/attest/oci"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/google/go-containerregistry/pkg/v1/empty"
"github.com/google/go-containerregistry/pkg/v1/mutate"
@@ -25,8 +26,14 @@ func ExampleSignStatements_remote() {
// signer, err := signerverifier.GetAWSSigner(cmd.Context(), aws_arn, aws_region)
// configure signing options
// use rekor transparency log wit static rekor public key (see options to use dynamic rekor public key)
rekor, err := tlog.NewRekorLog()
if err != nil {
panic(err)
}
opts := &attestation.SigningOptions{
SkipTL: true, // skip trust logging to a transparency log
TransparencyLog: rekor, // unset this to disable signature transparency logging
}
// load image index with unsigned attestation-manifests

107
go.mod
View File

@@ -1,28 +1,33 @@
module github.com/docker/attest
go 1.22.5
go 1.22.7
toolchain go1.22.8
require (
github.com/Masterminds/semver/v3 v3.3.0
github.com/aws/aws-sdk-go-v2/config v1.27.33
github.com/aws/aws-sdk-go-v2/config v1.27.43
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8
github.com/containerd/containerd/v2 v2.0.0-rc.5
github.com/containerd/platforms v0.2.1
github.com/distribution/reference v0.6.0
github.com/docker-library/bashbrew v0.1.12
github.com/go-openapi/runtime v0.28.0
github.com/go-openapi/strfmt v0.23.0
github.com/google/go-containerregistry v0.20.2
github.com/in-toto/in-toto-golang v0.9.0
github.com/open-policy-agent/opa v0.68.0
github.com/open-policy-agent/opa v0.69.0
github.com/opencontainers/image-spec v1.1.0
github.com/package-url/packageurl-go v0.1.3
github.com/secure-systems-lab/go-securesystemslib v0.8.0
github.com/sigstore/cosign/v2 v2.4.0
github.com/sigstore/cosign/v2 v2.4.1
github.com/sigstore/rekor v1.3.6
github.com/sigstore/sigstore v1.8.9
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.9
github.com/sigstore/sigstore/pkg/signature/kms/gcp v1.8.9
github.com/stretchr/testify v1.9.0
github.com/theupdateframework/go-tuf/v2 v2.0.0
google.golang.org/api v0.197.0
github.com/theupdateframework/go-tuf/v2 v2.0.2
google.golang.org/api v0.200.0
sigs.k8s.io/yaml v1.4.0
)
@@ -31,35 +36,38 @@ replace github.com/google/go-containerregistry => github.com/docker/go-container
require (
cloud.google.com/go v0.115.1 // indirect
cloud.google.com/go/auth v0.9.3 // indirect
cloud.google.com/go/auth v0.9.8 // indirect
cloud.google.com/go/auth/oauth2adapt v0.2.4 // indirect
cloud.google.com/go/compute/metadata v0.5.0 // indirect
cloud.google.com/go/iam v1.2.0 // indirect
cloud.google.com/go/kms v1.19.0 // indirect
cloud.google.com/go/longrunning v0.6.0 // indirect
cloud.google.com/go/compute/metadata v0.5.2 // indirect
cloud.google.com/go/iam v1.2.1 // indirect
cloud.google.com/go/kms v1.20.0 // indirect
cloud.google.com/go/longrunning v0.6.1 // indirect
github.com/Microsoft/hcsshim v0.12.6 // indirect
github.com/OneOfOne/xxhash v1.2.8 // indirect
github.com/ProtonMail/go-crypto v1.0.0 // indirect
github.com/agnivade/levenshtein v1.1.1 // indirect
github.com/agnivade/levenshtein v1.2.0 // indirect
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect
github.com/aws/aws-sdk-go-v2 v1.30.5 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 // indirect
github.com/aws/aws-sdk-go-v2 v1.32.2 // indirect
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 // indirect
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 // indirect
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 // indirect
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecr v1.29.1 // indirect
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.24.1 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 // indirect
github.com/aws/smithy-go v1.20.4 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 // indirect
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 // indirect
github.com/aws/aws-sdk-go-v2/service/kms v1.35.7 // indirect
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 // indirect
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 // indirect
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 // indirect
github.com/aws/smithy-go v1.22.0 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/blang/semver v3.5.1+incompatible // indirect
github.com/cespare/xxhash/v2 v2.3.0 // indirect
github.com/cloudflare/circl v1.3.8 // indirect
github.com/containerd/containerd v1.7.22 // indirect
github.com/containerd/errdefs v0.2.0 // indirect
github.com/containerd/log v0.1.0 // indirect
github.com/containerd/stargz-snapshotter/estargz v0.15.1 // indirect
github.com/cyberphone/json-canonicalization v0.0.0-20231217050601-ba74d44ecf5f // indirect
@@ -74,7 +82,7 @@ require (
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-chi/chi v4.1.2+incompatible // indirect
github.com/go-ini/ini v1.67.0 // indirect
github.com/go-jose/go-jose/v4 v4.0.2 // indirect
github.com/go-jose/go-jose/v4 v4.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.23.0 // indirect
@@ -102,7 +110,7 @@ require (
github.com/jellydator/ttlcache/v3 v3.3.0 // indirect
github.com/jmespath/go-jmespath v0.4.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/klauspost/compress v1.17.10 // indirect
github.com/letsencrypt/boulder v0.0.0-20240620165639-de9c06129bec // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
@@ -113,10 +121,10 @@ require (
github.com/oklog/ulid v1.3.1 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.2 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_golang v1.20.2 // indirect
github.com/prometheus/client_golang v1.20.4 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.55.0 // indirect
github.com/prometheus/procfs v0.15.1 // indirect
@@ -126,7 +134,6 @@ require (
github.com/sassoftware/relic v7.2.1+incompatible // indirect
github.com/shibumi/go-pathspec v1.3.0 // indirect
github.com/sigstore/protobuf-specs v0.3.2 // indirect
github.com/sigstore/sigstore v1.8.8 // indirect
github.com/sigstore/timestamp-authority v1.2.2 // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
@@ -147,32 +154,34 @@ require (
github.com/yashtewari/glob-intersection v0.2.0 // indirect
go.mongodb.org/mongo-driver v1.15.0 // indirect
go.opencensus.io v0.24.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 // indirect
go.opentelemetry.io/otel v1.29.0 // indirect
go.opentelemetry.io/otel/metric v1.29.0 // indirect
go.opentelemetry.io/otel/sdk v1.28.0 // indirect
go.opentelemetry.io/otel/trace v1.29.0 // indirect
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect
go.opentelemetry.io/otel v1.30.0 // indirect
go.opentelemetry.io/otel/metric v1.30.0 // indirect
go.opentelemetry.io/otel/sdk v1.30.0 // indirect
go.opentelemetry.io/otel/trace v1.30.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.27.0 // indirect
golang.org/x/crypto v0.27.0 // indirect
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 // indirect
golang.org/x/mod v0.19.0 // indirect
golang.org/x/net v0.29.0 // indirect
golang.org/x/crypto v0.28.0 // indirect
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sync v0.8.0 // indirect
golang.org/x/sys v0.25.0 // indirect
golang.org/x/term v0.24.0 // indirect
golang.org/x/text v0.18.0 // indirect
golang.org/x/time v0.6.0 // indirect
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 // indirect
google.golang.org/grpc v1.66.1 // indirect
google.golang.org/protobuf v1.34.2 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/time v0.7.0 // indirect
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 // indirect
google.golang.org/grpc v1.67.1 // indirect
google.golang.org/protobuf v1.35.1 // indirect
gopkg.in/ini.v1 v1.67.0 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
gotest.tools/v3 v3.5.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/klog/v2 v2.130.1 // indirect
pault.ag/go/debian v0.12.0 // indirect
pault.ag/go/topsort v0.1.1 // indirect
)

303
go.sum
View File

@@ -1,18 +1,18 @@
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
cloud.google.com/go v0.115.1 h1:Jo0SM9cQnSkYfp44+v+NQXHpcHqlnRJk2qxh6yvxxxQ=
cloud.google.com/go v0.115.1/go.mod h1:DuujITeaufu3gL68/lOFIirVNJwQeyf5UXyi+Wbgknc=
cloud.google.com/go/auth v0.9.3 h1:VOEUIAADkkLtyfr3BLa3R8Ed/j6w1jTBmARx+wb5w5U=
cloud.google.com/go/auth v0.9.3/go.mod h1:7z6VY+7h3KUdRov5F1i8NDP5ZzWKYmEPO842BgCsmTk=
cloud.google.com/go/auth v0.9.8 h1:+CSJ0Gw9iVeSENVCKJoLHhdUykDgXSc4Qn+gu2BRtR8=
cloud.google.com/go/auth v0.9.8/go.mod h1:xxA5AqpDrvS+Gkmo9RqrGGRh6WSNKKOXhY3zNOr38tI=
cloud.google.com/go/auth/oauth2adapt v0.2.4 h1:0GWE/FUsXhf6C+jAkWgYm7X9tK8cuEIfy19DBn6B6bY=
cloud.google.com/go/auth/oauth2adapt v0.2.4/go.mod h1:jC/jOpwFP6JBxhB3P5Rr0a9HLMC/Pe3eaL4NmdvqPtc=
cloud.google.com/go/compute/metadata v0.5.0 h1:Zr0eK8JbFv6+Wi4ilXAR8FJ3wyNdpxHKJNPos6LTZOY=
cloud.google.com/go/compute/metadata v0.5.0/go.mod h1:aHnloV2TPI38yx4s9+wAZhHykWvVCfu7hQbF+9CWoiY=
cloud.google.com/go/iam v1.2.0 h1:kZKMKVNk/IsSSc/udOb83K0hL/Yh/Gcqpz+oAkoIFN8=
cloud.google.com/go/iam v1.2.0/go.mod h1:zITGuWgsLZxd8OwAlX+eMFgZDXzBm7icj1PVTYG766Q=
cloud.google.com/go/kms v1.19.0 h1:x0OVJDl6UH1BSX4THKlMfdcFWoE4ruh90ZHuilZekrU=
cloud.google.com/go/kms v1.19.0/go.mod h1:e4imokuPJUc17Trz2s6lEXFDt8bgDmvpVynH39bdrHM=
cloud.google.com/go/longrunning v0.6.0 h1:mM1ZmaNsQsnb+5n1DNPeL0KwQd9jQRqSqSDEkBZr+aI=
cloud.google.com/go/longrunning v0.6.0/go.mod h1:uHzSZqW89h7/pasCWNYdUpwGz3PcVWhrWupreVPYLts=
cloud.google.com/go/compute/metadata v0.5.2 h1:UxK4uu/Tn+I3p2dYWTfiX4wva7aYlKixAHn3fyqngqo=
cloud.google.com/go/compute/metadata v0.5.2/go.mod h1:C66sj2AluDcIqakBq/M8lw8/ybHgOZqin2obFxa/E5k=
cloud.google.com/go/iam v1.2.1 h1:QFct02HRb7H12J/3utj0qf5tobFh9V4vR6h9eX5EBRU=
cloud.google.com/go/iam v1.2.1/go.mod h1:3VUIJDPpwT6p/amXRC5GY8fCCh70lxPygguVtI0Z4/g=
cloud.google.com/go/kms v1.20.0 h1:uKUvjGqbBlI96xGE669hcVnEMw1Px/Mvfa62dhM5UrY=
cloud.google.com/go/kms v1.20.0/go.mod h1:/dMbFF1tLLFnQV44AoI2GlotbjowyUfgVwezxW291fM=
cloud.google.com/go/longrunning v0.6.1 h1:lOLTFxYpr8hcRtcwWir5ITh1PAKUD/sG2lKrTSYjyMc=
cloud.google.com/go/longrunning v0.6.1/go.mod h1:nHISoOZpBcmlwbJmiVk5oDRz0qG/ZxPynEGs1iZ79s0=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2 h1:BnG6pr9TTr6CYlrJznYUDj6V7xldD1W+1iXPum0wT/w=
cuelabs.dev/go/oci/ociregistry v0.0.0-20240404174027-a39bec0462d2/go.mod h1:pK23AUVXuNzzTpfMCA06sxZGeVQ/75FdVtW249de9Uo=
cuelang.org/go v0.9.2 h1:pfNiry2PdRBr02G/aKm5k2vhzmqbAOoaB4WurmEbWvs=
@@ -21,12 +21,12 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=
github.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0 h1:8+4G8JaejP8Xa6W46PzJEwisNgBXMvFcz78N6zG/ARw=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/alibabacloudsdkgo/helper v0.2.0/go.mod h1:GgeIE+1be8Ivm7Sh4RgwI42aTtC9qrcj+Y9Y6CjJhJs=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0 h1:kcnfY4vljxXliXDBrA9K9lwF8IoEZ4Up6Eg9kWTIm28=
github.com/AliyunContainerService/ack-ram-tool/pkg/credentials/provider v0.14.0/go.mod h1:tlqp9mUGbsP+0z3Q+c0Q5MgSdq/OMwQhm5bffR3Q3ss=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=
github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0 h1:GJHeeA2N7xrG3q30L2UXDyuWRzDM900/65j70wcM4Ww=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.13.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 h1:nyQWyZvwGTvunIMxi1Y9uXkcyr+I7TeNrr/foo4Kpk8=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0/go.mod h1:l38EPgmsp71HHLq9j7De57JcKOWPyhrsW1Awm1JS6K0=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0 h1:tfLQ34V6F7tVSwoTf/4lH5sE0o6eCJuNDTmH09nDpbc=
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.7.0/go.mod h1:9kIvujWAA58nmPmWB1m23fyWic1kYZMxD9CxaWn4Qpg=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.10.0 h1:ywEEhmNahHBihViHepv3xPBn1663uRv2t2q/ESv9seY=
@@ -54,18 +54,21 @@ github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBp
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2 h1:XHOnouVk1mxXfQidrMEnLlPk9UMeRtyBTnEFtxkV0kU=
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/DataDog/zstd v1.4.8/go.mod h1:g4AWEaM3yOg3HYfnJ3YIawPnVdXJh9QME85blwSAmyw=
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Microsoft/hcsshim v0.12.6 h1:qEnZjoHXv+4/s0LmKZWE0/AiZmMWEIkFfWBSf1a0wlU=
github.com/Microsoft/hcsshim v0.12.6/go.mod h1:ZABCLVcvLMjIkzr9rUGcQ1QA0p0P3Ps+d3N1g2DsFfk=
github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8=
github.com/OneOfOne/xxhash v1.2.8/go.mod h1:eZbhyaAYD41SGSSsnmcpxVoRiQ/MPUTjUdIIOT9Um7Q=
github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78=
github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0=
github.com/ThalesIgnite/crypto11 v1.2.5 h1:1IiIIEqYmBvUYFeMnHqRft4bwf/O36jryEUpY+9ef8E=
github.com/ThalesIgnite/crypto11 v1.2.5/go.mod h1:ILDKtnCKiQ7zRoNxcp36Y1ZR8LBPmR2E23+wTQe/MlE=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
github.com/agnivade/levenshtein v1.1.1/go.mod h1:veldBMzWxcCG2ZvUTKD2kJNRdCk5hVbJomOvKkmgYbo=
github.com/agnivade/levenshtein v1.2.0 h1:U9L4IOT0Y3i0TIlUIDJ7rVUziKi/zPbrJGaFrtYH3SY=
github.com/agnivade/levenshtein v1.2.0/go.mod h1:QVVI16kDrtSuwcpd0p1+xMC6Z/VfhtCyDIjcwga4/DU=
github.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=
github.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=
github.com/alibabacloud-go/alibabacloud-gateway-spi v0.0.4 h1:iC9YFYKDGEy3n/FtqJnOkZsene9olVspKmkX5A2YBEo=
@@ -88,56 +91,56 @@ github.com/alibabacloud-go/tea-utils v1.4.5 h1:h0/6Xd2f3bPE4XHTvkpjwxowIwRCJAJOq
github.com/alibabacloud-go/tea-utils v1.4.5/go.mod h1:KNcT0oXlZZxOXINnZBs6YvgOd5aYp9U67G+E3R8fcQw=
github.com/alibabacloud-go/tea-xml v1.1.3 h1:7LYnm+JbOq2B+T/B0fHC4Ies4/FofC4zHzYtqw7dgt0=
github.com/alibabacloud-go/tea-xml v1.1.3/go.mod h1:Rq08vgCcCAjHyRi/M7xlHKUykZCEtyBy9+DPF6GgEu8=
github.com/aliyun/credentials-go v1.3.1 h1:uq/0v7kWrxmoLGpqjx7vtQ/s03f0zR//0br/xWDTE28=
github.com/aliyun/credentials-go v1.3.1/go.mod h1:8jKYhQuDawt8x2+fusqa1Y6mPxemTsBEN04dgcAcYz0=
github.com/aliyun/credentials-go v1.3.2 h1:L4WppI9rctC8PdlMgyTkF8bBsy9pyKQEzBD1bHMRl+g=
github.com/aliyun/credentials-go v1.3.2/go.mod h1:tlpz4uys4Rn7Ik4/piGRrTbXy2uLKvePgQJJduE+Y5c=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0 h1:jfIu9sQUG6Ig+0+Ap1h4unLjW6YQJpKZVmUzxsD4E/Q=
github.com/arbovm/levenshtein v0.0.0-20160628152529-48b4e1c0c4d0/go.mod h1:t2tdKJDJF9BV14lnkjHmOQgcvEKgtqs5a1N3LNdJhGE=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=
github.com/aws/aws-sdk-go v1.55.5 h1:KKUZBfBoyqy5d3swXyiC7Q76ic40rYcbqH7qjh59kzU=
github.com/aws/aws-sdk-go v1.55.5/go.mod h1:eRwEWoyTWFMVYVQzKMNHWP5/RV4xIUGMQfXQHfHkpNU=
github.com/aws/aws-sdk-go-v2 v1.30.5 h1:mWSRTwQAb0aLE17dSzztCVJWI9+cRMgqebndjwDyK0g=
github.com/aws/aws-sdk-go-v2 v1.30.5/go.mod h1:CT+ZPWXbYrci8chcARI3OmI/qgd+f6WtuLOoaIA8PR0=
github.com/aws/aws-sdk-go-v2/config v1.27.33 h1:Nof9o/MsmH4oa0s2q9a0k7tMz5x/Yj5k06lDODWz3BU=
github.com/aws/aws-sdk-go-v2/config v1.27.33/go.mod h1:kEqdYzRb8dd8Sy2pOdEbExTTF5v7ozEXX0McgPE7xks=
github.com/aws/aws-sdk-go-v2/credentials v1.17.32 h1:7Cxhp/BnT2RcGy4VisJ9miUPecY+lyE9I8JvcZofn9I=
github.com/aws/aws-sdk-go-v2/credentials v1.17.32/go.mod h1:P5/QMF3/DCHbXGEGkdbilXHsyTBX5D3HSwcrSc9p20I=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13 h1:pfQ2sqNpMVK6xz2RbqLEL0GH87JOwSxPV2rzm8Zsb74=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.13/go.mod h1:NG7RXPUlqfsCLLFfi0+IpKN4sCB9D9fw/qTaSB+xRoU=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17 h1:pI7Bzt0BJtYA0N/JEC6B8fJ4RBrEMi1LBrkMdFYNSnQ=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.17/go.mod h1:Dh5zzJYMtxfIjYW+/evjQ8uj2OyR/ve2KROHGHlSFqE=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17 h1:Mqr/V5gvrhA2gvgnF42Zh5iMiQNcOYthFYwCyrnuWlc=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.17/go.mod h1:aLJpZlCmjE+V+KtN1q1uyZkfnUWpQGpbsn89XPKyzfU=
github.com/aws/aws-sdk-go-v2 v1.32.2 h1:AkNLZEyYMLnx/Q/mSKkcMqwNFXMAvFto9bNsHqcTduI=
github.com/aws/aws-sdk-go-v2 v1.32.2/go.mod h1:2SK5n0a2karNTv5tbP1SjsX0uhttou00v/HpXKM1ZUo=
github.com/aws/aws-sdk-go-v2/config v1.27.43 h1:p33fDDihFC390dhhuv8nOmX419wjOSDQRb+USt20RrU=
github.com/aws/aws-sdk-go-v2/config v1.27.43/go.mod h1:pYhbtvg1siOOg8h5an77rXle9tVG8T+BWLWAo7cOukc=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41 h1:7gXo+Axmp+R4Z+AK8YFQO0ZV3L0gizGINCOWxSLY9W8=
github.com/aws/aws-sdk-go-v2/credentials v1.17.41/go.mod h1:u4Eb8d3394YLubphT4jLEwN1rLNq2wFOlT6OuxFwPzU=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17 h1:TMH3f/SCAWdNtXXVPPu5D6wrr4G5hI1rAxbcocKfC7Q=
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.17/go.mod h1:1ZRXLdTpzdJb9fwTMXiLipENRxkGMTn1sfKexGllQCw=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21 h1:UAsR3xA31QGf79WzpG/ixT9FZvQlh5HY1NRqSHBNOCk=
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.21/go.mod h1:JNr43NFf5L9YaG3eKTm7HQzls9J+A9YYcGI5Quh1r2Y=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21 h1:6jZVETqmYCadGFvrYEQfC5fAQmlo80CeL5psbno6r0s=
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.21/go.mod h1:1SR0GbLlnN3QUmYaflZNiH1ql+1qrSiB2vwcJ+4UM60=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 h1:VaRN3TlFdd6KxX1x3ILT5ynH6HvKgqdiXoTxAF4HQcQ=
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc=
github.com/aws/aws-sdk-go-v2/service/ecr v1.29.1 h1:ywNLJrn/Qn4enDsz/XnKlvpnLqvJxFGQV2BltWltbis=
github.com/aws/aws-sdk-go-v2/service/ecr v1.29.1/go.mod h1:WadVIk+UrTvWuAsCp6BKGX4i2snurpz8mPWhJQnS7Dg=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.24.1 h1:Eq9i/mvOlGghiKe9NtsmeD9Wlwg8p4fbsqrMb3nWirM=
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.24.1/go.mod h1:VtOgEoLEPV1YADuq+Z2XOK6/wKkGW2YK6DjChZ/GvDs=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 h1:KypMCbLPPHEmf9DgMGw51jMj77VfGPAN2Kv4cfhlfgI=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4/go.mod h1:Vz1JQXliGcQktFTN/LN6uGppAIRoLBR2bMvIMP0gOjc=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19 h1:rfprUlsdzgl7ZL2KlXiUAoJnI/VxfHCvDFr2QDFj6u4=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.19/go.mod h1:SCWkEdRq8/7EK60NcvvQ6NXKuTcchAD4ROAsC37VEZE=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.5 h1:XUomV7SiclZl1QuXORdGcfFqHxEHET7rmNGtxTfNB+M=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.5/go.mod h1:A5CS0VRmxxj2YKYLCY08l/Zzbd01m6JZn0WzxgT1OCA=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7 h1:pIaGg+08llrP7Q5aiz9ICWbY8cqhTkyy+0SHvfzQpTc=
github.com/aws/aws-sdk-go-v2/service/sso v1.22.7/go.mod h1:eEygMHnTKH/3kNp9Jr1n3PdejuSNcgwLe1dWgQtO0VQ=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7 h1:/Cfdu0XV3mONYKaOt1Gr0k1KvQzkzPyiKUdlWJqy+J4=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.7/go.mod h1:bCbAxKDqNvkHxRaIMnyVPXPo+OaPRwvmgzMxbz1VKSA=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7 h1:NKTa1eqZYw8tiHSRGpP0VtTdub/8KNk8sDkNPFaOKDE=
github.com/aws/aws-sdk-go-v2/service/sts v1.30.7/go.mod h1:NXi1dIAGteSaRLqYgarlhP/Ij0cFT+qmCwiJqWh/U5o=
github.com/aws/smithy-go v1.20.4 h1:2HK1zBdPgRbjFOHlfeQZfpC4r72MOb9bZkiFwggKO+4=
github.com/aws/smithy-go v1.20.4/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0 h1:TToQNkvGguu209puTojY/ozlqy2d/SFNcoLIqTFi42g=
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.0/go.mod h1:0jp+ltwkf+SwG2fm/PKo8t4y8pJSgOCO4D8Lz3k0aHQ=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2 h1:s7NA1SOw8q/5c0wr8477yOPp0z+uBaXBnLE0XYb0POA=
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.2/go.mod h1:fnjjWyAW/Pj5HYOxl9LJqWtEwS7W2qgcRLWP+uWbss0=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.7 h1:v0D1LeMkA/X+JHAZWERrr+sUGOt8KrCZKnJA6KszkcE=
github.com/aws/aws-sdk-go-v2/service/kms v1.35.7/go.mod h1:K9lwD0Rsx9+NSaJKsdAdlDK4b2G4KKOEve9PzHxPoMI=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2 h1:bSYXVyUzoTHoKalBmwaZxs97HU9DWWI3ehHSAMa7xOk=
github.com/aws/aws-sdk-go-v2/service/sso v1.24.2/go.mod h1:skMqY7JElusiOUjMJMOv1jJsP7YUg7DrhgqZZWuzu1U=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2 h1:AhmO1fHINP9vFYUE0LHzCWg/LfUWUF+zFPEcY9QXb7o=
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.2/go.mod h1:o8aQygT2+MVP0NaV6kbdE1YnnIM8RRVQzoeUH45GOdI=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2 h1:CiS7i0+FUe+/YY1GvIBLLrR/XNGZ4CtM1Ll0XavNuVo=
github.com/aws/aws-sdk-go-v2/service/sts v1.32.2/go.mod h1:HtaiBI8CjYoNVde8arShXb94UbQQi9L4EMr6D+xGBwo=
github.com/aws/smithy-go v1.22.0 h1:uunKnWlcoL3zO7q+gG2Pk53joueEOsnNB28QdMsmiMM=
github.com/aws/smithy-go v1.22.0/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8 h1:SoFYaT9UyGkR0+nogNyD/Lj+bsixB+SNuAS4ABlEs6M=
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20231024185945-8841054dbdb8/go.mod h1:2JF49jcDOrLStIXN/j/K1EKRq8a8R2qRnlZA6/o/c7c=
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=
github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=
github.com/buildkite/agent/v3 v3.76.2 h1:SweFq3e0N20RikWsVeOXzTjfr0AoOskxm9c0bcNyI0E=
github.com/buildkite/agent/v3 v3.76.2/go.mod h1:9ffbmJD7d7C/nOcElj6Qm+uIj1QoYh3NNvka4rkKkss=
github.com/buildkite/go-pipeline v0.10.0 h1:EDffu+LfMY2k5u+iEdo6Jn3obGKsrL5wicc1O/yFeRs=
github.com/buildkite/go-pipeline v0.10.0/go.mod h1:eMH1kiav5VeiTiu0Mk2/M7nZhKyFeL4iGj7Y7rj4f3w=
github.com/buildkite/agent/v3 v3.81.0 h1:JVfkng2XnsXesFXwiFwLJFkuzVu4zvoJCvedfoIXD6E=
github.com/buildkite/agent/v3 v3.81.0/go.mod h1:edJeyycODRxaFvpT22rDGwaQ5oa4eB8GjtbjgX5VpFw=
github.com/buildkite/go-pipeline v0.13.1 h1:Y9p8pQIwPtauVwNrcmTDH6+XK7jE1nLuvWVaK8oymA8=
github.com/buildkite/go-pipeline v0.13.1/go.mod h1:2HHqlSFTYgHFhzedJu0LhLs9n5c9XkYnHiQFVN5HE4U=
github.com/buildkite/interpolate v0.1.3 h1:OFEhqji1rNTRg0u9DsSodg63sjJQEb1uWbENq9fUOBM=
github.com/buildkite/interpolate v0.1.3/go.mod h1:UNVe6A+UfiBNKbhAySrBbZFZFxQ+DXr9nWen6WVt/A8=
github.com/buildkite/roko v1.2.0 h1:hbNURz//dQqNl6Eo9awjQOVOZwSDJ8VEbBDxSfT9rGQ=
@@ -172,6 +175,12 @@ github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUo
github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be h1:J5BL2kskAlV9ckgEsNQXscjIaLiOYiZ75d4e94E6dcQ=
github.com/common-nighthawk/go-figure v0.0.0-20210622060536-734e95fb86be/go.mod h1:mk5IQ+Y0ZeO87b858TlA645sVcEcbiX6YqP98kt+7+w=
github.com/containerd/containerd v1.7.22 h1:nZuNnNRA6T6jB975rx2RRNqqH2k6ELYKDZfqTHqwyy0=
github.com/containerd/containerd v1.7.22/go.mod h1:e3Jz1rYRUZ2Lt51YrH9Rz0zPyJBOlSvB3ghr2jbVD8g=
github.com/containerd/containerd/v2 v2.0.0-rc.5 h1:ejHlOWce4MxQr+lGXdbCCyOZIWqIq027DjXI73SUwg8=
github.com/containerd/containerd/v2 v2.0.0-rc.5/go.mod h1:biu3ZtJwDHEdVxxho680KtHqFfk8WPA1hULNf/vJ7Lw=
github.com/containerd/errdefs v0.2.0 h1:XllDESRfJtVrMwMmR2mCabxyvBK4UlbyyiWI3MvRw0o=
github.com/containerd/errdefs v0.2.0/go.mod h1:C28ixlj3dKhQS9hsQ13b+HIb4X7+s2G4FYhbSPcRDLM=
github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I=
github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo=
github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A=
@@ -193,8 +202,8 @@ github.com/dgraph-io/badger/v3 v3.2103.5 h1:ylPa6qzbjYRQMU6jokoj4wzcaweHylt//CH0
github.com/dgraph-io/badger/v3 v3.2103.5/go.mod h1:4MPiseMeDQ3FNCYwRbbcBOGJLf5jsE0PPFzRiKjtcdw=
github.com/dgraph-io/ristretto v0.1.1 h1:6CWw5tJNgpegArSHpNHJKldNeq03FQCwYvfMVWajOK8=
github.com/dgraph-io/ristretto v0.1.1/go.mod h1:S1GPSBCYCIhmVNfcth17y2zZtQT6wzkzgwUve0VDWWA=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48 h1:fRzb/w+pyskVMQ+UbP35JkH8yB7MYb4q/qhBarqZE6g=
github.com/dgryski/trifles v0.0.0-20200323201526-dd97f9abfb48/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54 h1:SG7nF6SRlWhcT7cNTs5R6Hk4V2lcmLz2NsG2VnInyNo=
github.com/dgryski/trifles v0.0.0-20230903005119-f50d829f2e54/go.mod h1:if7Fbed8SFyPtHLHbg49SI7NAdJiC5WIA09pe59rfAA=
github.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352 h1:ge14PCmCvPjpMQMIAH7uKg0lrtNSOdpYsRXlwk3QbaE=
github.com/digitorus/pkcs7 v0.0.0-20230818184609-3a137a874352/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=
@@ -204,6 +213,8 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
github.com/docker-library/bashbrew v0.1.12 h1:qykd2fxTMiudN/70XItEQqgk/7LeVoDiBTEnKTpkst8=
github.com/docker-library/bashbrew v0.1.12/go.mod h1:6fyRRSm4vgBAgTw87EsfOT7wXKsc4JA9I5cdQJmwOm8=
github.com/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE=
github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
@@ -237,14 +248,16 @@ github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
github.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=
github.com/go-chi/chi v4.1.2+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
github.com/go-ini/ini v1.67.0 h1:z6ZrTEZqSWOTyH2FlglNbNgARyHG8oLW9gMELqKr06A=
github.com/go-ini/ini v1.67.0/go.mod h1:ByCAeIL28uOIIG0E3PJtZPDL8WnHpFKFOtgjp+3Ies8=
github.com/go-jose/go-jose/v3 v3.0.3 h1:fFKWeig/irsp7XD2zBxvnmA/XaRWp5V3CBsZXJF7G7k=
github.com/go-jose/go-jose/v3 v3.0.3/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
github.com/go-jose/go-jose/v4 v4.0.2 h1:R3l3kkBds16bO7ZFAEEcofK0MkrAJt3jlJznWZG0nvk=
github.com/go-jose/go-jose/v4 v4.0.2/go.mod h1:WVf9LFMHh/QVrmqrOfqun0C45tMe3RoiKJMPvgWwLfY=
github.com/go-jose/go-jose/v4 v4.0.4 h1:VsjPI33J0SB9vQM6PLmNjoHqMQNGPiZ0rHL7Ni7Q6/E=
github.com/go-jose/go-jose/v4 v4.0.4/go.mod h1:NKb5HO1EZccyMpiZNbdUw/14tiXNyUJh188dfnMCAfc=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@@ -286,8 +299,8 @@ github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4=
github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/glog v1.2.2 h1:1+mZ9upx1Dh6FmUTFR1naJ77miKiXgALjWOZ3NVFPmY=
github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -347,8 +360,8 @@ github.com/googleapis/gax-go/v2 v2.13.0/go.mod h1:Z/fvTZXF8/uw7Xu5GuslPw+bplx6SS
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys=
github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I=
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
github.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=
@@ -377,6 +390,8 @@ github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpO
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/in-toto/attestation v1.1.0 h1:oRWzfmZPDSctChD0VaQV7MJrywKOzyNrtpENQFq//2Q=
github.com/in-toto/attestation v1.1.0/go.mod h1:DB59ytd3z7cIHgXxwpSX2SABrU6WJUKg/grpdgHVgVs=
github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU=
github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
@@ -395,8 +410,9 @@ github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8Hm
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=
github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=
github.com/kjk/lzma v0.0.0-20161016003348-3fd93898850d/go.mod h1:phT/jsRPBAEqjAibu1BurrabCBNTYiVI+zbmyCZJY6Q=
github.com/klauspost/compress v1.17.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0=
github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -427,8 +443,8 @@ github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mozillazg/docker-credential-acr-helper v0.3.0 h1:DVWFZ3/O8BP6Ue3iS/Olw+G07u1hCq1EOVCDZZjCIBI=
github.com/mozillazg/docker-credential-acr-helper v0.3.0/go.mod h1:cZlu3tof523ujmLuiNUb6JsjtHcNA70u1jitrrdnuyA=
github.com/mozillazg/docker-credential-acr-helper v0.4.0 h1:Uoh3Z9CcpEDnLiozDx+D7oDgRq7X+R296vAqAumnOcw=
github.com/mozillazg/docker-credential-acr-helper v0.4.0/go.mod h1:2kiicb3OlPytmlNC9XGkLvVC+f0qTiJw3f/mhmeeQBg=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA=
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
github.com/nozzle/throttler v0.0.0-20180817012639-2ea982251481 h1:Up6+btDp321ZG5/zdSLo48H9Iaq0UQGthrhWC6pCxzE=
@@ -439,8 +455,8 @@ github.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=
github.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=
github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/oleiade/reflections v1.0.1 h1:D1XO3LVEYroYskEsoSiGItp9RUxG6jWnCVvrqH0HHQM=
github.com/oleiade/reflections v1.0.1/go.mod h1:rdFxbxq4QXVZWj0F+e9jqjDkc7dbp97vkRixKo2JR60=
github.com/oleiade/reflections v1.1.0 h1:D+I/UsXQB4esMathlt0kkZRJZdUDmhv5zGi/HOwYTWo=
github.com/oleiade/reflections v1.1.0/go.mod h1:mCxx0QseeVCHs5Um5HhJeCKVC7AwS8kO67tky4rdisA=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
@@ -452,8 +468,8 @@ github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1y
github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/onsi/gomega v1.19.0 h1:4ieX6qQjPP/BfC3mpsAtIGGlxTWPeA3Inl/7DtXw1tw=
github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro=
github.com/open-policy-agent/opa v0.68.0 h1:Jl3U2vXRjwk7JrHmS19U3HZO5qxQRinQbJ2eCJYSqJQ=
github.com/open-policy-agent/opa v0.68.0/go.mod h1:5E5SvaPwTpwt2WM177I9Z3eT7qUpmOGjk1ZdHs+TZ4w=
github.com/open-policy-agent/opa v0.69.0 h1:s2igLw2Z6IvGWGuXSfugWkVultDMsM9pXiDuMp7ckWw=
github.com/open-policy-agent/opa v0.69.0/go.mod h1:+qyXJGkpEJ6kpB1kGo8JSwHtVXbTdsGdQYPWWNYNj+4=
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
@@ -464,8 +480,8 @@ github.com/package-url/packageurl-go v0.1.3 h1:4juMED3hHiz0set3Vq3KeQ75KD1avthoX
github.com/package-url/packageurl-go v0.1.3/go.mod h1:nKAWB8E6uk1MHqiS/lQb9pYBGH2+mdJ2PJc2s50dQY0=
github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw=
github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k=
github.com/pelletier/go-toml/v2 v2.2.2 h1:aYUidT7k73Pcl9nb2gScu7NSrKCSHIDE89b3+6Wq+LM=
github.com/pelletier/go-toml/v2 v2.2.2/go.mod h1:1t835xjRzz80PqgE6HHgN2JOsmgYu/h4qDAS4n929Rs=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
@@ -473,8 +489,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/prometheus/client_golang v1.20.2 h1:5ctymQzZlyOON1666svgwn3s6IKWgfbjsejTMiXIyjg=
github.com/prometheus/client_golang v1.20.2/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_golang v1.20.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI=
github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
@@ -505,18 +521,18 @@ github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=
github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=
github.com/sigstore/cosign/v2 v2.4.0 h1:2NdidNgClg+oXr/fDIr37E/BE6j00gqgUhSiBK2kjSQ=
github.com/sigstore/cosign/v2 v2.4.0/go.mod h1:j+fH1DCUkcn92qp6ezDj4JbGMri6eG1nLJC+hs64rvc=
github.com/sigstore/fulcio v1.5.1 h1:Iasy1zfNjaq8BV4S8o6pXspLDU28PQC2z07GmOu9zpM=
github.com/sigstore/fulcio v1.5.1/go.mod h1:W1A/UHrTopy1IBZPMtHmxg7GPYAu+vt5dRXM3W6yjPo=
github.com/sigstore/cosign/v2 v2.4.1 h1:b8UXEfJFks3hmTwyxrRNrn6racpmccUycBHxDMkEPvU=
github.com/sigstore/cosign/v2 v2.4.1/go.mod h1:GvzjBeUKigI+XYnsoVQDmMAsMMc6engxztRSuxE+x9I=
github.com/sigstore/fulcio v1.6.3 h1:Mvm/bP6ELHgazqZehL8TANS1maAkRoM23CRAdkM4xQI=
github.com/sigstore/fulcio v1.6.3/go.mod h1:5SDgLn7BOUVLKe1DwOEX3wkWFu5qEmhUlWm+SFf0GH8=
github.com/sigstore/protobuf-specs v0.3.2 h1:nCVARCN+fHjlNCk3ThNXwrZRqIommIeNKWwQvORuRQo=
github.com/sigstore/protobuf-specs v0.3.2/go.mod h1:RZ0uOdJR4OB3tLQeAyWoJFbNCBFrPQdcokntde4zRBA=
github.com/sigstore/rekor v1.3.6 h1:QvpMMJVWAp69a3CHzdrLelqEqpTM3ByQRt5B5Kspbi8=
github.com/sigstore/rekor v1.3.6/go.mod h1:JDTSNNMdQ/PxdsS49DJkJ+pRJCO/83nbR5p3aZQteXc=
github.com/sigstore/sigstore v1.8.8 h1:B6ZQPBKK7Z7tO3bjLNnlCMG+H66tO4E/+qAphX8T/hg=
github.com/sigstore/sigstore v1.8.8/go.mod h1:GW0GgJSCTBJY3fUOuGDHeFWcD++c4G8Y9K015pwcpDI=
github.com/sigstore/sigstore-go v0.5.1 h1:5IhKvtjlQBeLnjKkzMELNG4tIBf+xXQkDzhLV77+/8Y=
github.com/sigstore/sigstore-go v0.5.1/go.mod h1:TuOfV7THHqiDaUHuJ5+QN23RP/YoKmsbwJpY+aaYPN0=
github.com/sigstore/sigstore v1.8.9 h1:NiUZIVWywgYuVTxXmRoTT4O4QAGiTEKup4N1wdxFadk=
github.com/sigstore/sigstore v1.8.9/go.mod h1:d9ZAbNDs8JJfxJrYmulaTazU3Pwr8uLL9+mii4BNR3w=
github.com/sigstore/sigstore-go v0.6.1 h1:tGkkv1oDIER+QYU5MrjqlttQOVDWfSkmYwMqkJhB/cg=
github.com/sigstore/sigstore-go v0.6.1/go.mod h1:Xe5GHmUeACRFbomUWzVkf/xYCn8xVifb9DgqJrV2dIw=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.9 h1:tgpdvjyoEgYFeTBFe4MHvBKsG+J4E7NVtstChIExVT8=
github.com/sigstore/sigstore/pkg/signature/kms/aws v1.8.9/go.mod h1:wCz6cAZKL/wFumDHX9l8VkVITS2GntrOfs2j/kwH4wo=
github.com/sigstore/sigstore/pkg/signature/kms/azure v1.8.8 h1:RKk4Z+qMaLORUdT7zntwMqKiYAej1VQlCswg0S7xNSY=
@@ -548,7 +564,6 @@ github.com/spiffe/go-spiffe/v2 v2.3.0/go.mod h1:Oxsaio7DBgSNqhAO9i/9tLClaVlfRok7
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
@@ -556,7 +571,6 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.7.2/go.mod h1:R6va5+xMeoiuVRoj+gSkQ7d3FALtqAAGI1FQKckRals=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
@@ -569,8 +583,8 @@ github.com/thales-e-security/pool v0.0.2 h1:RAPs4q2EbWsTit6tpzuvTFlgFRJ3S8Evf5gt
github.com/thales-e-security/pool v0.0.2/go.mod h1:qtpMm2+thHtqhLzTwgDBj/OuNnMpupY8mv0Phz0gjhU=
github.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=
github.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=
github.com/theupdateframework/go-tuf/v2 v2.0.0 h1:rD8d9RotYBprZVgC+9oyTZ5MmawepnTSTqoDuxjWgbs=
github.com/theupdateframework/go-tuf/v2 v2.0.0/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA=
github.com/theupdateframework/go-tuf/v2 v2.0.2 h1:PyNnjV9BJNzN1ZE6BcWK+5JbF+if370jjzO84SS+Ebo=
github.com/theupdateframework/go-tuf/v2 v2.0.2/go.mod h1:baB22nBHeHBCeuGZcIlctNq4P61PcOdyARlplg5xmLA=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=
github.com/tjfoc/gmsm v1.4.1 h1:aMe1GlZb+0bLjn+cKTPEvvn9oUEBlJitaZiiBwsbgho=
@@ -579,12 +593,15 @@ github.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG
github.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=
github.com/vbatts/tar-split v0.11.5 h1:3bHCTIheBm1qFTcgh9oPu+nNBtX+XJIupG/vacinCts=
github.com/vbatts/tar-split v0.11.5/go.mod h1:yZbwRsSeGjusneWgA781EKej9HF8vme8okylkAeNKLk=
github.com/xanzy/go-gitlab v0.107.0 h1:P2CT9Uy9yN9lJo3FLxpMZ4xj6uWcpnigXsjvqJ6nd2Y=
github.com/xanzy/go-gitlab v0.107.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM=
github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg=
github.com/xanzy/go-gitlab v0.109.0 h1:RcRme5w8VpLXTSTTMZdVoQWY37qTJWg+gwdQl4aAttE=
github.com/xanzy/go-gitlab v0.109.0/go.mod h1:wKNKh3GkYDMOsGmnfuX+ITCmDuSDWFO0G+C4AygL9RY=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
github.com/yashtewari/glob-intersection v0.2.0 h1:8iuHdN88yYuCzCdjt0gDe+6bAhUwBeEWqThExu54RFg=
github.com/yashtewari/glob-intersection v0.2.0/go.mod h1:LK7pIC3piUjovexikBbJ26Yml7g8xa5bsjfx2v1fwok=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
@@ -597,26 +614,26 @@ go.mongodb.org/mongo-driver v1.15.0 h1:rJCKC8eEliewXjZGf0ddURtl7tTVy1TK3bfl0gkUS
go.mongodb.org/mongo-driver v1.15.0/go.mod h1:Vzb0Mk/pa7e6cWw85R4F/endUC3u0U9jGcNU603k65c=
go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0=
go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0 h1:TT4fX+nBOA/+LUkobKGW1ydGcn+G3vRw9+g5HwCphpk=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
go.opentelemetry.io/otel v1.29.0 h1:PdomN/Al4q/lN6iBJEN3AwPvUiHPMlt93c8bqTG5Llw=
go.opentelemetry.io/otel v1.29.0/go.mod h1:N/WtXPs1CNCUEx+Agz5uouwCba+i+bJGFicT8SR4NP8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0 h1:R3X6ZXmNPRR8ul6i3WgFURCHzaXjHdm0karRG/+dj3s=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.28.0/go.mod h1:QWFXnDavXWwMx2EEcZsf3yxgEKAqsxQ+Syjp+seyInw=
go.opentelemetry.io/otel/metric v1.29.0 h1:vPf/HFWTNkPu1aYeIsc98l4ktOQaL6LeSoeV2g+8YLc=
go.opentelemetry.io/otel/metric v1.29.0/go.mod h1:auu/QWieFVWx+DmQOUMgj0F8LHWdgalxXqvp7BII/W8=
go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE=
go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg=
go.opentelemetry.io/otel/trace v1.29.0 h1:J/8ZNK4XgR7a21DZUAsbF8pZ5Jcw1VhACmnYt39JTi4=
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q=
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI=
go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts=
go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4=
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8=
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4=
go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w=
go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ=
go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE=
go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg=
go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc=
go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o=
go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0=
go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8=
go.step.sm/crypto v0.51.1 h1:ktUg/2hetEMiBAqgz502ktZDGoDoGrcHFg3XpkmkvvA=
go.step.sm/crypto v0.51.1/go.mod h1:PdrhttNU/tG9/YsVd4fdlysBN+UV503p0o2irFZQlAw=
go.step.sm/crypto v0.51.2 h1:5EiCGIMg7IvQTGmJrwRosbXeprtT80OhoS/PJarg60o=
go.step.sm/crypto v0.51.2/go.mod h1:QK7czLjN2k+uqVp5CHXxJbhc70kVRSP+0CQF3zsR5M0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
@@ -626,22 +643,23 @@ go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20201016220609-9e8e0b390897/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842 h1:vr/HnozRka3pE4EsMEg1lgkXJkTFJCVUX+S/ZT6wYzM=
golang.org/x/exp v0.0.0-20240506185415-9bf2ced13842/go.mod h1:XtvwrStGgqGPLc4cjQfWqZHG1YFdYs6swckp8vpsjnc=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8 h1:yixxcjnhBmY0nkL253HFVIm0JsFHwrHdT3Yh6szTnfY=
golang.org/x/exp v0.0.0-20240613232115-7f521ea00fb8/go.mod h1:jj3sYF3dwk5D+ghuXyeI3r5MFf+NT2An6/9dOA95KSI=
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.19.0 h1:fEdghXQSo20giMthA7cd28ZC+jts4amQ3YMXiP5oMQ8=
golang.org/x/mod v0.19.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -660,8 +678,8 @@ golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
golang.org/x/net v0.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo=
golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
@@ -696,15 +714,15 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34=
golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
golang.org/x/term v0.24.0 h1:Mh5cbb+Zk2hqqXNO7S1iTjEphVL+jb8ZWaqh/g+JWkM=
golang.org/x/term v0.24.0/go.mod h1:lOBK/LVxemqiMij05LGJ0tzNr8xlmwBRJ81PX6wVLH8=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
@@ -712,10 +730,10 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.6.0 h1:eTDhh4ZXt5Qf0augr54TN6suAUudPcawVZeIAPU7D4U=
golang.org/x/time v0.6.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
@@ -732,26 +750,26 @@ golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20220517211312-f3a8303e98df/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8=
google.golang.org/api v0.197.0 h1:x6CwqQLsFiA5JKAiGyGBjc2bNtHtLddhJCE2IKuhhcQ=
google.golang.org/api v0.197.0/go.mod h1:AuOuo20GoQ331nq7DquGHlU6d+2wN2fZ8O0ta60nRNw=
google.golang.org/api v0.200.0 h1:0ytfNWn101is6e9VBoct2wrGDjOi5vn7jw5KtaQgDrU=
google.golang.org/api v0.200.0/go.mod h1:Tc5u9kcbjO7A8SwGlYj4IiVifJU01UqXtEgDMYmBmV8=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1 h1:BulPr26Jqjnd4eYDVe+YvyR7Yc2vJGkO5/0UxD0/jZU=
google.golang.org/genproto v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:hL97c3SYopEHblzpxRL4lSs523++l8DYxGM1FQiYmb4=
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed h1:3RgNmBoI9MZhsj3QxC+AP/qQhNwpCLOvYDYYsFrhFt0=
google.golang.org/genproto/googleapis/api v0.0.0-20240827150818-7e3bb234dfed/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1 h1:pPJltXNxVzT4pK9yD8vR9X75DaWYYmLGMsEvBfFQZzQ=
google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU=
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9 h1:nFS3IivktIU5Mk6KQa+v6RKkHUpdQpphqGNLxqNnbEk=
google.golang.org/genproto v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:tEzYTYZxbmVNOu0OAFH9HzdJtLn6h4Aj89zzlBCdHms=
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA=
google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9 h1:QCqS/PdaHTSWGvupk2F/ehwHtGc0/GYkT+3GAcR1CCc=
google.golang.org/genproto/googleapis/rpc v0.0.0-20241007155032-5fefd90f89a9/go.mod h1:GX3210XPVPUjJbTUbvwI8f2IpZDMZuPJWDzDuebbviI=
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc=
google.golang.org/grpc v1.66.1 h1:hO5qAXR19+/Z44hmvIM4dQFMSYX9XcWsByfoxutBpAM=
google.golang.org/grpc v1.66.1/go.mod h1:s3/l6xSSCURdVfAnL+TqCNMyTDAGN6+lZeVxnZR128Y=
google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E=
google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA=
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
@@ -763,8 +781,8 @@ google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpAD
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
google.golang.org/protobuf v1.35.1 h1:m3LfL6/Ca+fqnjnlqQXNpFPABW1UD7mjh8KO2mKFytA=
google.golang.org/protobuf v1.35.1/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -788,24 +806,29 @@ gotest.tools/v3 v3.5.1 h1:EENdUnS3pdur5nybKYIh2Vfgc8IUNBjxDPSjtiJcOzU=
gotest.tools/v3 v3.5.1/go.mod h1:isy3WKz7GK6uNw/sbHzfKBLvlvXwUyV06n6brMxxopU=
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
k8s.io/api v0.28.3 h1:Gj1HtbSdB4P08C8rs9AR94MfSGpRhJgsS+GF9V26xMM=
k8s.io/api v0.28.3/go.mod h1:MRCV/jr1dW87/qJnZ57U5Pak65LGmQVkKTzf3AtKFHc=
k8s.io/apimachinery v0.28.3 h1:B1wYx8txOaCQG0HmYF6nbpU8dg6HvA06x5tEffvOe7A=
k8s.io/apimachinery v0.28.3/go.mod h1:uQTKmIqs+rAYaq+DFaoD2X7pcjLOqbQX2AOiO0nIpb8=
k8s.io/client-go v0.28.3 h1:2OqNb72ZuTZPKCl+4gTKvqao0AMOl9f3o2ijbAj3LI4=
k8s.io/client-go v0.28.3/go.mod h1:LTykbBp9gsA7SwqirlCXBWtK0guzfhpoW4qSm7i9dxo=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU=
k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI=
k8s.io/apimachinery v0.31.1 h1:mhcUBbj7KUjaVhyXILglcVjuS4nYXiwC+KKFBgIVy7U=
k8s.io/apimachinery v0.31.1/go.mod h1:rsPdaZJfTfLsNJSQzNHQvYoTmxhoOEofxtOsF3rtsMo=
k8s.io/client-go v0.31.1 h1:f0ugtWSbWpxHR7sjVpQwuvw9a3ZKLXX0u0itkFXufb0=
k8s.io/client-go v0.31.1/go.mod h1:sKI8871MJN2OyeqRlmA4W4KM9KBdBUpDLu/43eGemCg=
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00 h1:aVUu9fTY98ivBPKR9Y5w/AuzbMm96cd3YHRTU83I780=
k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA=
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0 h1:jgGTlFYnhF1PM1Ax/lAlxUPE+KfCIXHaathvJg1C3ak=
k8s.io/utils v0.0.0-20240502163921-fe8a2dddb1d0/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8 h1:pUdcCO1Lk/tbT5ztQWOBi5HBgbBP1J8+AsQnQCKsi8A=
k8s.io/utils v0.0.0-20240711033017-18e509b52bc8/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
pault.ag/go/debian v0.12.0 h1:b8ctSdBSGJ98NE1VLn06aSx70EUpczlP2qqSHEiYYJA=
pault.ag/go/debian v0.12.0/go.mod h1:UbnMr3z/KZepjq7VzbYgBEfz8j4+Pyrm2L5X1fzhy/k=
pault.ag/go/topsort v0.0.0-20160530003732-f98d2ad46e1a/go.mod h1:INqx0ClF7kmPAMk2zVTX8DRnhZ/yaA/Mg52g8KFKE7k=
pault.ag/go/topsort v0.1.1 h1:L0QnhUly6LmTv0e3DEzbN2q6/FGgAcQvaEw65S53Bg4=
pault.ag/go/topsort v0.1.1/go.mod h1:r1kc/L0/FZ3HhjezBIPaNVhkqv8L0UJ9bxRuHRVZ0q4=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd h1:EDPBXCAspyGV4jQlpZSudPeMmr1bNJefnuqLsRAsHZo=
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0=
sigs.k8s.io/release-utils v0.8.4 h1:4QVr3UgbyY/d9p74LBhg0njSVQofUsAZqYOzVZBhdBw=
sigs.k8s.io/release-utils v0.8.4/go.mod h1:m1bHfscTemQp+z+pLCZnkXih9n0+WukIUU70n6nFnU0=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0 h1:UZbZAZfX0wV2zr7YZorDz6GXROfDFj6LvqCRm4VUVKk=
sigs.k8s.io/structured-merge-diff/v4 v4.3.0/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 h1:150L+0vs/8DA78h1u02ooW1/fFq/Lwr+sGiqlzvrtq4=
sigs.k8s.io/structured-merge-diff/v4 v4.4.1/go.mod h1:N8hJocpFajUSSeSJ9bOZ77VzejKZaXsTtZo4/u7Io08=
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
software.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=

View File

@@ -7,32 +7,36 @@ import (
_ "embed"
"encoding/pem"
"fmt"
"io"
"log"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"strings"
"testing"
"time"
"github.com/docker/attest/attestation"
"github.com/docker/attest/signerverifier"
"github.com/docker/attest/tlog"
"github.com/docker/attest/useragent"
"github.com/google/go-containerregistry/pkg/registry"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
const (
UseMockTL = true
UseMockKMS = true
AWSRegion = "us-east-1"
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
UseMockKMS = true
AWSRegion = "us-east-1"
AWSKMSKeyARN = "arn:aws:kms:us-east-1:175142243308:alias/doi-signing" // sandbox
UnsignedLinuxAMD64ImageDigest = "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"
UnsignedLinuxArm64ImageDigest = "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e"
)
func UnsignedTestIndex(rel ...string) string {
rel = append(rel, "test", "testdata", "unsigned-index")
return filepath.Join(rel...)
}
func UnsignedTestImage(rel ...string) string {
rel = append(rel, "test", "testdata", "unsigned-test-image")
rel = append(rel, "test", "testdata", "unsigned-image")
return filepath.Join(rel...)
}
@@ -60,15 +64,7 @@ func GetMockSigner(_ context.Context) (dsse.SignerVerifier, error) {
}
func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
var tl tlog.TL
if UseMockTL {
tl = tlog.GetMockTL()
} else {
tl = &tlog.RekorTL{}
}
ctx := tlog.WithTL(context.Background(), tl)
ctx := context.Background()
var signer dsse.SignerVerifier
var err error
if UseMockKMS {
@@ -87,6 +83,7 @@ func Setup(t *testing.T) (context.Context, dsse.SignerVerifier) {
}
func NewLocalRegistry(ctx context.Context, options ...registry.Option) *httptest.Server {
options = append(options, registry.Logger(log.New(io.Discard, "", log.LstdFlags)))
regHandler := registry.New(options...)
return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Check the user agent
@@ -99,7 +96,7 @@ func NewLocalRegistry(ctx context.Context, options ...registry.Option) *httptest
}))
}
func publicKeyToPEM(pubKey crypto.PublicKey) (string, error) {
func PublicKeyToPEM(pubKey crypto.PublicKey) (string, error) {
derBytes, err := x509.MarshalPKIXPublicKey(pubKey)
if err != nil {
return "", err
@@ -112,24 +109,3 @@ func publicKeyToPEM(pubKey crypto.PublicKey) (string, error) {
return string(pem.EncodeToMemory(pemBlock)), nil
}
// LoadKeyMetadata loads the key metadata for the given signer verifier.
func GenKeyMetadata(sv dsse.SignerVerifier) (*attestation.KeyMetadata, error) {
pub := sv.Public()
pem, err := publicKeyToPEM(pub)
if err != nil {
return nil, fmt.Errorf("failed to convert public key to PEM: %w", err)
}
id, err := sv.KeyID()
if err != nil {
return nil, err
}
return &attestation.KeyMetadata{
ID: id,
Status: "active",
SigningFormat: "dssev1",
From: time.Now(),
PEM: pem,
}, nil
}

View File

@@ -1,4 +1,4 @@
package config
package mapping
import (
"errors"
@@ -8,6 +8,7 @@ import (
"regexp"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
"sigs.k8s.io/yaml"
)
@@ -33,6 +34,13 @@ func validateMappingsFile(mappings *policyMappingsFile) error {
if rule.PolicyID != "" && rule.Replacement != "" {
validationErrors = append(validationErrors, fmt.Errorf("rule cannot have both policy-id and replacement: %s", rule))
}
if rule.Platforms != nil {
for _, platform := range rule.Platforms {
if platform == "" {
validationErrors = append(validationErrors, fmt.Errorf("rule has empty platform: %s", rule))
}
}
}
}
for _, policy := range mappings.Policies {
if policy.ID == "" {
@@ -100,14 +108,24 @@ func expandMappingFile(mappingFile *policyMappingsFile) (*PolicyMappings, error)
var rules []*PolicyRule
for _, rule := range mappingFile.Rules {
r, err := regexp.Compile(rule.Pattern)
patternRegex, err := regexp.Compile(rule.Pattern)
if err != nil {
return nil, err
}
platforms := make([]*v1.Platform, 0, len(rule.Platforms))
for _, platform := range rule.Platforms {
parsedPlatform, err := v1.ParsePlatform(platform)
if err != nil {
return nil, fmt.Errorf("failed to parse platform %s: %w", platform, err)
}
platforms = append(platforms, parsedPlatform)
}
rules = append(rules, &PolicyRule{
Pattern: r,
Pattern: patternRegex,
PolicyID: rule.PolicyID,
Replacement: rule.Replacement,
Platforms: platforms,
})
}

View File

@@ -1,4 +1,4 @@
package config
package mapping
import (
"testing"

80
mapping/match.go Normal file
View File

@@ -0,0 +1,80 @@
package mapping
import (
"fmt"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type matchType string
const (
MatchTypePolicy matchType = "policy"
MatchTypeMatchNoPolicy matchType = "match_no_policy"
MatchTypeNoMatch matchType = "no_match"
)
type PolicyMatch struct {
MatchType matchType
Policy *PolicyMapping
Rule *PolicyRule
MatchedName string
}
func (mappings *PolicyMappings) FindPolicyMatch(imageName string, platform *v1.Platform) (*PolicyMatch, error) {
if mappings == nil {
return &PolicyMatch{MatchType: MatchTypeNoMatch, MatchedName: imageName}, nil
}
return mappings.findPolicyMatchImpl(imageName, platform, make(map[*PolicyRule]bool))
}
func (mappings *PolicyMappings) findPolicyMatchImpl(imageName string, platform *v1.Platform, matched map[*PolicyRule]bool) (*PolicyMatch, error) {
for _, rule := range mappings.Rules {
if !rule.matchesPlatform(platform) {
continue
}
if rule.Pattern.MatchString(imageName) {
switch {
case rule.PolicyID == "" && rule.Replacement == "":
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
case rule.PolicyID != "" && rule.Replacement != "":
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
case rule.PolicyID != "":
policy := mappings.Policies[rule.PolicyID]
if policy != nil {
return &PolicyMatch{
MatchType: MatchTypePolicy,
Policy: policy,
Rule: rule,
MatchedName: imageName,
}, nil
}
return &PolicyMatch{
MatchType: MatchTypeMatchNoPolicy,
Rule: rule,
MatchedName: imageName,
}, nil
case rule.Replacement != "":
if matched[rule] {
return nil, fmt.Errorf("rewrite loop detected")
}
matched[rule] = true
imageName = rule.Pattern.ReplaceAllString(imageName, rule.Replacement)
return mappings.findPolicyMatchImpl(imageName, platform, matched)
}
}
}
return &PolicyMatch{MatchType: MatchTypeNoMatch}, nil
}
func (rule *PolicyRule) matchesPlatform(platform *v1.Platform) bool {
if len(rule.Platforms) == 0 {
return true
}
for i := range rule.Platforms {
if rule.Platforms[i].Equals(*platform) {
return true
}
}
return false
}

200
mapping/match_test.go Normal file
View File

@@ -0,0 +1,200 @@
package mapping
import (
"path/filepath"
"testing"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindPolicyMatch(t *testing.T) {
defaultPlatform, err := v1.ParsePlatform("linux/amd64")
require.NoError(t, err)
testCases := []struct {
name string
imageName string
mappingDir string
expectError bool
expectLoadingError bool
expectedMatchType matchType
expectedPolicyID string
expectedImageName string
platform string
}{
{
name: "alpine",
mappingDir: "doi",
imageName: "docker.io/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "no match",
mappingDir: "doi",
imageName: "docker.io/something/else",
expectedMatchType: MatchTypeNoMatch,
},
{
name: "match, no policy",
mappingDir: "local",
imageName: "docker.io/library/alpine",
expectedMatchType: MatchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "simple rewrite",
mappingDir: "simple-rewrite",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite no match",
mappingDir: "rewrite-to-no-match",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypeNoMatch,
},
{
name: "rewrite to match, no policy",
mappingDir: "rewrite-to-local",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: MatchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "multiple rewrites",
mappingDir: "rewrite-multiple",
imageName: "myevencoolermirror.org/library/alpine",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite loop",
mappingDir: "rewrite-loop",
imageName: "yin/alpine",
expectError: true,
},
{
name: "alpine with platform",
mappingDir: "doi",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with no matching platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/arm64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
{
name: "alpine with platform",
mappingDir: "doi-platform",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "alpine with invalid platform in mapping",
mappingDir: "doi-platform-broken",
imageName: "docker.io/library/alpine",
platform: "linux/amd64",
expectLoadingError: true,
},
{
name: "firefox with > 1 platforms in policy",
mappingDir: "doi-platform",
imageName: "docker.io/mozilla/firefox",
platform: "linux/arm64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/mozilla/firefox",
},
{
name: "firefox with > 1 platforms in policy (no match)",
mappingDir: "doi-platform",
imageName: "docker.io/mozilla/firefox",
platform: "macOs/arm64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
{
name: "rewrite and platform",
mappingDir: "doi-platform",
imageName: "mycoolmirror.org/library/alpine",
platform: "linux/amd64",
expectedMatchType: MatchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite and platform mismatch",
mappingDir: "doi-platform",
imageName: "mycoolmirror.org/library/alpine",
platform: "macOs/amd64",
expectedMatchType: MatchTypeNoMatch,
expectedPolicyID: "docker-official-images",
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mappings, err := LoadLocalMappings(filepath.Join("testdata", "mappings", tc.mappingDir))
if tc.expectLoadingError {
require.Error(t, err)
return
}
require.NoError(t, err)
platform := defaultPlatform
if tc.platform != "" {
platform, err = v1.ParsePlatform(tc.platform)
require.NoError(t, err)
}
match, err := mappings.FindPolicyMatch(tc.imageName, platform)
if tc.expectError {
require.Error(t, err)
// TODO: check error matches expected error message
return
}
require.NoError(t, err)
assert.Equal(t, tc.expectedMatchType, match.MatchType)
if match.MatchType == MatchTypePolicy {
if assert.NotNil(t, match.Policy) {
assert.Equal(t, tc.expectedPolicyID, match.Policy.ID)
}
}
if match.MatchType == MatchTypeMatchNoPolicy || match.MatchType == MatchTypePolicy {
assert.Equal(t, tc.expectedImageName, match.MatchedName)
}
})
}
}

View File

@@ -0,0 +1,11 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
platforms: ["linux/amd64/broken/platform/spec/1.0:foobar"]
policy-id: docker-official-images

View File

@@ -0,0 +1,17 @@
version: v1
kind: policy-mapping
policies:
- id: docker-official-images
description: Docker Official Images
files:
- path: doi/policy.rego
rules:
- pattern: "^docker[.]io/library/(.*)$"
platforms: ["linux/amd64"]
policy-id: docker-official-images
- pattern: "^docker.io/mozilla/(.*)$"
platforms: ["linux/amd64", "linux/arm64"]
policy-id: docker-official-images
- pattern: "^mycoolmirror[.]org/library/(.*)$"
platforms: ["linux/amd64"]
rewrite: "docker.io/library/$1"

View File

@@ -1,7 +1,9 @@
package config
package mapping
import (
"regexp"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type policyMappingsFile struct {
@@ -12,9 +14,10 @@ type policyMappingsFile struct {
}
type policyRuleFile struct {
Pattern string `json:"pattern"`
PolicyID string `json:"policy-id"`
Replacement string `json:"rewrite"`
Pattern string `json:"pattern"`
Platforms []string `json:"platforms"`
PolicyID string `json:"policy-id"`
Replacement string `json:"rewrite"`
}
type PolicyMappings struct {
@@ -51,4 +54,5 @@ type PolicyRule struct {
Pattern *regexp.Regexp
PolicyID string
Replacement string
Platforms []*v1.Platform
}

View File

@@ -12,7 +12,7 @@ import (
)
func TestRegistryAuth(t *testing.T) {
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
// test cases for ecr, gcr and dockerhub
testCases := []struct {

View File

@@ -67,7 +67,7 @@ func TestRefToPurl(t *testing.T) {
// Test fix for https://github.com/docker/secure-artifacts-team-issues/issues/202
func TestImageDigestForPlatform(t *testing.T) {
idx, err := layout.ImageIndexFromPath(test.UnsignedTestImage(".."))
idx, err := layout.ImageIndexFromPath(test.UnsignedTestIndex(".."))
assert.NoError(t, err)
idxm, err := idx.IndexManifest()
@@ -86,14 +86,14 @@ func TestImageDigestForPlatform(t *testing.T) {
desc, err := oci.ImageDescriptor(mfs2, p)
assert.NoError(t, err)
digest := desc.Digest.String()
assert.Equal(t, "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620", digest)
assert.Equal(t, test.UnsignedLinuxAMD64ImageDigest, digest)
p, err = oci.ParsePlatform("linux/arm64")
assert.NoError(t, err)
desc, err = oci.ImageDescriptor(mfs2, p)
assert.NoError(t, err)
digest = desc.Digest.String()
assert.Equal(t, "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e", digest)
assert.Equal(t, test.UnsignedLinuxArm64ImageDigest, digest)
}
func TestWithoutTag(t *testing.T) {

View File

@@ -18,7 +18,7 @@ import (
func TestSavingIndex(t *testing.T) {
outputLayout := test.CreateTempDir(t, "", "mirror-test")
attIdx, err := oci.IndexFromPath(test.UnsignedTestImage(".."))
attIdx, err := oci.IndexFromPath(test.UnsignedTestIndex(".."))
require.NoError(t, err)
ctx := context.Background()
@@ -73,7 +73,7 @@ func TestSavingReferrers(t *testing.T) {
},
}
digest, err := v1.NewHash("sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620")
digest, err := v1.NewHash(test.UnsignedLinuxAMD64ImageDigest)
require.NoError(t, err)
subject := &v1.Descriptor{
MediaType: "application/vnd.oci.image.manifest.v1+json",

View File

@@ -1,65 +0,0 @@
package policy
import (
"fmt"
"github.com/docker/attest/config"
)
type matchType string
const (
matchTypePolicy matchType = "policy"
matchTypeMatchNoPolicy matchType = "match_no_policy"
matchTypeNoMatch matchType = "no_match"
)
type policyMatch struct {
matchType matchType
policy *config.PolicyMapping
rule *config.PolicyRule
matchedName string
}
func findPolicyMatch(imageName string, mappings *config.PolicyMappings) (*policyMatch, error) {
if mappings == nil {
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
}
return findPolicyMatchImpl(imageName, mappings, make(map[*config.PolicyRule]bool))
}
func findPolicyMatchImpl(imageName string, mappings *config.PolicyMappings, matched map[*config.PolicyRule]bool) (*policyMatch, error) {
for _, rule := range mappings.Rules {
if rule.Pattern.MatchString(imageName) {
switch {
case rule.PolicyID == "" && rule.Replacement == "":
return nil, fmt.Errorf("rule %s has neither policy-id nor rewrite", rule.Pattern)
case rule.PolicyID != "" && rule.Replacement != "":
return nil, fmt.Errorf("rule %s has both policy-id and rewrite", rule.Pattern)
case rule.PolicyID != "":
policy := mappings.Policies[rule.PolicyID]
if policy != nil {
return &policyMatch{
matchType: matchTypePolicy,
policy: policy,
rule: rule,
matchedName: imageName,
}, nil
}
return &policyMatch{
matchType: matchTypeMatchNoPolicy,
rule: rule,
matchedName: imageName,
}, nil
case rule.Replacement != "":
if matched[rule] {
return nil, fmt.Errorf("rewrite loop detected")
}
matched[rule] = true
imageName = rule.Pattern.ReplaceAllString(imageName, rule.Replacement)
return findPolicyMatchImpl(imageName, mappings, matched)
}
}
}
return &policyMatch{matchType: matchTypeNoMatch, matchedName: imageName}, nil
}

View File

@@ -1,112 +0,0 @@
package policy
import (
"path/filepath"
"testing"
"github.com/docker/attest/config"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestFindPolicyMatch(t *testing.T) {
testCases := []struct {
name string
imageName string
mappingDir string
expectError bool
expectLoadingError bool
expectedMatchType matchType
expectedPolicyID string
expectedImageName string
}{
{
name: "alpine",
mappingDir: "doi",
imageName: "docker.io/library/alpine",
expectedMatchType: matchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "no match",
mappingDir: "doi",
imageName: "docker.io/something/else",
expectedMatchType: matchTypeNoMatch,
expectedImageName: "docker.io/something/else",
},
{
name: "match, no policy",
mappingDir: "local",
imageName: "docker.io/library/alpine",
expectedMatchType: matchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "simple rewrite",
mappingDir: "simple-rewrite",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: matchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite no match",
mappingDir: "rewrite-to-no-match",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: matchTypeNoMatch,
expectedImageName: "badredirect.org/alpine",
},
{
name: "rewrite to match, no policy",
mappingDir: "rewrite-to-local",
imageName: "mycoolmirror.org/library/alpine",
expectedMatchType: matchTypeMatchNoPolicy,
expectedImageName: "docker.io/library/alpine",
},
{
name: "multiple rewrites",
mappingDir: "rewrite-multiple",
imageName: "myevencoolermirror.org/library/alpine",
expectedMatchType: matchTypePolicy,
expectedPolicyID: "docker-official-images",
expectedImageName: "docker.io/library/alpine",
},
{
name: "rewrite loop",
mappingDir: "rewrite-loop",
imageName: "yin/alpine",
expectError: true,
},
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
mappings, err := config.LoadLocalMappings(filepath.Join("testdata", "mappings", tc.mappingDir))
require.NoError(t, err)
match, err := findPolicyMatch(tc.imageName, mappings)
if tc.expectError {
require.Error(t, err)
// TODO: check error matches expected error message
return
}
require.NoError(t, err)
assert.Equal(t, tc.expectedMatchType, match.matchType)
if match.matchType == matchTypePolicy {
if assert.NotNil(t, match.policy) {
assert.Equal(t, tc.expectedPolicyID, match.policy.ID)
}
}
assert.Equal(t, tc.expectedImageName, match.matchedName)
})
}
}

View File

@@ -6,7 +6,7 @@ import (
"github.com/distribution/reference"
"github.com/docker/attest/attestation"
"github.com/docker/attest/config"
"github.com/docker/attest/mapping"
"github.com/docker/attest/oci"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/package-url/packageurl-go"
@@ -22,9 +22,9 @@ func CreateImageDetailsResolver(imageSource *oci.ImageSpec) (oci.ImageDetailsRes
return nil, fmt.Errorf("unsupported image source type: %s", imageSource.Type)
}
func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *config.PolicyMapping) (attestation.Resolver, error) {
if mapping.Attestations != nil {
if mapping.Attestations.Style == config.AttestationStyleAttached {
func CreateAttestationResolver(resolver oci.ImageDetailsResolver, policyMapping *mapping.PolicyMapping) (attestation.Resolver, error) {
if policyMapping.Attestations != nil {
if policyMapping.Attestations.Style == mapping.AttestationStyleAttached {
switch resolver := resolver.(type) {
case *oci.RegistryImageDetailsResolver:
return attestation.NewRegistryResolver(resolver)
@@ -34,8 +34,8 @@ func CreateAttestationResolver(resolver oci.ImageDetailsResolver, mapping *confi
return nil, fmt.Errorf("unsupported image details resolver type: %T", resolver)
}
}
if mapping.Attestations.Repo != "" {
return attestation.NewReferrersResolver(resolver, attestation.WithReferrersRepo(mapping.Attestations.Repo))
if policyMapping.Attestations.Repo != "" {
return attestation.NewReferrersResolver(resolver, attestation.WithReferrersRepo(policyMapping.Attestations.Repo))
}
}
return attestation.NewReferrersResolver(resolver)

View File

@@ -5,11 +5,12 @@ import (
"fmt"
"os"
"path/filepath"
"strings"
"testing"
"github.com/docker/attest/attestation"
"github.com/docker/attest/config"
"github.com/docker/attest/internal/test"
"github.com/docker/attest/mapping"
"github.com/docker/attest/oci"
"github.com/docker/attest/policy"
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -19,13 +20,13 @@ import (
"github.com/stretchr/testify/require"
)
func loadAttestation(t *testing.T, path string) *attestation.Envelope {
func loadAttestation(t *testing.T, path string) *attestation.EnvelopeReference {
ex, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
env := new(attestation.Envelope)
env := new(attestation.EnvelopeReference)
err = json.Unmarshal(ex, env)
if err != nil {
t.Fatal(err)
@@ -39,12 +40,14 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
TestDataPath := filepath.Join("..", "test", "testdata")
ExampleAttestation := filepath.Join(TestDataPath, "example_attestation.json")
re := policy.NewRegoEvaluator(true)
verifier, err := attestation.NewVerfier()
require.NoError(t, err)
re := policy.NewRegoEvaluator(true, verifier)
defaultResolver := attestation.MockResolver{
Envs: []*attestation.Envelope{loadAttestation(t, ExampleAttestation)},
Envs: []*attestation.EnvelopeReference{loadAttestation(t, ExampleAttestation)},
}
defaultPlatform, err := v1.ParsePlatform("linux/amd64")
require.NoError(t, err)
testCases := []struct {
policyPath string
expectSuccess bool
@@ -86,7 +89,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
imageName, err := tc.resolver.ImageName(ctx)
require.NoError(t, err)
resolver := policy.NewResolver(nil, tc.opts)
policy, err := resolver.ResolvePolicy(ctx, imageName)
policy, err := resolver.ResolvePolicy(ctx, imageName, defaultPlatform)
if tc.resolveErrorStr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tc.resolveErrorStr)
@@ -107,7 +110,7 @@ func TestRegoEvaluator_Evaluate(t *testing.T) {
}
func TestLoadingMappings(t *testing.T) {
policyMappings, err := config.LoadLocalMappings(filepath.Join("testdata", "policies", "allow"))
policyMappings, err := mapping.LoadLocalMappings(filepath.Join("testdata", "policies", "allow"))
require.NoError(t, err)
assert.Equal(t, len(policyMappings.Rules), 3)
for _, mirror := range policyMappings.Rules {
@@ -119,37 +122,37 @@ func TestLoadingMappings(t *testing.T) {
func TestCreateAttestationResolver(t *testing.T) {
mockResolver := attestation.MockResolver{
Envs: []*attestation.Envelope{},
Envs: []*attestation.EnvelopeReference{},
}
layoutResolver := &attestation.LayoutResolver{}
registryResolver := &oci.RegistryImageDetailsResolver{}
nilRepoReferrers := &config.PolicyMapping{
Attestations: &config.AttestationConfig{
Style: config.AttestationStyleReferrers,
nilRepoReferrers := &mapping.PolicyMapping{
Attestations: &mapping.AttestationConfig{
Style: mapping.AttestationStyleReferrers,
},
}
referrers := &config.PolicyMapping{
Attestations: &config.AttestationConfig{
referrers := &mapping.PolicyMapping{
Attestations: &mapping.AttestationConfig{
Repo: "localhost:5000/repo",
Style: config.AttestationStyleReferrers,
Style: mapping.AttestationStyleReferrers,
},
}
attached := &config.PolicyMapping{
Attestations: &config.AttestationConfig{
Style: config.AttestationStyleAttached,
attached := &mapping.PolicyMapping{
Attestations: &mapping.AttestationConfig{
Style: mapping.AttestationStyleAttached,
},
}
testCases := []struct {
name string
resolver oci.ImageDetailsResolver
mapping *config.PolicyMapping
mapping *mapping.PolicyMapping
errorStr string
}{
{name: "referrers", resolver: layoutResolver, mapping: referrers},
{name: "referrers (no mapped repo)", resolver: layoutResolver, mapping: nilRepoReferrers},
{name: "referrers (no mapping)", resolver: layoutResolver, mapping: &config.PolicyMapping{Attestations: nil}},
{name: "referrers (no mapping)", resolver: layoutResolver, mapping: &mapping.PolicyMapping{Attestations: nil}},
{name: "attached (registry)", resolver: registryResolver, mapping: attached},
{name: "attached (layout)", resolver: layoutResolver, mapping: attached},
{name: "attached (unsupported)", resolver: mockResolver, mapping: attached, errorStr: "unsupported image details resolver type"},
@@ -168,11 +171,11 @@ func TestCreateAttestationResolver(t *testing.T) {
}
switch resolver.(type) {
case *attestation.ReferrersResolver:
assert.Equal(t, tc.mapping.Attestations.Style, config.AttestationStyleReferrers)
assert.Equal(t, tc.mapping.Attestations.Style, mapping.AttestationStyleReferrers)
case *attestation.RegistryResolver:
assert.Equal(t, tc.mapping.Attestations.Style, config.AttestationStyleAttached)
assert.Equal(t, tc.mapping.Attestations.Style, mapping.AttestationStyleAttached)
case *attestation.LayoutResolver:
assert.Equal(t, tc.mapping.Attestations.Style, config.AttestationStyleAttached)
assert.Equal(t, tc.mapping.Attestations.Style, mapping.AttestationStyleAttached)
}
})
}
@@ -355,6 +358,7 @@ func TestVerifySubject(t *testing.T) {
},
}
digestHex := strings.TrimPrefix(test.UnsignedLinuxAMD64ImageDigest, "sha256:")
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
defaultResolver.Image = tc.img
@@ -363,7 +367,7 @@ func TestVerifySubject(t *testing.T) {
return &v1.Platform{Architecture: "amd64", OS: "linux"}, nil
}
// digest from mock resolver
tc.subject[0].Digest = map[string]string{"sha256": "da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}
tc.subject[0].Digest = map[string]string{"sha256": digestHex}
if tc.digest != "" {
tc.subject[0].Digest = map[string]string{"sha256": tc.digest}
}
@@ -379,7 +383,7 @@ func TestVerifySubject(t *testing.T) {
subject := []intoto.Subject{
{
Name: "pkg:docker/alpine@latest?platform=linux%2Famd64",
Digest: map[string]string{"sha256": "da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"},
Digest: map[string]string{"sha256": digestHex},
},
}

View File

@@ -1,12 +1,14 @@
package policy
import (
"bytes"
"context"
"encoding/json"
"fmt"
"os"
"path/filepath"
"github.com/docker-library/bashbrew/manifest"
"github.com/docker/attest/attestation"
intoto "github.com/in-toto/in-toto-golang/in_toto"
"github.com/open-policy-agent/opa/ast"
@@ -21,7 +23,8 @@ import (
)
type regoEvaluator struct {
debug bool
debug bool
attestationVerifier attestation.Verifier
}
const (
@@ -29,9 +32,10 @@ const (
resultBinding = "result"
)
func NewRegoEvaluator(debug bool) Evaluator {
func NewRegoEvaluator(debug bool, attestationVerifier attestation.Verifier) Evaluator {
return &regoEvaluator{
debug: debug,
debug: debug,
attestationVerifier: attestationVerifier,
}
}
@@ -86,7 +90,8 @@ func (re *regoEvaluator) Evaluate(ctx context.Context, resolver attestation.Reso
rego.Store(store),
rego.GenerateJSON(jsonGenerator[Result]()),
)
for _, custom := range RegoFunctions(resolver) {
regoFnOpts := NewRegoFunctionOptions(resolver, re.attestationVerifier)
for _, custom := range RegoFunctions(regoFnOpts) {
regoOpts = append(regoOpts, custom.Func)
}
@@ -146,6 +151,12 @@ var attestDecl = &ast.Builtin{
Nondeterministic: true,
}
var internalParseLibraryDefinitionDecl = &ast.Builtin{
Name: "attest.internals.parse_library_definition",
Decl: types.NewFunction(types.Args(types.S), dynamicObj),
Nondeterministic: false,
}
func wrapFunctionResult(value *ast.Term, err error) (*ast.Term, error) {
var terms [][2]*ast.Term
if err != nil {
@@ -169,105 +180,146 @@ func handleErrors2(f func(rCtx rego.BuiltinContext, a, b *ast.Term) (*ast.Term,
}
}
func RegoFunctions(resolver attestation.Resolver) []*tester.Builtin {
func RegoFunctions(regoOpts *RegoFnOpts) []*tester.Builtin {
return []*tester.Builtin{
{
Decl: verifyDecl,
Func: rego.Function2(
&rego.Function{
Name: verifyDecl.Name,
Decl: verifyDecl.Decl,
Memoize: true,
Nondeterministic: verifyDecl.Nondeterministic,
},
handleErrors2(verifyInTotoEnvelope(resolver))),
},
{
Decl: attestDecl,
Func: rego.Function1(
&rego.Function{
Name: attestDecl.Name,
Decl: attestDecl.Decl,
Memoize: true,
Nondeterministic: attestDecl.Nondeterministic,
},
handleErrors1(fetchInTotoAttestations(resolver))),
},
builtin2(verifyDecl, regoOpts.verifyInTotoEnvelope),
builtin1(attestDecl, regoOpts.fetchInTotoAttestations),
builtin1(internalParseLibraryDefinitionDecl, regoOpts.internalParseLibraryDefinition),
}
}
func fetchInTotoAttestations(resolver attestation.Resolver) rego.Builtin1 {
return func(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
return nil, fmt.Errorf("predicateTypeTerm is not a string")
}
predicateType := string(predicateTypeStr)
envelopes, err := resolver.Attestations(rCtx.Context, predicateType)
if err != nil {
return nil, err
}
// Convert each envelope to an ast.Value.
values := make([]*ast.Term, len(envelopes))
for i, envelope := range envelopes {
value, err := ast.InterfaceToValue(envelope)
if err != nil {
return nil, err
}
values[i] = ast.NewTerm(value)
}
// Wrap the values in an ast.Set and convert it to an ast.Term.
set := ast.NewTerm(ast.NewSet(values...))
return set, nil
func builtin1(decl *ast.Builtin, f rego.Builtin1) *tester.Builtin {
return &tester.Builtin{
Decl: decl,
Func: rego.Function1(
&rego.Function{
Name: decl.Name,
Decl: decl.Decl,
Memoize: true,
Nondeterministic: decl.Nondeterministic,
},
handleErrors1(f)),
}
}
func verifyInTotoEnvelope(resolver attestation.Resolver) rego.Builtin2 {
return func(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
env := new(attestation.Envelope)
opts := new(attestation.VerifyOptions)
err := ast.As(envTerm.Value, env)
if err != nil {
return nil, fmt.Errorf("failed to cast envelope: %w", err)
}
err = ast.As(optsTerm.Value, &opts)
if err != nil {
return nil, fmt.Errorf("failed to cast verifier options: %w", err)
}
func builtin2(decl *ast.Builtin, f rego.Builtin2) *tester.Builtin {
return &tester.Builtin{
Decl: decl,
Func: rego.Function2(
&rego.Function{
Name: decl.Name,
Decl: decl.Decl,
Memoize: true,
Nondeterministic: decl.Nondeterministic,
},
handleErrors2(f)),
}
}
payload, err := attestation.VerifyDSSE(rCtx.Context, env, opts)
if err != nil {
return nil, fmt.Errorf("failed to verify envelope: %w", err)
}
type RegoFnOpts struct {
attestationResolver attestation.Resolver
attestationVerifier attestation.Verifier
}
statement := new(intoto.Statement)
// this is exported for testing here and in clients of the library.
func NewRegoFunctionOptions(resolver attestation.Resolver, verifier attestation.Verifier) *RegoFnOpts {
return &RegoFnOpts{
attestationResolver: resolver,
attestationVerifier: verifier,
}
}
switch env.PayloadType {
case intoto.PayloadType:
err = json.Unmarshal(payload, statement)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
// TODO: implement other types of envelope
default:
return nil, fmt.Errorf("unsupported payload type: %s", env.PayloadType)
}
// because we don't control the signature here (blame rego)
// nolint:gocritic
func (regoOpts *RegoFnOpts) fetchInTotoAttestations(rCtx rego.BuiltinContext, predicateTypeTerm *ast.Term) (*ast.Term, error) {
predicateTypeStr, ok := predicateTypeTerm.Value.(ast.String)
if !ok {
return nil, fmt.Errorf("predicateTypeTerm is not a string")
}
predicateType := string(predicateTypeStr)
err = VerifySubject(rCtx.Context, statement.Subject, resolver)
if err != nil {
return nil, fmt.Errorf("failed to verify subject: %w", err)
}
envelopes, err := regoOpts.attestationResolver.Attestations(rCtx.Context, predicateType)
if err != nil {
return nil, err
}
value, err := ast.InterfaceToValue(statement)
// Convert each envelope to an ast.Value.
values := make([]*ast.Term, len(envelopes))
for i, envelope := range envelopes {
value, err := ast.InterfaceToValue(envelope)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
values[i] = ast.NewTerm(value)
}
// Wrap the values in an ast.Set and convert it to an ast.Term.
set := ast.NewTerm(ast.NewSet(values...))
return set, nil
}
// because we don't control the signature here (blame rego)
// nolint:gocritic
func (regoOpts *RegoFnOpts) verifyInTotoEnvelope(rCtx rego.BuiltinContext, envTerm, optsTerm *ast.Term) (*ast.Term, error) {
env := new(attestation.Envelope)
opts := new(attestation.VerifyOptions)
err := ast.As(envTerm.Value, env)
if err != nil {
return nil, fmt.Errorf("failed to cast envelope: %w", err)
}
err = ast.As(optsTerm.Value, &opts)
if err != nil {
return nil, fmt.Errorf("failed to cast verifier options: %w", err)
}
payload, err := attestation.VerifyDSSE(rCtx.Context, regoOpts.attestationVerifier, env, opts)
if err != nil {
return nil, fmt.Errorf("failed to verify envelope: %w", err)
}
statement := new(intoto.Statement)
switch env.PayloadType {
case intoto.PayloadType:
err = json.Unmarshal(payload, statement)
if err != nil {
return nil, fmt.Errorf("failed to unmarshal statement: %w", err)
}
// TODO: implement other types of envelope
default:
return nil, fmt.Errorf("unsupported payload type: %s", env.PayloadType)
}
err = VerifySubject(rCtx.Context, statement.Subject, regoOpts.attestationResolver)
if err != nil {
return nil, fmt.Errorf("failed to verify subject: %w", err)
}
value, err := ast.InterfaceToValue(statement)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
}
// because we don't control the signature here (blame rego)
// nolint:gocritic
func (regoOpts *RegoFnOpts) internalParseLibraryDefinition(_ rego.BuiltinContext, definitionTerm *ast.Term) (*ast.Term, error) {
definitionStr, ok := definitionTerm.Value.(ast.String)
if !ok {
return nil, fmt.Errorf("predicateTypeTerm is not a string")
}
definition := string(definitionStr)
defBuffer := bytes.NewBufferString(definition)
parsed, err := manifest.Parse2822(defBuffer)
if err != nil {
return nil, err
}
value, err := ast.InterfaceToValue(parsed)
if err != nil {
return nil, err
}
return ast.NewTerm(value), nil
}
func loadYAML(path string, bs []byte) (interface{}, error) {

89
policy/rego_test.go Normal file
View File

@@ -0,0 +1,89 @@
package policy
import (
"context"
"testing"
"github.com/docker/attest/attestation"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/open-policy-agent/opa/tester"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPolicy(t *testing.T) {
paths := []string{"testdata/policies/test/fetch"}
modules, store, err := tester.Load(paths, nil)
require.NoError(t, err)
resolver := &NullAttestationResolver{}
opts := NewRegoFunctionOptions(resolver, nil)
ctx := context.Background()
ch, err := tester.NewRunner().
SetStore(store).
AddCustomBuiltins(RegoFunctions(opts)).
CapturePrintOutput(true).
RaiseBuiltinErrors(true).
EnableTracing(true).
SetModules(modules).
RunTests(ctx, nil)
require.NoError(t, err)
require.NoError(t, err)
results := buffer(ch)
assert.Equalf(t, 1, len(results), "expected 1 results, got %d", len(results))
assert.Truef(t, results[0].Pass(), "expected result 1 to pass, got %v", results[0])
assert.True(t, resolver.called)
}
func TestPolicyDefParse(t *testing.T) {
paths := []string{"testdata/policies/test/def_parse"}
modules, store, err := tester.Load(paths, nil)
require.NoError(t, err)
resolver := &NullAttestationResolver{}
opts := NewRegoFunctionOptions(resolver, nil)
ctx := context.Background()
ch, err := tester.NewRunner().
SetStore(store).
AddCustomBuiltins(RegoFunctions(opts)).
CapturePrintOutput(true).
RaiseBuiltinErrors(true).
EnableTracing(true).
SetModules(modules).
RunTests(ctx, nil)
require.NoError(t, err)
require.NoError(t, err)
results := buffer(ch)
t.Log(string(results[0].Output))
assert.Equalf(t, 1, len(results), "expected 1 results, got %d", len(results))
assert.Truef(t, results[0].Pass(), "expected result 1 to pass, got %v", results[0].Location)
}
func buffer[T any](ch chan T) []T {
var out []T
for v := range ch {
out = append(out, v)
}
return out
}
type NullAttestationResolver struct {
called bool
}
func (r *NullAttestationResolver) ImageName(_ context.Context) (string, error) {
return "", nil
}
func (r *NullAttestationResolver) ImagePlatform(_ context.Context) (*v1.Platform, error) {
return v1.ParsePlatform("")
}
func (r *NullAttestationResolver) ImageDescriptor(_ context.Context) (*v1.Descriptor, error) {
return nil, nil
}
func (r *NullAttestationResolver) Attestations(_ context.Context, _ string) ([]*attestation.EnvelopeReference, error) {
r.called = true
return nil, nil
}

View File

@@ -8,9 +8,10 @@ import (
"path/filepath"
"github.com/distribution/reference"
"github.com/docker/attest/config"
"github.com/docker/attest/internal/util"
"github.com/docker/attest/mapping"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
)
type Resolver struct {
@@ -25,7 +26,7 @@ func NewResolver(tufClient tuf.Downloader, opts *Options) *Resolver {
}
}
func (r *Resolver) ResolvePolicy(_ context.Context, imageName string) (*Policy, error) {
func (r *Resolver) ResolvePolicy(_ context.Context, imageName string, platform *v1.Platform) (*Policy, error) {
p, err := r.resolvePolicyByID()
if err != nil {
return nil, fmt.Errorf("failed to resolve policy by id: %w", err)
@@ -37,45 +38,45 @@ func (r *Resolver) ResolvePolicy(_ context.Context, imageName string) (*Policy,
if err != nil {
return nil, fmt.Errorf("failed to parse image name: %w", err)
}
localMappings, err := config.LoadLocalMappings(r.opts.LocalPolicyDir)
localMappings, err := mapping.LoadLocalMappings(r.opts.LocalPolicyDir)
if err != nil {
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
}
match, err := findPolicyMatch(imageName, localMappings)
match, err := localMappings.FindPolicyMatch(imageName, platform)
if err != nil {
return nil, err
}
if match.matchType == matchTypePolicy {
return r.resolveLocalPolicy(match.policy, imageName, match.matchedName)
if match.MatchType == mapping.MatchTypePolicy {
return r.resolveLocalPolicy(match.Policy, imageName, match.MatchedName)
}
if !r.opts.DisableTUF {
tufMappings, err := config.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
tufMappings, err := mapping.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings as fallback: %w", err)
}
// it's a mirror of a tuf policy
if match.matchType == matchTypeMatchNoPolicy {
if match.MatchType == mapping.MatchTypeMatchNoPolicy {
for _, mapping := range tufMappings.Policies {
if mapping.ID == match.rule.PolicyID {
return r.resolveTUFPolicy(mapping, imageName, match.matchedName)
if mapping.ID == match.Rule.PolicyID {
return r.resolveTUFPolicy(mapping, imageName, match.MatchedName)
}
}
}
// try to resolve a tuf policy directly
match, err = findPolicyMatch(imageName, tufMappings)
match, err = tufMappings.FindPolicyMatch(imageName, platform)
if err != nil {
return nil, err
}
if match.matchType == matchTypePolicy {
return r.resolveTUFPolicy(match.policy, imageName, match.matchedName)
if match.MatchType == mapping.MatchTypePolicy {
return r.resolveTUFPolicy(match.Policy, imageName, match.MatchedName)
}
}
return nil, nil
}
func (r *Resolver) resolveLocalPolicy(mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
func (r *Resolver) resolveLocalPolicy(mapping *mapping.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
if r.opts.LocalPolicyDir == "" {
return nil, fmt.Errorf("local policy dir not set")
}
@@ -118,7 +119,7 @@ func (r *Resolver) resolveLocalPolicy(mapping *config.PolicyMapping, imageName s
return policy, nil
}
func (r *Resolver) resolveTUFPolicy(mapping *config.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
func (r *Resolver) resolveTUFPolicy(mapping *mapping.PolicyMapping, imageName string, matchedName string) (*Policy, error) {
var URI string
var digest map[string]string
files := make([]*File, 0, len(mapping.Files))
@@ -159,7 +160,7 @@ func (r *Resolver) resolveTUFPolicy(mapping *config.PolicyMapping, imageName str
func (r *Resolver) resolvePolicyByID() (*Policy, error) {
if r.opts.PolicyID != "" {
localMappings, err := config.LoadLocalMappings(r.opts.LocalPolicyDir)
localMappings, err := mapping.LoadLocalMappings(r.opts.LocalPolicyDir)
if err != nil {
return nil, fmt.Errorf("failed to load local policy mappings: %w", err)
}
@@ -171,7 +172,7 @@ func (r *Resolver) resolvePolicyByID() (*Policy, error) {
}
if !r.opts.DisableTUF {
tufMappings, err := config.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
tufMappings, err := mapping.LoadTUFMappings(r.tufClient, r.opts.LocalTargetsDir)
if err != nil {
return nil, fmt.Errorf("failed to load tuf policy mappings by id: %w", err)
}

View File

@@ -7,6 +7,7 @@ import (
"github.com/docker/attest/internal/test"
"github.com/docker/attest/policy"
"github.com/docker/attest/tuf"
v1 "github.com/google/go-containerregistry/pkg/v1"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@@ -17,7 +18,8 @@ func TestResolvePolicy(t *testing.T) {
noLocalPolicyPath := "testdata/policies/no-policy"
testPolicyID := "docker-official-images"
testImageName := "localhost:5001/test/repo:tag"
defaultPlatform, err := v1.ParsePlatform("linux/amd64")
require.NoError(t, err)
testCases := []struct {
name string
policyPath string
@@ -52,7 +54,7 @@ func TestResolvePolicy(t *testing.T) {
opts.DisableTUF = tc.DisableTUF
opts.LocalTargetsDir = tempDir
resolver := policy.NewResolver(tufClient, opts)
policy, err := resolver.ResolvePolicy(context.Background(), testImageName)
policy, err := resolver.ResolvePolicy(context.Background(), testImageName, defaultPlatform)
require.NoError(t, err)
assert.NotNil(t, policy)
if tc.DisableTUF || tc.localOverridesTUF {

View File

@@ -0,0 +1,18 @@
package def_parse_test
import rego.v1
test_parse_library_definition if {
def := `Maintainers: me <me@example.com> (@me)
GitRepo: blah
Tags: 1, 2, 3
GitCommit: fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688
File: Dockerfile.foo`
result := attest.internals.parse_library_definition(def)
definition := result.value
definition.Entries[0].GitRepo == "blah"
definition.Entries[0].GitCommit == "fa105cb3c26c8f0e87d7dbb1bf5293691ac2f688"
definition.Entries[0].Tags == ["1", "2", "3"]
definition.Entries[0].File == "Dockerfile.foo"
}

View File

@@ -0,0 +1,7 @@
package attest
import rego.v1
success if {
some env in attest.fetch("foo")
}

View File

@@ -0,0 +1,9 @@
package attest_test
import rego.v1
import data.attest
test_sucess if {
attest.success
}

View File

@@ -1,16 +1,18 @@
package policy
import (
"github.com/docker/attest/config"
"github.com/docker/attest/attestation"
"github.com/docker/attest/mapping"
"github.com/docker/attest/tuf"
intoto "github.com/in-toto/in-toto-golang/in_toto"
)
type Summary struct {
Subjects []intoto.Subject `json:"subjects"`
SLSALevels []string `json:"slsa_levels"`
Verifier string `json:"verifier"`
PolicyURI string `json:"policy_uri"`
Subjects []intoto.Subject `json:"subjects"`
Inputs []attestation.ResourceDescriptor `json:"input_attestations"`
SLSALevels []string `json:"slsa_levels"`
Verifier string `json:"verifier"`
PolicyURI string `json:"policy_uri"`
}
type Violation struct {
@@ -27,33 +29,39 @@ type Result struct {
}
type Options struct {
TUFClientOptions *tuf.ClientOptions
DisableTUF bool
LocalTargetsDir string
LocalPolicyDir string
PolicyID string
ReferrersRepo string
AttestationStyle config.AttestationStyle
Debug bool
TUFClientOptions *tuf.ClientOptions
DisableTUF bool
LocalTargetsDir string
LocalPolicyDir string
PolicyID string
ReferrersRepo string
AttestationStyle mapping.AttestationStyle
Debug bool
AttestationVerifier attestation.Verifier
// extra parameters to pass through to rego as policy inputs
Parameters Parameters
}
type Parameters map[string]string
type Policy struct {
InputFiles []*File
Query string
Mapping *config.PolicyMapping
Mapping *mapping.PolicyMapping
ResolvedName string
URI string
Digest map[string]string
}
type Input struct {
Digest string `json:"digest"`
PURL string `json:"purl"`
Tag string `json:"tag,omitempty"`
Domain string `json:"domain"`
NormalizedName string `json:"normalized_name"`
FamiliarName string `json:"familiar_name"`
Platform string `json:"platform"`
Digest string `json:"digest"`
PURL string `json:"purl"`
Tag string `json:"tag,omitempty"`
Domain string `json:"domain"`
NormalizedName string `json:"normalized_name"`
FamiliarName string `json:"familiar_name"`
Platform string `json:"platform"`
Parameters Parameters `json:"parameters"`
}
type File struct {

View File

@@ -10,36 +10,45 @@ function check_command () {
function cleanup_testdata () {
echo "Cleaning up existing testdata..."
rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_INDEX_DIR:?}"
rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_INDEX_DIR:?}"
rm -rf "${TESTDATA_PATH:?}/${UNSIGNED_IMAGE_DIR:?}"
rm -rf "${TESTDATA_PATH:?}/${NO_PROVENANCE_IMAGE_DIR:?}"
}
function build_unsigned_image () {
function build_unsigned_index () {
echo "Building $UNSIGNED_INDEX_DIR..."
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance true --platform linux/amd64,linux/arm64 \
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_INDEX_DIR"
}
function build_no_provenance_index () {
echo "Building unsigned $NO_PROVENANCE_INDEX_DIR..."
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom true --provenance false --platform linux/amd64,linux/arm64 \
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$NO_PROVENANCE_INDEX_DIR"
}
function build_image () {
echo "Building $UNSIGNED_IMAGE_DIR..."
docker buildx build "$TEST_IMAGE_DOCKERFILE_PATH" --sbom true --provenance true --platform linux/amd64,linux/arm64 \
--output type=oci,tar=false,name="$TEST_IMAGE_REPO:$TEST_IMAGE_TAG",dest="$TESTDATA_PATH/$UNSIGNED_IMAGE_DIR"
}
function build_no_provenance_image () {
echo "Building unsigned $NO_PROVENANCE_IMAGE_DIR..."
docker buildx build "$TEST_IMAGE_DOCKERFILE_PATH" --sbom true --provenance false --platform linux/amd64,linux/arm64 \
--output type=oci,tar=false,name="$TEST_IMAGE_REPO:$TEST_IMAGE_TAG",dest="$TESTDATA_PATH/$NO_PROVENANCE_IMAGE_DIR"
docker buildx build "$TEST_INDEX_DOCKERFILE_PATH" --sbom false --provenance false --platform linux/amd64 \
--output type=oci,tar=false,name="$TEST_INDEX_REPO:$TEST_INDEX_TAG",dest="$TESTDATA_PATH/$UNSIGNED_IMAGE_DIR"
}
# Check required commands
check_command docker
TESTDATA_PATH="../test/testdata"
TEST_IMAGE_DOCKERFILE_PATH="../test"
TEST_IMAGE_REPO="test-image"
TEST_IMAGE_TAG="test"
UNSIGNED_IMAGE_DIR="unsigned-test-image"
NO_PROVENANCE_IMAGE_DIR="no-provenance-image"
TEST_INDEX_DOCKERFILE_PATH="../test"
TEST_INDEX_REPO="test-image"
TEST_INDEX_TAG="test"
UNSIGNED_INDEX_DIR="unsigned-index"
NO_PROVENANCE_INDEX_DIR="no-provenance-index"
UNSIGNED_IMAGE_DIR="unsigned-image"
ATTESTATION_PAYLOADTYPE="application/vnd.in-toto+json"
# Run steps
cleanup_testdata
build_unsigned_image
build_no_provenance_image
build_unsigned_index
build_no_provenance_index
build_image
echo "Process completed successfully."

View File

@@ -19,9 +19,12 @@ func SignStatements(ctx context.Context, idx v1.ImageIndex, signer dsse.SignerVe
// sign every attestation layer in each manifest
for _, manifest := range attestationManifests {
for _, layer := range manifest.OriginalLayers {
err = manifest.Add(ctx, signer, layer.Statement, opts)
if err != nil {
return nil, fmt.Errorf("failed to sign attestation layer %w", err)
// skip layers without statements
if layer.Statement != nil {
err = manifest.Add(ctx, signer, layer.Statement, opts)
if err != nil {
return nil, fmt.Errorf("failed to sign attestation layer %w", err)
}
}
}
}

View File

@@ -15,7 +15,7 @@ import (
)
var (
NoProvenanceImage = filepath.Join("test", "testdata", "no-provenance-image")
NoProvenanceImage = filepath.Join("test", "testdata", "no-provenance-index")
PassPolicyDir = filepath.Join("test", "testdata", "local-policy-pass")
PassMirrorPolicyDir = filepath.Join("test", "testdata", "local-policy-mirror")
PassNoTLPolicyDir = filepath.Join("test", "testdata", "local-policy-no-tl")
@@ -35,8 +35,8 @@ func TestSignVerifyOCILayout(t *testing.T) {
expectedAttestations int
replace bool
}{
{"signed replaced", test.UnsignedTestImage(), 0, 4, true},
{"without replace", test.UnsignedTestImage(), 4, 4, false},
{"signed replaced", test.UnsignedTestIndex(), 0, 4, true},
{"without replace", test.UnsignedTestIndex(), 4, 4, false},
// image without provenance doesn't fail
{"no provenance (replace)", NoProvenanceImage, 0, 2, true},
{"no provenance (no replace)", NoProvenanceImage, 2, 2, false},

View File

@@ -20,8 +20,5 @@ func GetAWSSigner(ctx context.Context, keyARN string, region string) (dsse.Signe
if err != nil {
return nil, fmt.Errorf("error getting aws crypto signer: %w", err)
}
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil
return NewECDSASignerVerifier(cs)
}

View File

@@ -9,52 +9,17 @@ import (
"crypto/x509"
"encoding/pem"
"fmt"
"io"
"github.com/docker/attest/internal/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ECDSA256SignerVerifier struct {
crypto.Signer
}
// implement keyid function.
func (s *ECDSA256SignerVerifier) KeyID() (string, error) {
keyid, err := KeyID(s.Signer.Public())
if err != nil {
return "", fmt.Errorf("error getting keyid: %w", err)
}
return keyid, nil
}
func (s *ECDSA256SignerVerifier) Public() crypto.PublicKey {
return s.Signer.Public()
}
func (s *ECDSA256SignerVerifier) Sign(_ context.Context, data []byte) ([]byte, error) {
return s.Signer.Sign(rand.Reader, data, crypto.SHA256)
}
func (s *ECDSA256SignerVerifier) Verify(_ context.Context, data []byte, sig []byte) error {
pub, ok := s.Signer.Public().(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("public key is not ecdsa")
}
ok = ecdsa.VerifyASN1(pub, util.SHA256(data), sig)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func LoadKeyPair(priv []byte) (dsse.SignerVerifier, error) {
privateKey, err := parsePriv(priv)
if err != nil {
return nil, err
}
return &ECDSA256SignerVerifier{
Signer: privateKey,
}, nil
return NewECDSASignerVerifier(privateKey)
}
func parsePriv(privkeyBytes []byte) (*ecdsa.PrivateKey, error) {
@@ -78,7 +43,26 @@ func GenKeyPair() (dsse.SignerVerifier, error) {
if err != nil {
return nil, err
}
return &ECDSA256SignerVerifier{
Signer: signer,
}, nil
return NewECDSASignerVerifier(signer)
}
// ensure it implements crypto.Signer.
var _ crypto.Signer = (*cryptoSignerWrapper)(nil)
type cryptoSignerWrapper struct {
sv dsse.SignerVerifier
}
// Public implements crypto.Signer.
func (c *cryptoSignerWrapper) Public() crypto.PublicKey {
return c.sv.Public()
}
// Sign implements crypto.Signer.
func (c *cryptoSignerWrapper) Sign(_ io.Reader, digest []byte, _ crypto.SignerOpts) (signature []byte, err error) {
return c.sv.Sign(context.Background(), digest)
}
func AsCryptoSigner(signer dsse.SignerVerifier) (crypto.Signer, error) {
return &cryptoSignerWrapper{sv: signer}, nil
}

80
signerverifier/ecdsa.go Normal file
View File

@@ -0,0 +1,80 @@
package signerverifier
import (
"context"
"crypto"
"crypto/ecdsa"
"crypto/rand"
"fmt"
"github.com/docker/attest/internal/util"
"github.com/secure-systems-lab/go-securesystemslib/dsse"
)
type ecdsaVerifier struct {
publicKey *ecdsa.PublicKey
keyID string
}
// ensure ECDSAVerifier implements dsse.Verifier.
var _ dsse.Verifier = (*ecdsaVerifier)(nil)
func NewECDSAVerifier(publicKey crypto.PublicKey) (dsse.Verifier, error) {
ecdsaPublicKey, ok := (publicKey).(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("public key is not an ECDSA public key")
}
return &ecdsaVerifier{
publicKey: ecdsaPublicKey,
}, nil
}
func (v *ecdsaVerifier) Verify(_ context.Context, data, signature []byte) error {
// verify payload ecdsa signature
ok := ecdsa.VerifyASN1(v.publicKey, util.SHA256(data), signature)
if !ok {
return fmt.Errorf("payload signature is not valid")
}
return nil
}
func (v *ecdsaVerifier) Public() crypto.PublicKey {
return v.publicKey
}
func (v *ecdsaVerifier) KeyID() (string, error) {
if v.keyID != "" {
return v.keyID, nil
}
keyID, err := KeyID(v.publicKey)
if err != nil {
return "", fmt.Errorf("failed to get key ID: %w", err)
}
v.keyID = keyID
return v.keyID, nil
}
// must implement dsse.SignerVerifier interface.
var _ dsse.SignerVerifier = (*ecdsa256SignerVerifier)(nil)
type ecdsa256SignerVerifier struct {
signer crypto.Signer
dsse.Verifier
}
func NewECDSASignerVerifier(signer crypto.Signer) (dsse.SignerVerifier, error) {
verifier, err := NewECDSAVerifier(signer.Public())
if err != nil {
return nil, fmt.Errorf("failed to create verifier: %w", err)
}
sv := &ecdsa256SignerVerifier{
signer: signer,
Verifier: verifier,
}
return sv, nil
}
func (s *ecdsa256SignerVerifier) Sign(_ context.Context, data []byte) ([]byte, error) {
return s.signer.Sign(rand.Reader, data, crypto.SHA256)
}

View File

@@ -21,8 +21,5 @@ func GetGCPSigner(ctx context.Context, reference string, opts ...option.ClientOp
if err != nil {
return nil, fmt.Errorf("error getting gcp crypto signer: %w", err)
}
signer := &ECDSA256SignerVerifier{
Signer: cs,
}
return signer, nil
return NewECDSASignerVerifier(cs)
}

View File

@@ -40,6 +40,14 @@ func TestGCPKMS_Signer(t *testing.T) {
publicKey, err := ParsePublicKey([]byte(publicKeyPEM))
require.NoError(t, err)
// verify payload ecdsa signature
ok := ecdsa.VerifyASN1(publicKey, hash, sig)
ecdsaPublicKey, ok := publicKey.(*ecdsa.PublicKey)
if !ok {
t.Fatal("Failed to convert publicKey to *ecdsa.PublicKey")
}
ok = ecdsa.VerifyASN1(ecdsaPublicKey, hash, sig)
assert.True(t, ok)
err = signer.Verify(ctx, msg, sig)
require.NoError(t, err)
}

View File

@@ -1,6 +1,7 @@
package signerverifier
import (
"crypto"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
@@ -9,7 +10,7 @@ import (
const pemType = "PUBLIC KEY"
func ParsePublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
func ParsePublicKey(pubkeyBytes []byte) (crypto.PublicKey, error) {
p, _ := pem.Decode(pubkeyBytes)
if p == nil {
return nil, fmt.Errorf("pubkey file does not contain any PEM data")
@@ -17,12 +18,15 @@ func ParsePublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
if p.Type != pemType {
return nil, fmt.Errorf("pubkey file does not contain a public key")
}
pubKey, err := x509.ParsePKIXPublicKey(p.Bytes)
if err != nil {
return nil, fmt.Errorf("error failed to parse public key: %w", err)
}
return x509.ParsePKIXPublicKey(p.Bytes)
}
ecdsaPubKey, ok := pubKey.(*ecdsa.PublicKey)
func ParseECDSAPublicKey(pubkeyBytes []byte) (*ecdsa.PublicKey, error) {
pk, err := ParsePublicKey(pubkeyBytes)
if err != nil {
return nil, err
}
ecdsaPubKey, ok := pk.(*ecdsa.PublicKey)
if !ok {
return nil, fmt.Errorf("error public key is not an ecdsa key: %w", err)
}
@@ -34,6 +38,5 @@ func ConvertToPEM(ecdsaPubKey *ecdsa.PublicKey) ([]byte, error) {
if err != nil {
return nil, fmt.Errorf("error failed to marshal public key: %w", err)
}
return pem.EncodeToMemory(&pem.Block{Type: pemType, Bytes: pubKeyBytes}), nil
}

View File

@@ -0,0 +1 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064"]}}

View File

@@ -0,0 +1,19 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f",
"size": 167
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064",
"size": 917,
"annotations": {
"in-toto.io/predicate-type": "https://spdx.dev/Document"
}
}
]
}

View File

@@ -0,0 +1,28 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec",
"size": 288,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5",
"size": 558,
"annotations": {
"vnd.docker.reference.digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}

View File

@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","ArgsEscaped":true},"created":null,"history":[{"created_by":"CMD []","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}}

View File

@@ -0,0 +1 @@
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test@latest?platform=linux%2Farm64","digest":{"sha256":"e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec"}}],"predicate":{"SPDXID":"SPDXRef-DOCUMENT","creationInfo":{"created":"2024-09-19T20:28:48Z","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0"],"licenseListVersion":"3.23"},"dataLicense":"CC0-1.0","documentNamespace":"https://anchore.com/syft/dir/sbom-4d662591-02b0-4448-8cdc-c8b539bbe1a0","name":"sbom","packages":[{"SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","downloadLocation":"NOASSERTION","filesAnalyzed":false,"name":"sbom","primaryPackagePurpose":"FILE","supplier":"NOASSERTION"}],"relationships":[{"relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES","spdxElementId":"SPDXRef-DOCUMENT"}],"spdxVersion":"SPDX-2.3"}}

View File

@@ -0,0 +1,10 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46",
"size": 308
},
"layers": null
}

View File

@@ -0,0 +1,15 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1",
"size": 855,
"annotations": {
"io.containerd.image.name": "docker.io/library/test-image:test",
"org.opencontainers.image.ref.name": "test"
}
}
]
}

View File

@@ -23,7 +23,7 @@ atts := union({
provs("https://spdx.dev/Document"),
})
opts := {"keys": keys}
opts := {"keys": keys, "skip_tl": true}
statements contains s if {
some att in atts

View File

@@ -0,0 +1 @@
config.yaml

View File

@@ -0,0 +1,15 @@
version: v1
kind: policy-mapping
policies:
- id: test-images
description: Local test images
files:
- path: policy.rego
- path: config.yaml #auto generated
attestations:
style: attached
rules:
- pattern: "^docker[.]io/library/test-image$"
policy-id: test-images
- pattern: "^mirror[.]org/library/(.*)$"
rewrite: docker.io/library/$1

View File

@@ -0,0 +1,61 @@
package attest
import rego.v1
import data.keys
import input.parameters
provs(pred) := p if {
res := attest.fetch(pred)
not res.error
p := res.value
}
atts := union({
provs("https://slsa.dev/provenance/v0.2"),
provs("https://spdx.dev/Document"),
})
opts := {"keys": keys, "skip_tl": true}
statements contains s if {
parameters.foo == "bar"
some att in atts
res := attest.verify(att, opts)
not res.error
s := res.value
}
subjects contains subject if {
some statement in statements
some subject in statement.subject
}
unsafe_statement_from_attestation(att) := statement if {
payload := att.payload
statement := json.unmarshal(base64.decode(payload))
}
violations contains violation if {
some att in atts
statement := unsafe_statement_from_attestation(att)
res := attest.verify(att, opts)
err := res.error
violation := {
"type": "unsigned_statement",
"description": sprintf("Statement is not correctly signed: %v", [err]),
"attestation": statement,
"details": {"error": err},
}
}
result := {
"success": count(statements) > 0,
"violations": violations,
"summary": {
"subjects": subjects,
"slsa_level": "SLSA_BUILD_LEVEL_3",
"verifier": "docker-official-images",
"policy_uri": "https://docker.com/official/policy/v0.1",
},
}

View File

@@ -23,7 +23,7 @@ atts := union({
provs("https://spdx.dev/Document"),
})
opts := {"keys": keys}
opts := {"keys": keys, "skip_tl": true}
statements contains s if {
some att in atts
@@ -37,11 +37,17 @@ subjects contains subject if {
some subject in statement.subject
}
inputs contains desc if {
some att in atts
desc := att.resourceDescriptor
}
result := {
"success": true,
"violations": set(),
"summary": {
"subjects": subjects,
"input_attestations": inputs,
"slsa_levels": ["SLSA_BUILD_LEVEL_3"],
"verifier": "docker-official-images",
"policy_uri": "https://docker.com/official/policy/v0.1",

View File

@@ -22,7 +22,7 @@ atts := union({
provs("https://spdx.dev/Document"),
})
opts := {"keys": keys}
opts := {"keys": keys, "skip_tl": false}
statements contains s if {
some att in atts

View File

@@ -0,0 +1 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064"]}}

View File

@@ -0,0 +1,19 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:5049b0fd8de6fc8937065a0b26214e5a1e620e98488de6bac72c0284b1a5242f",
"size": 167
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:e2c3b7df754e062b0c6b17c5262ea237fc86d68432e86e68724c57f04be3d064",
"size": 917,
"annotations": {
"in-toto.io/predicate-type": "https://spdx.dev/Document"
}
}
]
}

View File

@@ -0,0 +1,28 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec",
"size": 288,
"platform": {
"architecture": "arm64",
"os": "linux"
}
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:a051db630f91aae4fc649b455724f2c5c60ae0c508e87d88937e862524f488b5",
"size": 558,
"annotations": {
"vnd.docker.reference.digest": "sha256:e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
"architecture": "unknown",
"os": "unknown"
}
}
]
}

View File

@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/","ArgsEscaped":true},"created":null,"history":[{"created_by":"CMD []","comment":"buildkit.dockerfile.v0","empty_layer":true}],"os":"linux","rootfs":{"type":"layers","diff_ids":null}}

View File

@@ -0,0 +1 @@
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test@latest?platform=linux%2Farm64","digest":{"sha256":"e44a73ec811b0442dfcdd13a0eb035746d0569662684dafe2f3e8abe644871ec"}}],"predicate":{"SPDXID":"SPDXRef-DOCUMENT","creationInfo":{"created":"2024-09-19T20:28:48Z","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0"],"licenseListVersion":"3.23"},"dataLicense":"CC0-1.0","documentNamespace":"https://anchore.com/syft/dir/sbom-4d662591-02b0-4448-8cdc-c8b539bbe1a0","name":"sbom","packages":[{"SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","downloadLocation":"NOASSERTION","filesAnalyzed":false,"name":"sbom","primaryPackagePurpose":"FILE","supplier":"NOASSERTION"}],"relationships":[{"relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES","spdxElementId":"SPDXRef-DOCUMENT"}],"spdxVersion":"SPDX-2.3"}}

View File

@@ -0,0 +1,10 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:cbeaa84fb2fdfc8fd5e437555e94a323ac8acc69e68278d127cb4adf595f9d46",
"size": 308
},
"layers": null
}

View File

@@ -0,0 +1,14 @@
{
"schemaVersion": 2,
"mediaType": "application/vnd.oci.image.index.v1+json",
"manifests": [
{
"mediaType": "application/vnd.oci.image.index.v1+json",
"digest": "sha256:bba371330d0124ce45f669c5d73092a3f2078ed1491e2bc52189a82e279074a1",
"size": 855,
"annotations": {
"org.opencontainers.image.ref.name": "test"
}
}
]
}

View File

@@ -1 +0,0 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e"]}}

View File

@@ -1 +0,0 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850"]}}

View File

@@ -1 +0,0 @@
{"schemaVersion":2,"manifests":[{"mediaType":"application/vnd.oci.image.index.v1+json","digest":"sha256:1effe3a77c594e579388dc4553dbbe762e4457a099ab8b706e67f5f9fc934701","size":1607,"annotations":{"org.opencontainers.image.created":"2024-04-29T10:23:48Z","org.opencontainers.image.ref.name":"docker.io/library/test-image:test"}}]}

View File

@@ -3,13 +3,13 @@
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:b6ef78de3633e45d1c08019fbabb4464fabd6dd32e82c67ea2b2a3c4e8bacdf5",
"digest": "sha256:bb0ed50656ccdb2eb114407de579554426777d6dc0e4206a6f746afb4ee5237e",
"size": 167
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:a9646604f9522bf59d203a86ac5c2354a573ea041b8846409c4fc0f8c4a70850",
"digest": "sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33",
"size": 946,
"annotations": {
"in-toto.io/predicate-type": "https://spdx.dev/Document"

View File

@@ -3,13 +3,13 @@
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:2e82727457f04f320b643cb6e13bcbafb8e0dc0adc0443f1a25666f9518c5071",
"digest": "sha256:816b20ea86474dcfb2906ffaf4410262dfcb0d49fdfb60698775f7bc10aad7fb",
"size": 167
},
"layers": [
{
"mediaType": "application/vnd.in-toto+json",
"digest": "sha256:da5651e8877b960aa30f32f317fbeba28f5e06f1ce4d3895b3b8770140280a2e",
"digest": "sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae",
"size": 946,
"annotations": {
"in-toto.io/predicate-type": "https://spdx.dev/Document"

View File

@@ -4,7 +4,7 @@
"manifests": [
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
"digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390",
"size": 476,
"platform": {
"architecture": "amd64",
@@ -13,7 +13,7 @@
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
"digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d",
"size": 476,
"platform": {
"architecture": "arm64",
@@ -22,10 +22,10 @@
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:2aaebbb079957470e7c0adddbb054b2b4c01f717d408efba753da2bf6e8905da",
"digest": "sha256:059eea09507d0f904b8892ee59fcd3ddec1a637fc40fb7c83c432c6ff27e2f91",
"size": 558,
"annotations": {
"vnd.docker.reference.digest": "sha256:da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620",
"vnd.docker.reference.digest": "sha256:7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {
@@ -35,10 +35,10 @@
},
{
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"digest": "sha256:9b009d6b84b1ed941070b3f919823446286a674ad669d0baa8ab2c358aeb3a82",
"digest": "sha256:0b1ee0f360b073d2f76ceed15a63e291659fbcc6c3caf3be39e437d8344b520e",
"size": 558,
"annotations": {
"vnd.docker.reference.digest": "sha256:7a76cec943853f9f7105b1976afa1bf7cd5bb6afc4e9d5852dd8da7cf81ae86e",
"vnd.docker.reference.digest": "sha256:52f7a760b9322aa1af76d998763868b7d1bfec2331a2574a438ef44c92c0c46d",
"vnd.docker.reference.type": "attestation-manifest"
},
"platform": {

View File

@@ -0,0 +1 @@
{"architecture":"amd64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}}

View File

@@ -3,13 +3,13 @@
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:d85d624a324422194b43cccd975b5752cf0acaedd668bb525fcd40c3587cc460",
"size": 453
"digest": "sha256:c0bd7799c46e00830b4d7cb8c1f622d14aae81643a90be5ec38c9be4bdd70f6c",
"size": 438
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
"digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336",
"size": 116
}
]

View File

@@ -1 +1 @@
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"da8b190665956ea07890a0273e2a9c96bfe291662f08e2860e868eef69c34620"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-6d900ae6-587d-4695-9c01-511801a85b65","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.12.4"],"created":"2024-03-08T16:42:30Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}
{"_type":"https://in-toto.io/Statement/v0.1","predicateType":"https://spdx.dev/Document","subject":[{"name":"pkg:docker/test-image@test?platform=linux%2Famd64","digest":{"sha256":"7ae6b41655929ad8e1848064874a98ac3f68884996c79907f6525e3045f75390"}}],"predicate":{"spdxVersion":"SPDX-2.3","dataLicense":"CC0-1.0","SPDXID":"SPDXRef-DOCUMENT","name":"sbom","documentNamespace":"https://anchore.com/syft/dir/sbom-b92d7d2e-7ffe-4d0a-8194-9af68e80e169","creationInfo":{"licenseListVersion":"3.23","creators":["Organization: Anchore, Inc","Tool: syft-v0.105.0","Tool: buildkit-v0.15.2"],"created":"2024-09-27T16:10:21Z"},"packages":[{"name":"sbom","SPDXID":"SPDXRef-DocumentRoot-Directory-sbom","supplier":"NOASSERTION","downloadLocation":"NOASSERTION","filesAnalyzed":false,"primaryPackagePurpose":"FILE"}],"relationships":[{"spdxElementId":"SPDXRef-DOCUMENT","relatedSpdxElement":"SPDXRef-DocumentRoot-Directory-sbom","relationshipType":"DESCRIBES"}]}}

View File

@@ -3,13 +3,13 @@
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"config": {
"mediaType": "application/vnd.oci.image.config.v1+json",
"digest": "sha256:1c70b3e7c3a57801501ec127aa6c918c390c373294ec4fc48f2c6fe703fcc6fe",
"size": 453
"digest": "sha256:363133d587b90ff7a21f7b32a96be8422c6799683f0e1e6d71de5c03a82ab35e",
"size": 438
},
"layers": [
{
"mediaType": "application/vnd.oci.image.layer.v1.tar+gzip",
"digest": "sha256:97a548f8d65d9ab617f608dd621f59e0d43a3b346f34c34eb58da31f00a9b0ad",
"digest": "sha256:07d9a868932bd092fa0a4c4df943785a7ba9cee12dbf446d02488319a5fbf336",
"size": 116
}
]

View File

@@ -0,0 +1 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:f0dac65dd0ff6a656c419c654ac672c38029a3f1a4b4acce062bd2f5a923ffae"]}}

View File

@@ -0,0 +1 @@
{"architecture":"unknown","os":"unknown","config":{},"rootfs":{"type":"layers","diff_ids":["sha256:618f1e2f903648dde23cc38dc0ed7eed83d5394a6902bb7bfae8fa707c2e5c33"]}}

View File

@@ -0,0 +1 @@
{"architecture":"arm64","config":{"Env":["PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"],"WorkingDir":"/"},"created":"2024-09-27T16:10:13.292759474Z","history":[{"created":"2024-09-27T16:10:13.292759474Z","created_by":"COPY /tmp/hello.txt / # buildkit","comment":"buildkit.dockerfile.v0"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:81a78ab7aa0b72d665a9c203b4c30f0423e434b789ed95b2d418e60a1b726470"]}}

Some files were not shown because too many files have changed in this diff Show More