mirror of
				https://gitea.com/Lydanne/buildx.git
				synced 2025-11-04 18:13:42 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			400 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			400 lines
		
	
	
		
			10 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
package imagetools
 | 
						|
 | 
						|
import (
 | 
						|
	"context"
 | 
						|
	"encoding/base64"
 | 
						|
	"encoding/json"
 | 
						|
	"fmt"
 | 
						|
	"io"
 | 
						|
	"strings"
 | 
						|
 | 
						|
	"github.com/containerd/containerd/remotes"
 | 
						|
	intoto "github.com/in-toto/in-toto-golang/in_toto"
 | 
						|
	slsa02 "github.com/in-toto/in-toto-golang/in_toto/slsa_provenance/v0.2"
 | 
						|
	"github.com/opencontainers/go-digest"
 | 
						|
	ocispec "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
	v1 "github.com/opencontainers/image-spec/specs-go/v1"
 | 
						|
)
 | 
						|
 | 
						|
type attestationType int
 | 
						|
 | 
						|
const (
 | 
						|
	plainSpdx             attestationType = 0
 | 
						|
	dsseEmbeded           attestationType = 1
 | 
						|
	plainSpdxAndDSSEEmbed attestationType = 2
 | 
						|
)
 | 
						|
 | 
						|
type mockFetcher struct {
 | 
						|
}
 | 
						|
 | 
						|
type mockResolver struct {
 | 
						|
	fetcher remotes.Fetcher
 | 
						|
	pusher  remotes.Pusher
 | 
						|
}
 | 
						|
 | 
						|
var manifests = make(map[digest.Digest]manifest)
 | 
						|
var indexes = make(map[digest.Digest]index)
 | 
						|
 | 
						|
func (f mockFetcher) Fetch(ctx context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
 | 
						|
	switch desc.MediaType {
 | 
						|
	case ocispec.MediaTypeImageIndex:
 | 
						|
		reader := io.NopCloser(strings.NewReader(indexes[desc.Digest].desc.Annotations["test_content"]))
 | 
						|
		return reader, nil
 | 
						|
	case ocispec.MediaTypeImageManifest:
 | 
						|
		reader := io.NopCloser(strings.NewReader(manifests[desc.Digest].desc.Annotations["test_content"]))
 | 
						|
		return reader, nil
 | 
						|
	default:
 | 
						|
		reader := io.NopCloser(strings.NewReader(desc.Annotations["test_content"]))
 | 
						|
		return reader, nil
 | 
						|
	}
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
func (r mockResolver) Resolve(ctx context.Context, ref string) (name string, desc ocispec.Descriptor, err error) {
 | 
						|
	d := digest.Digest(strings.ReplaceAll(ref, "docker.io/library/test@", ""))
 | 
						|
	return string(d), indexes[d].desc, nil
 | 
						|
}
 | 
						|
 | 
						|
func (r mockResolver) Fetcher(ctx context.Context, ref string) (remotes.Fetcher, error) {
 | 
						|
	return r.fetcher, nil
 | 
						|
}
 | 
						|
 | 
						|
func (r mockResolver) Pusher(ctx context.Context, ref string) (remotes.Pusher, error) {
 | 
						|
	return r.pusher, nil
 | 
						|
}
 | 
						|
 | 
						|
func getMockResolver() remotes.Resolver {
 | 
						|
	resolver := mockResolver{
 | 
						|
		fetcher: mockFetcher{},
 | 
						|
	}
 | 
						|
 | 
						|
	return resolver
 | 
						|
}
 | 
						|
 | 
						|
func getImageNoAttestation() *result {
 | 
						|
	return getImageFromManifests(getBaseManifests())
 | 
						|
}
 | 
						|
 | 
						|
func getImageWithAttestation(t attestationType) *result {
 | 
						|
	manifestList := getBaseManifests()
 | 
						|
 | 
						|
	objManifest := ocispec.Manifest{
 | 
						|
		MediaType: v1.MediaTypeImageManifest,
 | 
						|
		Layers:    getAttestationLayers(t),
 | 
						|
		Annotations: map[string]string{
 | 
						|
			"platform": "linux/amd64",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	jsonContent, _ := json.Marshal(objManifest)
 | 
						|
	jsonString := string(jsonContent)
 | 
						|
	d := digest.FromString(jsonString)
 | 
						|
 | 
						|
	manifestList[d] = manifest{
 | 
						|
		desc: ocispec.Descriptor{
 | 
						|
			MediaType: v1.MediaTypeImageManifest,
 | 
						|
			Digest:    d,
 | 
						|
			Size:      int64(len(jsonString)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"vnd.docker.reference.digest": string(getManifestDigestForArch(manifestList, "linux", "amd64")),
 | 
						|
				"vnd.docker.reference.type":   "attestation-manifest",
 | 
						|
				"test_content":                jsonString,
 | 
						|
			},
 | 
						|
			Platform: &v1.Platform{
 | 
						|
				Architecture: "unknown",
 | 
						|
				OS:           "unknown",
 | 
						|
			},
 | 
						|
		},
 | 
						|
		manifest: objManifest,
 | 
						|
	}
 | 
						|
 | 
						|
	objManifest = ocispec.Manifest{
 | 
						|
		MediaType: v1.MediaTypeImageManifest,
 | 
						|
		Layers:    getAttestationLayers(t),
 | 
						|
		Annotations: map[string]string{
 | 
						|
			"platform": "linux/arm64",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	jsonContent, _ = json.Marshal(objManifest)
 | 
						|
	jsonString = string(jsonContent)
 | 
						|
	d = digest.FromString(jsonString)
 | 
						|
	manifestList[d] = manifest{
 | 
						|
		desc: ocispec.Descriptor{
 | 
						|
			MediaType: v1.MediaTypeImageManifest,
 | 
						|
			Digest:    d,
 | 
						|
			Size:      int64(len(jsonString)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"vnd.docker.reference.digest": string(getManifestDigestForArch(manifestList, "linux", "arm64")),
 | 
						|
				"vnd.docker.reference.type":   "attestation-manifest",
 | 
						|
				"test_content":                jsonString,
 | 
						|
			},
 | 
						|
			Platform: &v1.Platform{
 | 
						|
				Architecture: "unknown",
 | 
						|
				OS:           "unknown",
 | 
						|
			},
 | 
						|
		},
 | 
						|
	}
 | 
						|
 | 
						|
	return getImageFromManifests(manifestList)
 | 
						|
}
 | 
						|
 | 
						|
func getImageFromManifests(manifests map[digest.Digest]manifest) *result {
 | 
						|
	r := &result{
 | 
						|
		indexes:   make(map[digest.Digest]index),
 | 
						|
		manifests: manifests,
 | 
						|
		images:    make(map[string]digest.Digest),
 | 
						|
		refs:      make(map[digest.Digest][]digest.Digest),
 | 
						|
		assets:    make(map[string]asset),
 | 
						|
	}
 | 
						|
 | 
						|
	r.images["linux/amd64"] = getManifestDigestForArch(manifests, "linux", "amd64")
 | 
						|
	r.images["linux/arm64"] = getManifestDigestForArch(manifests, "linux", "arm64")
 | 
						|
 | 
						|
	manifestsDesc := []v1.Descriptor{}
 | 
						|
	for _, val := range manifests {
 | 
						|
		manifestsDesc = append(manifestsDesc, val.desc)
 | 
						|
	}
 | 
						|
 | 
						|
	objIndex := v1.Index{
 | 
						|
		MediaType: v1.MediaTypeImageIndex,
 | 
						|
		Manifests: manifestsDesc,
 | 
						|
	}
 | 
						|
	jsonContent, _ := json.Marshal(objIndex)
 | 
						|
	jsonString := string(jsonContent)
 | 
						|
	d := digest.FromString(jsonString)
 | 
						|
 | 
						|
	if _, ok := indexes[d]; !ok {
 | 
						|
		indexes[d] = index{
 | 
						|
			desc: ocispec.Descriptor{
 | 
						|
				MediaType: v1.MediaTypeImageIndex,
 | 
						|
				Digest:    d,
 | 
						|
				Size:      int64(len(jsonString)),
 | 
						|
				Annotations: map[string]string{
 | 
						|
					"test_content": jsonString,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			index: objIndex,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	r.indexes[d] = indexes[d]
 | 
						|
	return r
 | 
						|
}
 | 
						|
 | 
						|
func getManifestDigestForArch(manifests map[digest.Digest]manifest, os string, arch string) digest.Digest {
 | 
						|
	for d, m := range manifests {
 | 
						|
		if m.desc.Platform.OS == os && m.desc.Platform.Architecture == arch {
 | 
						|
			return d
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return digest.Digest("")
 | 
						|
}
 | 
						|
 | 
						|
func getBaseManifests() map[digest.Digest]manifest {
 | 
						|
	if len(manifests) == 0 {
 | 
						|
		config := getConfig()
 | 
						|
		content := "amd64-content"
 | 
						|
		objManifest := ocispec.Manifest{
 | 
						|
			MediaType: v1.MediaTypeImageManifest,
 | 
						|
			Config:    config,
 | 
						|
			Layers: []v1.Descriptor{
 | 
						|
				{
 | 
						|
					MediaType: v1.MediaTypeImageLayerGzip,
 | 
						|
					Digest:    digest.FromString(content),
 | 
						|
					Size:      int64(len(content)),
 | 
						|
				},
 | 
						|
			},
 | 
						|
		}
 | 
						|
		jsonContent, _ := json.Marshal(objManifest)
 | 
						|
		jsonString := string(jsonContent)
 | 
						|
		d := digest.FromString(jsonString)
 | 
						|
 | 
						|
		manifests[d] = manifest{
 | 
						|
			desc: ocispec.Descriptor{
 | 
						|
				MediaType: v1.MediaTypeImageManifest,
 | 
						|
				Digest:    d,
 | 
						|
				Size:      int64(len(jsonString)),
 | 
						|
				Platform: &v1.Platform{
 | 
						|
					Architecture: "amd64",
 | 
						|
					OS:           "linux",
 | 
						|
				},
 | 
						|
				Annotations: map[string]string{
 | 
						|
					"test_content": jsonString,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			manifest: objManifest,
 | 
						|
		}
 | 
						|
 | 
						|
		content = "arm64-content"
 | 
						|
		objManifest = ocispec.Manifest{
 | 
						|
			MediaType: v1.MediaTypeImageManifest,
 | 
						|
			Config:    config,
 | 
						|
			Layers: []v1.Descriptor{
 | 
						|
				{
 | 
						|
					MediaType: v1.MediaTypeImageLayerGzip,
 | 
						|
					Digest:    digest.FromString(content),
 | 
						|
					Size:      int64(len(content)),
 | 
						|
				},
 | 
						|
			},
 | 
						|
		}
 | 
						|
		jsonContent, _ = json.Marshal(objManifest)
 | 
						|
		jsonString = string(jsonContent)
 | 
						|
		d = digest.FromString(jsonString)
 | 
						|
 | 
						|
		manifests[d] = manifest{
 | 
						|
			desc: ocispec.Descriptor{
 | 
						|
				MediaType: v1.MediaTypeImageManifest,
 | 
						|
				Digest:    d,
 | 
						|
				Size:      int64(len(jsonString)),
 | 
						|
				Platform: &v1.Platform{
 | 
						|
					Architecture: "arm64",
 | 
						|
					OS:           "linux",
 | 
						|
				},
 | 
						|
				Annotations: map[string]string{
 | 
						|
					"test_content": jsonString,
 | 
						|
				},
 | 
						|
			},
 | 
						|
			manifest: objManifest,
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	return manifests
 | 
						|
}
 | 
						|
 | 
						|
func getConfig() v1.Descriptor {
 | 
						|
	config := v1.ImageConfig{
 | 
						|
		Env: []string{
 | 
						|
			"config",
 | 
						|
		},
 | 
						|
	}
 | 
						|
	jsonContent, _ := json.Marshal(config)
 | 
						|
	jsonString := string(jsonContent)
 | 
						|
	d := digest.FromString(jsonString)
 | 
						|
 | 
						|
	return v1.Descriptor{
 | 
						|
		MediaType: ocispec.MediaTypeImageConfig,
 | 
						|
		Digest:    d,
 | 
						|
		Size:      int64(len(jsonString)),
 | 
						|
		Annotations: map[string]string{
 | 
						|
			"test_content": jsonString,
 | 
						|
		},
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
func getAttestationLayers(t attestationType) []v1.Descriptor {
 | 
						|
	layers := []v1.Descriptor{}
 | 
						|
 | 
						|
	if t == plainSpdx || t == plainSpdxAndDSSEEmbed {
 | 
						|
		layers = append(layers, v1.Descriptor{
 | 
						|
			MediaType: inTotoGenericMime,
 | 
						|
			Digest:    digest.FromString(attestationContent),
 | 
						|
			Size:      int64(len(attestationContent)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"in-toto.io/predicate-type": intoto.PredicateSPDX,
 | 
						|
				"test_content":              attestationContent,
 | 
						|
			},
 | 
						|
		})
 | 
						|
		layers = append(layers, v1.Descriptor{
 | 
						|
			MediaType: inTotoGenericMime,
 | 
						|
			Digest:    digest.FromString(provenanceContent),
 | 
						|
			Size:      int64(len(provenanceContent)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"in-toto.io/predicate-type": slsa02.PredicateSLSAProvenance,
 | 
						|
				"test_content":              provenanceContent,
 | 
						|
			},
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	if t == dsseEmbeded || t == plainSpdxAndDSSEEmbed {
 | 
						|
		dsseAttestation := fmt.Sprintf("{\"payload\":\"%s\"}", base64.StdEncoding.EncodeToString([]byte(attestationContent)))
 | 
						|
		dsseProvenance := fmt.Sprintf("{\"payload\":\"%s\"}", base64.StdEncoding.EncodeToString([]byte(provenanceContent)))
 | 
						|
		layers = append(layers, v1.Descriptor{
 | 
						|
			MediaType: inTotoSPDXDSSEMime,
 | 
						|
			Digest:    digest.FromString(dsseAttestation),
 | 
						|
			Size:      int64(len(dsseAttestation)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"in-toto.io/predicate-type": intoto.PredicateSPDX,
 | 
						|
				"test_content":              dsseAttestation,
 | 
						|
			},
 | 
						|
		})
 | 
						|
		layers = append(layers, v1.Descriptor{
 | 
						|
			MediaType: inTotoProvenanceDSSEMime,
 | 
						|
			Digest:    digest.FromString(dsseProvenance),
 | 
						|
			Size:      int64(len(dsseProvenance)),
 | 
						|
			Annotations: map[string]string{
 | 
						|
				"in-toto.io/predicate-type": slsa02.PredicateSLSAProvenance,
 | 
						|
				"test_content":              dsseProvenance,
 | 
						|
			},
 | 
						|
		})
 | 
						|
	}
 | 
						|
 | 
						|
	return layers
 | 
						|
}
 | 
						|
 | 
						|
const attestationContent = `
 | 
						|
{
 | 
						|
    "_type": "https://in-toto.io/Statement/v0.1",
 | 
						|
    "predicateType": "https://spdx.dev/Document",
 | 
						|
    "predicate": {
 | 
						|
		"name": "sbom",
 | 
						|
		"spdxVersion": "SPDX-2.3",
 | 
						|
		"SPDXID": "SPDXRef-DOCUMENT",
 | 
						|
		"creationInfo": {
 | 
						|
			"created": "2024-01-31T16:09:05Z",
 | 
						|
			"creators": [
 | 
						|
				"Tool: buildkit-v0.11.0"
 | 
						|
			],
 | 
						|
			"licenseListVersion": "3.22"
 | 
						|
		},
 | 
						|
		"dataLicense": "CC0-1.0",
 | 
						|
		"documentNamespace": "https://example.com",
 | 
						|
		"packages": [
 | 
						|
			{
 | 
						|
				"name": "sbom",
 | 
						|
				"SPDXID": "SPDXRef-DocumentRoot-Directory-sbom",
 | 
						|
				"copyrightText": "",
 | 
						|
				"downloadLocation": "NOASSERTION",
 | 
						|
				"primaryPackagePurpose": "FILE",
 | 
						|
				"supplier": "NOASSERTION"
 | 
						|
			}
 | 
						|
		],
 | 
						|
		"relationships": [
 | 
						|
			{
 | 
						|
				"relatedSpdxElement": "SPDXRef-DocumentRoot-Directory-sbom",
 | 
						|
				"relationshipType": "DESCRIBES",
 | 
						|
				"spdxElementId": "SPDXRef-DOCUMENT"
 | 
						|
			}
 | 
						|
		]
 | 
						|
	}
 | 
						|
}
 | 
						|
`
 | 
						|
 | 
						|
const provenanceContent = `
 | 
						|
{
 | 
						|
	"_type": "https://in-toto.io/Statement/v0.1",
 | 
						|
	"predicateType": "https://slsa.dev/provenance/v0.2",
 | 
						|
	"predicate": {
 | 
						|
		"buildType": "https://example.com/Makefile",
 | 
						|
		"builder": { 
 | 
						|
			"id": "mailto:person@example.com"
 | 
						|
		},
 | 
						|
		"invocation": {
 | 
						|
			"configSource": {
 | 
						|
				"uri": "https://example.com/example-1.2.3.tar.gz",
 | 
						|
				"digest": {"sha256": ""},
 | 
						|
				"entryPoint": "src:foo"
 | 
						|
			},
 | 
						|
			"parameters": {
 | 
						|
				"CFLAGS": "-O3"
 | 
						|
			},
 | 
						|
			"materials": [
 | 
						|
				{
 | 
						|
					"uri": "https://example.com/example-1.2.3.tar.gz",
 | 
						|
					"digest": {"sha256": ""}
 | 
						|
				}
 | 
						|
			]
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
`
 |