Files
buildx/policy/materials.go
Tonis Tiigi 4880756a0f policy: simplify recursive material resolution
Unify root/material unknown resolution with recursive Input traversal.

Signed-off-by: Tonis Tiigi <tonistiigi@gmail.com>
2026-02-26 09:14:25 -08:00

117 lines
3.3 KiB
Go

package policy
import (
"path"
"strconv"
"strings"
"github.com/distribution/reference"
"github.com/docker/buildx/util/urlutil"
slsa1 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v1"
"github.com/moby/buildkit/solver/pb"
"github.com/moby/buildkit/util/gitutil"
"github.com/moby/buildkit/util/purl"
"github.com/opencontainers/go-digest"
ocispecs "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/pkg/errors"
)
func isMaterialKey(key string) (idx int, rest string, ok bool) {
const prefix = "image.provenance.materials["
if !strings.HasPrefix(key, prefix) {
return 0, "", false
}
rest = strings.TrimPrefix(key, prefix)
end := strings.IndexByte(rest, ']')
if end < 0 {
return 0, "", false
}
n, err := strconv.Atoi(rest[:end])
if err != nil {
return 0, "", false
}
rest = strings.TrimPrefix(rest[end+1:], ".")
return n, rest, true
}
func parseSLSAMaterial(m slsa1.ResourceDescriptor) (*pb.SourceOp, *ocispecs.Platform, error) {
uri := m.URI
dgst := m.Digest
if strings.HasPrefix(uri, "pkg:docker/") {
return dockerMaterialSource(uri, dgst)
}
if gu, err := gitutil.ParseURL(uri); err == nil {
if strings.HasSuffix(strings.ToLower(gu.Path), ".git") || gu.Scheme != gitutil.HTTPProtocol && gu.Scheme != gitutil.HTTPSProtocol {
return gitMaterialSource(uri)
}
}
if urlutil.IsHTTPURL(uri) {
return &pb.SourceOp{Identifier: uri}, nil, nil
}
return nil, nil, errors.Errorf("unsupported material URI %q", uri)
}
func dockerMaterialSource(uri string, dgst map[string]string) (*pb.SourceOp, *ocispecs.Platform, error) {
refStr, platform, err := purl.PURLToRef(uri)
if err != nil {
return nil, nil, err
}
named, err := reference.ParseNormalizedNamed(refStr)
if err != nil {
return nil, nil, errors.Wrapf(err, "invalid docker reference %q from %q", refStr, uri)
}
if checksum := strings.TrimSpace(dgst["sha256"]); checksum != "" {
dgstRef, err := digest.Parse(checksum)
if err != nil {
dgstRef, err = digest.Parse("sha256:" + checksum)
}
if err != nil {
return nil, nil, errors.Wrapf(err, "invalid material digest %q for %q", checksum, uri)
}
if canonical, ok := named.(reference.Canonical); ok {
if canonical.Digest() != dgstRef {
return nil, nil, errors.Errorf("material digest mismatch for %q: ref has %s but provenance has %s", uri, canonical.Digest(), dgstRef)
}
} else {
named, err = reference.WithDigest(named, dgstRef)
if err != nil {
return nil, nil, errors.Wrapf(err, "failed to add digest %q to %q", dgstRef, refStr)
}
}
}
return &pb.SourceOp{Identifier: "docker-image://" + named.String()}, platform, nil
}
func gitMaterialSource(uri string) (*pb.SourceOp, *ocispecs.Platform, error) {
gu, err := gitutil.ParseURL(uri)
if err != nil {
return nil, nil, err
}
switch gu.Scheme {
case gitutil.HTTPSProtocol, gitutil.HTTPProtocol, gitutil.SSHProtocol, gitutil.GitProtocol:
default:
return nil, nil, errors.Errorf("unsupported git material URI %q", uri)
}
id := gu.Host + path.Join("/", gu.Path)
if gu.Opts != nil && (gu.Opts.Ref != "" || gu.Opts.Subdir != "") {
id += "#" + gu.Opts.Ref
if gu.Opts.Subdir != "" {
id += ":" + gu.Opts.Subdir
}
}
return &pb.SourceOp{
Identifier: "git://" + id,
Attrs: map[string]string{
pb.AttrFullRemoteURL: uri,
},
}, nil, nil
}