Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 54 additions & 1 deletion pkg/fanal/artifact/image/remote_sbom.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package image

import (
"context"
"encoding/json"
"errors"
"fmt"
"os"
Expand Down Expand Up @@ -105,7 +106,20 @@ func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descri
return artifact.Reference{}, xerrors.Errorf("SBOM download error: %w", err)
}

res, err := a.inspectSBOMFile(ctx, filepath.Join(tmpDir, fileName))
filePath := filepath.Join(tmpDir, fileName)

// Handle Sigstore bundles differently - extract DSSE payload
if desc.ArtifactType == oci.SigstoreBundleArtifactType {
dsseFilePath, err := a.extractDSSEFromSigstoreBundle(filePath)
if err != nil {
return artifact.Reference{}, xerrors.Errorf("failed to extract DSSE from Sigstore bundle: %w", err)
}
defer os.Remove(dsseFilePath)
filePath = dsseFilePath
fmt.Printf("DEBUG: Extracted DSSE to file: %s\n", dsseFilePath)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can use a.logger.Debug

}

res, err := a.inspectSBOMFile(ctx, filePath)
if err != nil {
return res, xerrors.Errorf("SBOM error: %w", err)
}
Expand All @@ -116,6 +130,45 @@ func (a Artifact) parseReferrer(ctx context.Context, repo string, desc v1.Descri
return res, nil
}

// SigstoreBundle represents the structure of a Sigstore bundle in accordance with https://github.com/sigstore/cosign/blob/main/specs/BUNDLE_SPEC.md
type SigstoreBundle struct {
DSSEEnvelope json.RawMessage `json:"dsseEnvelope"`
MediaType string `json:"mediaType"`
}

func (a Artifact) extractDSSEFromSigstoreBundle(bundlePath string) (string, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use temp dir from parseReferrer?

// Read the Sigstore bundle file
bundleData, err := os.ReadFile(bundlePath)
if err != nil {
return "", xerrors.Errorf("failed to read Sigstore bundle: %w", err)
}

// Parse the Sigstore bundle
var bundle SigstoreBundle
if err := json.Unmarshal(bundleData, &bundle); err != nil {
return "", xerrors.Errorf("failed to parse Sigstore bundle: %w", err)
}

// Create a temporary file for the DSSE envelope
tmpFile, err := xos.CreateTemp("", "sigstore-dsse-")
if err != nil {
return "", xerrors.Errorf("failed to create temp file: %w", err)
}

// Write the DSSE envelope directly to the file
if _, err = tmpFile.Write(bundle.DSSEEnvelope); err != nil {
tmpFile.Close()
os.Remove(tmpFile.Name())
return "", xerrors.Errorf("failed to write DSSE envelope: %w", err)
}
if err = tmpFile.Close(); err != nil {
os.Remove(tmpFile.Name())
return "", xerrors.Errorf("failed to close temp file: %w", err)
}
Comment on lines +160 to +167
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we use defer for this?


return tmpFile.Name(), nil
}

func (a Artifact) inspectRekorSBOMAttestation(ctx context.Context) (artifact.Reference, error) {
digest, err := repoDigest(a.image, a.artifactOption.Insecure)
if err != nil {
Expand Down
130 changes: 130 additions & 0 deletions pkg/fanal/artifact/image/remote_sbom_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,3 +318,133 @@ func TestArtifact_inspectOCIReferrerSBOM(t *testing.T) {
})
}
}

func TestArtifact_inspectOCIReferrerSBOMSigstoreBundle(t *testing.T) {
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/v2":
_, err := w.Write([]byte("ok"))
assert.NoError(t, err)
case "/v2/test/image/referrers/sha256:782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02":
w.Header().Set("Content-Type", string(typesv1.OCIImageIndex))
http.ServeFile(w, r, "testdata/sigstore-index.json")
case "/v2/test/image/manifests/sha256:3efec4938089b060058525552ba8bf1888beddf29616e4243b4262792ed3fd2f":
http.ServeFile(w, r, "testdata/bundle-manifest.json")
case "/v2/test/image/blobs/sha256:92390b7d2d7b16c84725e9b77573fbd449a8f733113776ad78f983abb3173db3":
http.ServeFile(w, r, "testdata/sigstore-bundle.json")
}
}))
defer ts.Close()

u, err := url.Parse(ts.URL)
require.NoError(t, err)
registry := u.Host

type fields struct {
imageName string
repoDigests []string
}

tests := []struct {
name string
fields fields
artifactOpt artifact.Option
wantBlobs []cachetest.WantBlob
want artifact.Reference
wantErr string
}{
{
name: "happy path",
fields: fields{
imageName: registry + "/test/image:10",
repoDigests: []string{
registry + "/test/image@sha256:782143e39f1e7a04e3f6da2d88b1c057e5657363c4f90679f3e8a071b7619e02",
},
},
artifactOpt: artifact.Option{
SBOMSources: []string{"oci"},
},
wantBlobs: []cachetest.WantBlob{
{
ID: "sha256:2171d8ccf798e94d09aca9c6abf15d28abd3236def1caa4a394b6f0a69c4266d",
BlobInfo: types.BlobInfo{
SchemaVersion: types.BlobJSONSchemaVersion,
Applications: []types.Application{
{
Type: types.GoBinary,
Packages: types.Packages{
{
ID: "github.com/opencontainers/[email protected]",
Name: "github.com/opencontainers/go-digest",
Version: "v1.0.0",
Identifier: types.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "github.com/opencontainers",
Name: "go-digest",
Version: "v1.0.0",
},
BOMRef: "pkg:golang/github.com/opencontainers/[email protected]",
},
},
{
ID: "golang.org/x/[email protected]",
Name: "golang.org/x/sync",
Version: "v0.1.0",
Identifier: types.PkgIdentifier{
PURL: &packageurl.PackageURL{
Type: packageurl.TypeGolang,
Namespace: "golang.org/x",
Name: "sync",
Version: "v0.1.0",
},
BOMRef: "pkg:golang/golang.org/x/[email protected]",
},
},
},
},
},
},
},
},
want: artifact.Reference{
Name: registry + "/test/image:10",
Type: types.TypeCycloneDX,
ID: "sha256:2171d8ccf798e94d09aca9c6abf15d28abd3236def1caa4a394b6f0a69c4266d",
BlobIDs: []string{
"sha256:2171d8ccf798e94d09aca9c6abf15d28abd3236def1caa4a394b6f0a69c4266d",
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
c := cachetest.NewCache(t, nil)

fi := &fakei.FakeImage{}
fi.ConfigFileReturns(&v1.ConfigFile{}, nil)

img := &fakeImage{
name: tt.fields.imageName,
repoDigests: tt.fields.repoDigests,
Image: fi,
}
a, err := image2.NewArtifact(img, c, tt.artifactOpt)
require.NoError(t, err)

got, err := a.Inspect(t.Context())
if tt.wantErr != "" {
assert.ErrorContains(t, err, tt.wantErr)
return
}
defer a.Clean(got)

require.NoError(t, err, tt.name)
got.BOM = nil
assert.Equal(t, tt.want, got)

cachetest.AssertBlobs(t, c, tt.wantBlobs)
})
}
}
28 changes: 28 additions & 0 deletions pkg/fanal/artifact/image/testdata/bundle-manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"annotations": {
"dev.sigstore.bundle.content": "dsse-envelope",
"dev.sigstore.bundle.predicateType": "https://cyclonedx.org/bom",
"org.opencontainers.image.created": "2025-09-20T18:06:32Z"
},
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"config": {
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a",
"mediaType": "application/vnd.oci.empty.v1+json",
"size": 2
},
"layers": [
{
"digest": "sha256:92390b7d2d7b16c84725e9b77573fbd449a8f733113776ad78f983abb3173db3",
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"size": 8770
}
],
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"schemaVersion": 2,
"subject": {
"digest": "sha256:ea9c657ca44371fadd027510f2a1a68b799003294bf6a4827827971cc4390ad4",
"mediaType": "application/vnd.docker.distribution.manifest.v2+json",
"size": 1152
}
}
59 changes: 59 additions & 0 deletions pkg/fanal/artifact/image/testdata/sigstore-bundle.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
{
"dsseEnvelope": {
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjAuMSIsInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2N5Y2xvbmVkeC5vcmcvYm9tIiwic3ViamVjdCI6W3sibmFtZSI6ImdoY3IuaW8vcmluZ29kZXYvZGVtby1zYm9tIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImVhOWM2NTdjYTQ0MzcxZmFkZDAyNzUxMGYyYTFhNjhiNzk5MDAzMjk0YmY2YTQ4Mjc4Mjc5NzFjYzQzOTBhZDQifX1dLCJwcmVkaWNhdGUiOnsiYm9tRm9ybWF0IjoiQ3ljbG9uZURYIiwiY29tcG9uZW50cyI6W3siYm9tLXJlZiI6InBrZzpnb2xhbmcvZ2l0aHViLmNvbS9vcGVuY29udGFpbmVycy9nby1kaWdlc3RAdjEuMC4wIiwibmFtZSI6ImdpdGh1Yi5jb20vb3BlbmNvbnRhaW5lcnMvZ28tZGlnZXN0IiwicHJvcGVydGllcyI6W3sibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpQa2dUeXBlIiwidmFsdWUiOiJnb2JpbmFyeSJ9XSwicHVybCI6InBrZzpnb2xhbmcvZ2l0aHViLmNvbS9vcGVuY29udGFpbmVycy9nby1kaWdlc3RAdjEuMC4wIiwidHlwZSI6ImxpYnJhcnkiLCJ2ZXJzaW9uIjoidjEuMC4wIn0seyJib20tcmVmIjoicGtnOmdvbGFuZy9nb2xhbmcub3JnL3gvc3luY0B2MC4xLjAiLCJuYW1lIjoiZ29sYW5nLm9yZy94L3N5bmMiLCJwcm9wZXJ0aWVzIjpbeyJuYW1lIjoiYXF1YXNlY3VyaXR5OnRyaXZ5OlBrZ1R5cGUiLCJ2YWx1ZSI6ImdvYmluYXJ5In1dLCJwdXJsIjoicGtnOmdvbGFuZy9nb2xhbmcub3JnL3gvc3luY0B2MC4xLjAiLCJ0eXBlIjoibGlicmFyeSIsInZlcnNpb24iOiJ2MC4xLjAifV0sImRlcGVuZGVuY2llcyI6W3siZGVwZW5kc09uIjpbInBrZzpnb2xhbmcvZ2l0aHViLmNvbS9vcGVuY29udGFpbmVycy9nby1kaWdlc3RAdjEuMC4wIiwicGtnOmdvbGFuZy9nb2xhbmcub3JnL3gvc3luY0B2MC4xLjAiXSwicmVmIjoiYzYzMDlkNGEtZjZkYi00YWNjLThlNDQtZjBkMjcxNDkyYjY1In0seyJkZXBlbmRzT24iOlsiYzYzMDlkNGEtZjZkYi00YWNjLThlNDQtZjBkMjcxNDkyYjY1Il0sInJlZiI6InBrZzpvY2kvZGVtby1yZWZlcnJlcnMtMjAyM0BzaGEyNTY6ZTc2YTEzNDc1YzZiNGE3MTNhMGU0YTdhODU3NGNlNDUwMjc0ZDM0MDM1N2EyYzQwYjgyMjFjZmNmZWRmOGIxOT9yZXBvc2l0b3J5X3VybD1sb2NhbGhvc3Q6NTAwMSUyRmRlbW8tcmVmZXJyZXJzLTIwMjNcdTAwMjZhcmNoPWFybTY0In1dLCJtZXRhZGF0YSI6eyJjb21wb25lbnQiOnsiYm9tLXJlZiI6InBrZzpvY2kvZGVtby1yZWZlcnJlcnMtMjAyM0BzaGEyNTY6ZTc2YTEzNDc1YzZiNGE3MTNhMGU0YTdhODU3NGNlNDUwMjc0ZDM0MDM1N2EyYzQwYjgyMjFjZmNmZWRmOGIxOT9yZXBvc2l0b3J5X3VybD1sb2NhbGhvc3Q6NTAwMSUyRmRlbW8tcmVmZXJyZXJzLTIwMjNcdTAwMjZhcmNoPWFybTY0IiwibmFtZSI6ImxvY2FsaG9zdDo1MDAxL2RlbW8tcmVmZXJyZXJzLTIwMjM6YXBwIiwicHJvcGVydGllcyI6W3sibmFtZSI6ImFxdWFzZWN1cml0eTp0cml2eTpTY2hlbWFWZXJzaW9uIiwidmFsdWUiOiIyIn1dLCJwdXJsIjoicGtnOm9jaS9kZW1vLXJlZmVycmVycy0yMDIzQHNoYTI1NjplNzZhMTM0NzVjNmI0YTcxM2EwZTRhN2E4NTc0Y2U0NTAyNzRkMzQwMzU3YTJjNDBiODIyMWNmY2ZlZGY4YjE5P3JlcG9zaXRvcnlfdXJsPWxvY2FsaG9zdDo1MDAxJTJGZGVtby1yZWZlcnJlcnMtMjAyM1x1MDAyNmFyY2g9YXJtNjQiLCJ0eXBlIjoiY29udGFpbmVyIn0sInRpbWVzdGFtcCI6IjIwMjMtMDMtMjJUMTE6MzU6MzgrMDA6MDAiLCJ0b29scyI6W3sibmFtZSI6InRyaXZ5IiwidmVuZG9yIjoiYXF1YXNlY3VyaXR5IiwidmVyc2lvbiI6IjAuMzkuMCJ9XX0sInNlcmlhbE51bWJlciI6InVybjp1dWlkOjJlM2M2ODRjLTFmOTktNDY2Yy1hZDZiLWI3ODM5NDJlYjlkMSIsInNwZWNWZXJzaW9uIjoiMS40IiwidmVyc2lvbiI6MSwidnVsbmVyYWJpbGl0aWVzIjpbXX19",
"payloadType": "application/vnd.in-toto+json",
"signatures": [
{
"sig": "MEUCIQCixpYXidhSSGs+WhNVjF6eHiTj6eo0KxF8ihhHlyoHKgIgFv/p/hIhoS/yNTo4Xb/+Ac3fTfd1xNcGB+IJqTNyP/I="
}
]
},
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"verificationMaterial": {
"certificate": {
"rawBytes": "MIIC2zCCAmGgAwIBAgIUC8iVf2cW24GNi2YlrpI5xMh/3TMwCgYIKoZIzj0EAwMwNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRlcm1lZGlhdGUwHhcNMjUwOTIzMDUwMzQyWhcNMjUwOTIzMDUxMzQyWjAAMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAED4Q9U5ucpN780p9nUCiupZSrMpTVQW04/S+AFZVEKkBi3vndyRRXmogyQBDmpFO+ST8NZDBEJCKhEvfg/ZWKZaOCAYAwggF8MA4GA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUtHnkcI9f9YhXO7sRWkqqnKWaMBYwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4YZD8wKwYDVR0RAQH/BCEwH4EddGhvbWFzLmdyaW5pbmdlckByaW5nb2Rldi5jb20wLAYKKwYBBAGDvzABAQQeaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMC4GCisGAQQBg78wAQgEIAweaHR0cHM6Ly9naXRodWIuY29tL2xvZ2luL29hdXRoMIGJBgorBgEEAdZ5AgQCBHsEeQB3AHUA3T0wasbHETJjGR4cmWc3AqJKXrjePK3/h4pygC8p7o4AAAGZdPTFfQAABAMARjBEAiAZakMhKO+tfDvTmISGjfgGaO+Ehj6u1hQPSTBQGpoSPgIgAZeiL6xH76MTSWoYww3EUheSgOwoKIYTMaNh57PqmEAwCgYIKoZIzj0EAwMDaAAwZQIwQBdf+r3InPKcMDBwMjZkdMarevw4IfC0PYGKBf3u0/Y0RK4cyDAuCS4y81is2WoGAjEAnVymG/CTeF4Ff/jqxqIWaMCoKFiJ85NsNi4+NJ7m5qr/UT1LTeuly075bSGHH9zj"
},
"tlogEntries": [
{
"canonicalizedBody": "eyJhcGlWZXJzaW9uIjoiMC4wLjEiLCJraW5kIjoiZHNzZSIsInNwZWMiOnsiZW52ZWxvcGVIYXNoIjp7ImFsZ29yaXRobSI6InNoYTI1NiIsInZhbHVlIjoiZjYxZDkzMjkzMWVjNDliMTNiNTBjODgzZGQ3OTEzMzM3ZmMzZDQ2ZTdiYmI0YTczNDZiYTM4YmJmZmY1MjA1ZiJ9LCJwYXlsb2FkSGFzaCI6eyJhbGdvcml0aG0iOiJzaGEyNTYiLCJ2YWx1ZSI6IjI4OTBmZmEyYmVlODFjYTg4OWQxM2Y3ZjRjNDJiN2NkODc1YTJiYWM4ZDIyODcyYzcyZjJlMjNmZDJhZTkzNWEifSwic2lnbmF0dXJlcyI6W3sic2lnbmF0dXJlIjoiTUVVQ0lRQ2l4cFlYaWRoU1NHcytXaE5WakY2ZUhpVGo2ZW8wS3hGOGloaEhseW9IS2dJZ0Z2L3AvaElob1MveU5UbzRYYi8rQWMzZlRmZDF4TmNHQitJSnFUTnlQL0k9IiwidmVyaWZpZXIiOiJMUzB0TFMxQ1JVZEpUaUJEUlZKVVNVWkpRMEZVUlMwdExTMHRDazFKU1VNeWVrTkRRVzFIWjBGM1NVSkJaMGxWUXpocFZtWXlZMWN5TkVkT2FUSlpiSEp3U1RWNFRXZ3ZNMVJOZDBObldVbExiMXBKZW1vd1JVRjNUWGNLVG5wRlZrMUNUVWRCTVZWRlEyaE5UV015Ykc1ak0xSjJZMjFWZFZwSFZqSk5ValIzU0VGWlJGWlJVVVJGZUZaNllWZGtlbVJIT1hsYVV6RndZbTVTYkFwamJURnNXa2RzYUdSSFZYZElhR05PVFdwVmQwOVVTWHBOUkZWM1RYcFJlVmRvWTA1TmFsVjNUMVJKZWsxRVZYaE5lbEY1VjJwQlFVMUdhM2RGZDFsSUNrdHZXa2w2YWpCRFFWRlpTVXR2V2tsNmFqQkVRVkZqUkZGblFVVkVORkU1VlRWMVkzQk9Oemd3Y0RsdVZVTnBkWEJhVTNKTmNGUldVVmN3TkM5VEswRUtSbHBXUlV0clFta3pkbTVrZVZKU1dHMXZaM2xSUWtSdGNFWlBLMU5VT0U1YVJFSkZTa05MYUVWMlptY3ZXbGRMV21GUFEwRlpRWGRuWjBZNFRVRTBSd3BCTVZWa1JIZEZRaTkzVVVWQmQwbElaMFJCVkVKblRsWklVMVZGUkVSQlMwSm5aM0pDWjBWR1FsRmpSRUY2UVdSQ1owNVdTRkUwUlVablVWVjBTRzVyQ21OSk9XWTVXV2hZVHpkelVsZHJjWEZ1UzFkaFRVSlpkMGgzV1VSV1VqQnFRa0puZDBadlFWVXpPVkJ3ZWpGWmEwVmFZalZ4VG1wd1MwWlhhWGhwTkZrS1drUTRkMHQzV1VSV1VqQlNRVkZJTDBKRFJYZElORVZrWkVkb2RtSlhSbnBNYldSNVlWYzFjR0p0Wkd4amEwSjVZVmMxYm1JeVVteGthVFZxWWpJd2R3cE1RVmxMUzNkWlFrSkJSMFIyZWtGQ1FWRlJaV0ZJVWpCalNFMDJUSGs1Ym1GWVVtOWtWMGwxV1RJNWRFd3llSFphTW14MVRESTVhR1JZVW05TlF6UkhDa05wYzBkQlVWRkNaemM0ZDBGUlowVkpRWGRsWVVoU01HTklUVFpNZVRsdVlWaFNiMlJYU1hWWk1qbDBUREo0ZGxveWJIVk1NamxvWkZoU2IwMUpSMG9LUW1kdmNrSm5SVVZCWkZvMVFXZFJRMEpJYzBWbFVVSXpRVWhWUVROVU1IZGhjMkpJUlZSS2FrZFNOR050VjJNelFYRktTMWh5YW1WUVN6TXZhRFJ3ZVFwblF6aHdOMjgwUVVGQlIxcGtVRlJHWmxGQlFVSkJUVUZTYWtKRlFXbEJXbUZyVFdoTFR5dDBaa1IyVkcxSlUwZHFabWRIWVU4clJXaHFOblV4YUZGUUNsTlVRbEZIY0c5VFVHZEpaMEZhWldsTU5uaElOelpOVkZOWGIxbDNkek5GVldobFUyZFBkMjlMU1ZsVVRXRk9hRFUzVUhGdFJVRjNRMmRaU1V0dldra0tlbW93UlVGM1RVUmhRVUYzV2xGSmQxRkNaR1lyY2pOSmJsQkxZMDFFUW5kTmFscHJaRTFoY21WMmR6Ukpaa013VUZsSFMwSm1NM1V3TDFrd1VrczBZd3A1UkVGMVExTTBlVGd4YVhNeVYyOUhRV3BGUVc1V2VXMUhMME5VWlVZMFJtWXZhbkY0Y1VsWFlVMURiMHRHYVVvNE5VNXpUbWswSzA1S04yMDFjWEl2Q2xWVU1VeFVaWFZzZVRBM05XSlRSMGhJT1hwcUNpMHRMUzB0UlU1RUlFTkZVbFJKUmtsRFFWUkZMUzB0TFMwSyJ9XX19",
"inclusionPromise": {
"signedEntryTimestamp": "MEQCHwyPMnKpy/yUQ0ZwR9gK4a2uckTuTUdLxu4evTPuVd4CIQCzhKnq3MeJbnQ+UKne4pLB+zkvN+e/Feb5EPc8WnyueQ=="
},
"inclusionProof": {
"checkpoint": {
"envelope": "rekor.sigstore.dev - 1193050959916656506\n427690837\nsJPSNHZNtTPycokyg+lqN2e6YlRLlJ57qNvKbfgu4DA=\n\n— rekor.sigstore.dev wNI9ajBFAiEAsJNoQ9+KC5oVw5F8m6TJsMWRICYM/b5GtvdlEUzfKewCIEZ4yKxVTQnS8fOeS1wdxrENfB+ExH45cYvvUElK6lyZ\n"
},
"hashes": [
"LiSPCqC8Ls8vziACm+javgVtDbCunAnG231n36f+rmA=",
"SOInOiumiLsg+YEerz/uDUJxNsOKe+RoCBn66/8CAx0=",
"L7DlG4/e8uoLsciiN10/PQ9Dp11pI20zjdqLZkL+qes=",
"LF7kYuNdnJjUcOI8W65buH30EMIADhu/Kxl5u6ZEDxE=",
"jS89+x6mwua8j9KN5Dv6CO55M78GF8hi8oSIr0Bf2nw=",
"QnOvO+8sDYohFzr5WL8YeAUXNvpPvu8uJ94IvrDkCW0=",
"4ctDWxRuy9Mz4TnkTNS+M6IbMvBeMJ4nOhlt6OKpRjE=",
"CNxxnyEXbd2FPeCFwuWruKKVDmiib5NLbmERibRvCQ0=",
"hfVaV9xEkKbkj2E2/qTT82VHIma+u9NCAFqqsRiinIw=",
"2ZZMmiqKJ0oGmxZHgx7TcGakJLUPNXVLRdD2U48OC7g=",
"E7mN474KbNy0VYJOJx8PWikuCKMn+miLKMG6HAtUY6o=",
"fg+F12WPA6GgbJ0+XKaR8IkjWVPhNcjTcpgtCjk0a9A=",
"rO8wDSOjmY8VkspFqYaJS4TV5HxywICMlHM8gTxXkAA=",
"1mfy94KpcItqshH9+gwqV6jccupcaMpVsF28New8zDY=",
"vS7O4ozHIQZJWBiov+mkpI27GE8zAmVCEkRcP3NDyNE="
],
"logIndex": "427690836",
"rootHash": "sJPSNHZNtTPycokyg+lqN2e6YlRLlJ57qNvKbfgu4DA=",
"treeSize": "427690837"
},
"integratedTime": "1758603823",
"kindVersion": {
"kind": "dsse",
"version": "0.0.1"
},
"logId": {
"keyId": "wNI9atQGlz+VWfO6LRygH4QUfY/8W4RFwiT5i5WRgB0="
},
"logIndex": "549595098"
}
]
}
}
17 changes: 17 additions & 0 deletions pkg/fanal/artifact/image/testdata/sigstore-index.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"manifests": [
{
"annotations": {
"dev.sigstore.bundle.content": "dsse-envelope",
"dev.sigstore.bundle.predicateType": "https://cyclonedx.org/bom",
"org.opencontainers.image.created": "2025-09-23T15:33:31Z"
},
"artifactType": "application/vnd.dev.sigstore.bundle.v0.3+json",
"digest": "sha256:3efec4938089b060058525552ba8bf1888beddf29616e4243b4262792ed3fd2f",
"mediaType": "application/vnd.oci.image.manifest.v1+json",
"size": 878
}
],
"mediaType": "application/vnd.oci.image.index.v1+json",
"schemaVersion": 2
}
4 changes: 4 additions & 0 deletions pkg/oci/artifact.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ const (
CycloneDXArtifactType = "application/vnd.cyclonedx+json"
SPDXArtifactType = "application/spdx+json"

// Sigstore bundle artifact type for Cosign attestations in "new bundle format" > 2.5.0
SigstoreBundleArtifactType = "application/vnd.dev.sigstore.bundle.v0.3+json"

// Media types
OCIImageManifest = "application/vnd.oci.image.manifest.v1+json"

Expand All @@ -40,6 +43,7 @@ const (
var SupportedSBOMArtifactTypes = []string{
CycloneDXArtifactType,
SPDXArtifactType,
SigstoreBundleArtifactType,
}

// Option is a functional option
Expand Down
Loading