123 lines
3.5 KiB
Go
123 lines
3.5 KiB
Go
package oci
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"github.com/docker/attest/pkg/attestation"
|
|
att "github.com/docker/attest/pkg/attestation"
|
|
"github.com/google/go-containerregistry/pkg/name"
|
|
"github.com/google/go-containerregistry/pkg/v1/remote"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
type ReferrersResolver struct {
|
|
digest string
|
|
referrersRepo string
|
|
manifests []*attestation.AttestationManifest
|
|
ImageDetailsResolver
|
|
}
|
|
|
|
func NewReferrersAttestationResolver(src ImageDetailsResolver, options ...func(*ReferrersResolver) error) (*ReferrersResolver, error) {
|
|
res := &ReferrersResolver{
|
|
ImageDetailsResolver: src,
|
|
}
|
|
for _, opt := range options {
|
|
err := opt(res)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
return res, nil
|
|
}
|
|
|
|
func WithReferrersRepo(repo string) func(*ReferrersResolver) error {
|
|
return func(r *ReferrersResolver) error {
|
|
r.referrersRepo = repo
|
|
return nil
|
|
}
|
|
}
|
|
|
|
func (r *ReferrersResolver) resolveAttestations(ctx context.Context) error {
|
|
if r.manifests == nil {
|
|
imageName, err := r.ImageName(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get image name: %w", err)
|
|
}
|
|
subjectRef, err := name.ParseReference(imageName)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to parse reference: %w", err)
|
|
}
|
|
desc, err := r.ImageDescriptor(ctx)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get descriptor: %w", err)
|
|
}
|
|
subjectDigest := desc.Digest.String()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get digest: %w", err)
|
|
}
|
|
var referrersSubjectRef name.Digest
|
|
if r.referrersRepo != "" {
|
|
referrersSubjectRef, err = name.NewDigest(fmt.Sprintf("%s@%s", r.referrersRepo, subjectDigest))
|
|
if err != nil {
|
|
return fmt.Errorf("failed to create referrers reference: %w", err)
|
|
}
|
|
} else {
|
|
referrersSubjectRef = subjectRef.Context().Digest(subjectDigest)
|
|
}
|
|
// TODO - search for in-toto artifact type
|
|
referrersIndex, err := remote.Referrers(referrersSubjectRef)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get referrers: %w", err)
|
|
}
|
|
referrersIndexManifest, err := referrersIndex.IndexManifest()
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get index manifest: %w", err)
|
|
}
|
|
if len(referrersIndexManifest.Manifests) == 0 {
|
|
return errors.New("no referrers found")
|
|
}
|
|
aManifests := make([]*attestation.AttestationManifest, 0)
|
|
for _, m := range referrersIndexManifest.Manifests {
|
|
remoteRef := referrersSubjectRef.Context().Digest(m.Digest.String())
|
|
attestationImage, err := remote.Image(remoteRef)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get referred image: %w", err)
|
|
}
|
|
layers, err := attestation.GetAttestationsFromImage(attestationImage)
|
|
if err != nil {
|
|
return fmt.Errorf("failed to get attestations from image: %w", err)
|
|
}
|
|
attest := &attestation.AttestationManifest{
|
|
SubjectName: imageName,
|
|
OriginalLayers: layers,
|
|
OriginalDescriptor: &m,
|
|
SubjectDescriptor: desc,
|
|
}
|
|
aManifests = append(aManifests, attest)
|
|
}
|
|
|
|
if len(aManifests) == 0 {
|
|
return errors.New("no attestation manifests found")
|
|
}
|
|
r.manifests = aManifests
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (r *ReferrersResolver) Attestations(ctx context.Context, predicateType string) ([]*att.Envelope, error) {
|
|
err := r.resolveAttestations(ctx)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to resolve attestations: %w", err)
|
|
}
|
|
var envs []*att.Envelope
|
|
for _, attest := range r.manifests {
|
|
es, err := ExtractEnvelopes(attest, predicateType)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to extract envelopes: %w", err)
|
|
}
|
|
envs = append(envs, es...)
|
|
}
|
|
return envs, nil
|
|
}
|