Skip to content

Commit cd29a67

Browse files
author
Samu
authored
Return index manifest and digest when scanning images (#61)
1 parent 9690004 commit cd29a67

14 files changed

Lines changed: 156 additions & 190 deletions

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ go 1.24
55
toolchain go1.24.2
66

77
require (
8-
github.com/aquasecurity/trivy v0.61.0
8+
github.com/aquasecurity/trivy v0.61.1
99
github.com/containerd/containerd v1.7.27
1010
github.com/docker/docker v27.5.1+incompatible
1111
github.com/docker/go-connections v0.5.0
@@ -69,7 +69,7 @@ require (
6969
github.com/aquasecurity/go-version v0.0.1 // indirect
7070
github.com/aquasecurity/iamgo v0.0.10 // indirect
7171
github.com/aquasecurity/jfather v0.0.8 // indirect
72-
github.com/aquasecurity/trivy-checks v1.8.0 // indirect
72+
github.com/aquasecurity/trivy-checks v1.8.1 // indirect
7373
github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d // indirect
7474
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 // indirect
7575
github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect

go.sum

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -750,10 +750,10 @@ github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8 h1:b43UVqY
750750
github.com/aquasecurity/testdocker v0.0.0-20240730042311-4642e94c7fc8/go.mod h1:wXA9k3uuaxY3yu7gxrxZDPo/04FEMJtwyecdAlYrEIo=
751751
github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gwo=
752752
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
753-
github.com/aquasecurity/trivy v0.61.0 h1:FPRAQ9w6tyAXSNsc6encybNfNoRh4l89oPNmkJm+sgM=
754-
github.com/aquasecurity/trivy v0.61.0/go.mod h1:pZ+6kIfGdyAX7UGr0KF8/fwR1pk36STyaQtLvkHgXLA=
755-
github.com/aquasecurity/trivy-checks v1.8.0 h1:frMR06SEeDff1oEO6wBaTCqZCTBmZ+j8QAAl5EM1M4w=
756-
github.com/aquasecurity/trivy-checks v1.8.0/go.mod h1:zc1DGUFDUP/NUEMXlfaMsnVAEEEsygJrcd4SRQ7Mpko=
753+
github.com/aquasecurity/trivy v0.61.1 h1:Y7Ti5Nm5C2PAlMvPaAW3U/Oe4lQ7717lkjdo0qb+Cyc=
754+
github.com/aquasecurity/trivy v0.61.1/go.mod h1:QaAc1AijI1d9rEyKdepEpbPDyiUan4FL+qykVA/0WdY=
755+
github.com/aquasecurity/trivy-checks v1.8.1 h1:7df8KhZ0du2WAdGCUNcKYdz74iubAmP89+vaCUmxGbU=
756+
github.com/aquasecurity/trivy-checks v1.8.1/go.mod h1:zc1DGUFDUP/NUEMXlfaMsnVAEEEsygJrcd4SRQ7Mpko=
757757
github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d h1:T16WrTi21YsMLQVhtp1r1hOIYK3x4BjnftpL9cp64Eo=
758758
github.com/aquasecurity/trivy-db v0.0.0-20250227071930-8bd8a9b89e2d/go.mod h1:4bTsQPtMBN8v+UfUlE1aQBN1imftefnDafHBF85+aT8=
759759
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=

image/daemon.go

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -4,55 +4,49 @@ import (
44
"context"
55

66
"github.com/castai/image-analyzer/image/daemon"
7+
itypes "github.com/castai/image-analyzer/image/types"
8+
79
"github.com/google/go-containerregistry/pkg/name"
810
)
911

10-
func NewFromContainerdDaemon(ctx context.Context, imageName string) (ImageWithIndex, func(), error) {
12+
func NewFromContainerdDaemon(ctx context.Context, imageName string) (itypes.ImageWithIndex, func(), error) {
1113
img, cleanup, err := daemon.ContainerdImage(ctx, imageName)
1214
if err != nil {
1315
return nil, nil, err
1416
}
1517
return daemonImage{
16-
Image: img,
17-
name: imageName,
18+
ImageWithIndex: img,
19+
name: imageName,
1820
}, cleanup, nil
1921
}
2022

21-
func NewFromDockerDaemon(imageName string, ref name.Reference) (ImageWithIndex, func(), error) {
23+
func NewFromDockerDaemon(imageName string, ref name.Reference) (itypes.ImageWithIndex, func(), error) {
2224
img, cleanup, err := daemon.DockerImage(ref)
2325
if err != nil {
2426
return nil, nil, err
2527
}
2628
return daemonImage{
27-
Image: img,
28-
name: imageName,
29+
ImageWithIndex: img,
30+
name: imageName,
2931
}, cleanup, nil
3032
}
3133

32-
func NewFromDockerDaemonTarFile(imageName, localTarPath string, ref name.Reference) (ImageWithIndex, func(), error) {
34+
func NewFromDockerDaemonTarFile(imageName, localTarPath string, ref name.Reference) (itypes.ImageWithIndex, func(), error) {
3335
img, cleanup, err := daemon.DockerTarImage(ref, localTarPath)
3436
if err != nil {
3537
return nil, nil, err
3638
}
3739
return daemonImage{
38-
Image: img,
39-
name: imageName,
40+
ImageWithIndex: img,
41+
name: imageName,
4042
}, cleanup, nil
4143
}
4244

4345
type daemonImage struct {
44-
daemon.Image
46+
itypes.ImageWithIndex
4547
name string
4648
}
4749

4850
func (d daemonImage) Name() string {
4951
return d.name
5052
}
51-
52-
func (d daemonImage) ID() (string, error) {
53-
return ID(d)
54-
}
55-
56-
func (d daemonImage) LayerIDs() ([]string, error) {
57-
return LayerIDs(d)
58-
}

image/daemon/containerd.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import (
1515
"os"
1616
"time"
1717

18+
itypes "github.com/castai/image-analyzer/image/types"
19+
1820
"github.com/containerd/containerd"
1921
"github.com/containerd/containerd/content"
2022
"github.com/containerd/containerd/images/archive"
@@ -53,7 +55,7 @@ func imageWriter(client *containerd.Client, img containerd.Image) imageSave {
5355
}
5456

5557
// ContainerdImage implements v1.Image
56-
func ContainerdImage(ctx context.Context, imageName string) (Image, func(), error) {
58+
func ContainerdImage(ctx context.Context, imageName string) (itypes.ImageWithIndex, func(), error) {
5759
cleanup := func() {}
5860

5961
addr := os.Getenv("CONTAINERD_ADDRESS")

image/daemon/docker.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import (
1111
"fmt"
1212
"os"
1313

14+
itypes "github.com/castai/image-analyzer/image/types"
15+
1416
"github.com/docker/docker/client"
1517
"github.com/google/go-containerregistry/pkg/name"
1618
v1 "github.com/google/go-containerregistry/pkg/v1"
@@ -19,7 +21,7 @@ import (
1921

2022
// DockerImage implements v1.Image by extending daemon.Image.
2123
// The caller must call cleanup() to remove a temporary file.
22-
func DockerImage(ref name.Reference) (Image, func(), error) {
24+
func DockerImage(ref name.Reference) (itypes.ImageWithIndex, func(), error) {
2325
cleanup := func() {}
2426

2527
c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
@@ -70,7 +72,7 @@ func DockerImage(ref name.Reference) (Image, func(), error) {
7072

7173
// DockerTarImage implements v1.Image by extending daemon.Image.
7274
// The caller must call cleanup() to remove a temporary file.
73-
func DockerTarImage(ref name.Reference, localTarPath string) (Image, func(), error) {
75+
func DockerTarImage(ref name.Reference, localTarPath string) (itypes.ImageWithIndex, func(), error) {
7476
cleanup := func() {}
7577

7678
c, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())

image/daemon/image.go

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,6 @@ import (
2222
"github.com/google/go-containerregistry/pkg/v1/tarball"
2323
)
2424

25-
type Image interface {
26-
v1.Image
27-
RepoTags() []string
28-
RepoDigests() []string
29-
Index() *v1.IndexManifest
30-
}
31-
3225
var mu sync.Mutex
3326

3427
type opener func() (v1.Image, error)
@@ -89,6 +82,14 @@ func (img *image) populateImage() (err error) {
8982
return nil
9083
}
9184

85+
func (img *image) Name() string {
86+
return img.inspect.ID
87+
}
88+
89+
func (img *image) ID() (string, error) {
90+
return img.inspect.ID, nil
91+
}
92+
9293
func (img *image) ConfigName() (v1.Hash, error) {
9394
return v1.NewHash(img.inspect.ID)
9495
}
@@ -100,8 +101,14 @@ func (img *image) Manifest() (*v1.Manifest, error) {
100101
return img.Image.Manifest()
101102
}
102103

103-
func (img *image) Index() *v1.IndexManifest {
104-
return nil
104+
func (img *image) IndexDigest() (v1.Hash, error) {
105+
// index manifest is not available in daemon mode as the image is already pulled
106+
return v1.Hash{}, nil
107+
}
108+
109+
func (img *image) IndexManifest() (*v1.IndexManifest, error) {
110+
// index manifest is not available in daemon mode as the image is already pulled
111+
return nil, nil
105112
}
106113

107114
func (img *image) ConfigFile() (*v1.ConfigFile, error) {

image/hostfs/containerd_image.go

Lines changed: 45 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import (
1212
"path/filepath"
1313
"strings"
1414

15+
itypes "github.com/castai/image-analyzer/image/types"
16+
1517
v1 "github.com/google/go-containerregistry/pkg/v1"
1618
"github.com/google/go-containerregistry/pkg/v1/tarball"
1719
"github.com/google/go-containerregistry/pkg/v1/types"
@@ -24,7 +26,7 @@ const (
2426
blobs = "blobs"
2527
)
2628

27-
func NewContainerdImage(hash v1.Hash, cfg ContainerdHostFSConfig) (Image, error) {
29+
func NewContainerdImage(hash v1.Hash, cfg ContainerdHostFSConfig) (itypes.ImageWithIndex, error) {
2830
metadataReader := newContainerdMetadataReader(hash, cfg)
2931
metadata, err := metadataReader.readMetadata()
3032
if err != nil {
@@ -37,12 +39,13 @@ func NewContainerdImage(hash v1.Hash, cfg ContainerdHostFSConfig) (Image, error)
3739
}
3840

3941
return &containerdBlobImage{
40-
manifest: metadata.Manifest,
41-
index: metadata.Index,
42-
config: config,
43-
configBytes: configBytes,
44-
contentDir: cfg.ContentDir,
45-
imgHash: metadata.Digest,
42+
manifest: metadata.Manifest,
43+
manifestDigest: metadata.ManifestDigest,
44+
index: metadata.Index,
45+
indexDigest: metadata.IndexDigest,
46+
config: config,
47+
configBytes: configBytes,
48+
contentDir: cfg.ContentDir,
4649
}, nil
4750
}
4851

@@ -66,9 +69,10 @@ type ContainerdHostFSConfig struct {
6669
}
6770

6871
type containerdMetadata struct {
69-
Index *v1.IndexManifest
70-
Manifest *v1.Manifest
71-
Digest v1.Hash
72+
Index *v1.IndexManifest
73+
IndexDigest v1.Hash
74+
Manifest *v1.Manifest
75+
ManifestDigest v1.Hash
7276
}
7377

7478
type manifestOrIndex struct {
@@ -119,7 +123,7 @@ func (h *containerdMetadataReader) readMetadata() (*containerdMetadata, error) {
119123
manOrIdx manifestOrIndex
120124
)
121125

122-
metadata.Digest = h.imgHash
126+
metadata.ManifestDigest = h.imgHash
123127
if err := readManifest(
124128
path.Join(h.cfg.ContentDir, blobs, h.imgHash.Algorithm, h.imgHash.Hex), &manOrIdx,
125129
); err != nil {
@@ -138,7 +142,7 @@ func (h *containerdMetadataReader) readMetadata() (*containerdMetadata, error) {
138142
return nil, err
139143
}
140144

141-
metadata.Digest = v1.Hash{
145+
metadata.ManifestDigest = v1.Hash{
142146
Algorithm: "sha256",
143147
Hex: filename,
144148
}
@@ -152,6 +156,7 @@ func (h *containerdMetadataReader) readMetadata() (*containerdMetadata, error) {
152156
// Search manifest from index manifest.
153157
if len(manOrIdx.Manifests) > 0 {
154158
metadata.Index = manOrIdx.index()
159+
metadata.IndexDigest = h.imgHash
155160
for _, manifest := range manOrIdx.Manifests {
156161
if matchingPlatform(h.cfg.Platform, *manifest.Platform) {
157162
if err := readManifest(
@@ -163,7 +168,7 @@ func (h *containerdMetadataReader) readMetadata() (*containerdMetadata, error) {
163168
return nil, errors.New("invalid manifest, no layers")
164169
}
165170
metadata.Manifest = manOrIdx.manifest()
166-
metadata.Digest = manifest.Digest
171+
metadata.ManifestDigest = manifest.Digest
167172
return &metadata, nil
168173
}
169174
}
@@ -225,15 +230,28 @@ func (h *containerdMetadataReader) searchManifestPath() (string, string, error)
225230
}
226231

227232
type containerdBlobImage struct {
228-
manifest *v1.Manifest
229-
index *v1.IndexManifest
230-
config *v1.ConfigFile
231-
configBytes []byte
232-
imgHash v1.Hash
233+
manifest *v1.Manifest
234+
manifestDigest v1.Hash
235+
index *v1.IndexManifest
236+
indexDigest v1.Hash
237+
config *v1.ConfigFile
238+
configBytes []byte
233239

234240
contentDir string
235241
}
236242

243+
func (img *containerdBlobImage) Name() string {
244+
return img.config.Config.Image
245+
}
246+
247+
func (img *containerdBlobImage) ID() (string, error) {
248+
h, err := img.ConfigName()
249+
if err != nil {
250+
return "", err
251+
}
252+
return h.String(), nil
253+
}
254+
237255
func (b *containerdBlobImage) Layers() ([]v1.Layer, error) {
238256
l := make([]v1.Layer, len(b.manifest.Layers))
239257
for i, lay := range b.manifest.Layers {
@@ -259,16 +277,12 @@ func (b *containerdBlobImage) Manifest() (*v1.Manifest, error) {
259277
return b.manifest, nil
260278
}
261279

262-
func (b *containerdBlobImage) Index() *v1.IndexManifest {
263-
return b.index
264-
}
265-
266280
func (b *containerdBlobImage) RawConfigFile() ([]byte, error) {
267281
return b.configBytes, nil
268282
}
269283

270284
func (b *containerdBlobImage) Digest() (v1.Hash, error) {
271-
return b.imgHash, nil
285+
return b.manifestDigest, nil
272286
}
273287

274288
func (b *containerdBlobImage) LayerByDigest(hash v1.Hash) (v1.Layer, error) {
@@ -289,6 +303,14 @@ func (b *containerdBlobImage) LayerByDiffID(hash v1.Hash) (v1.Layer, error) {
289303
return b.LayerByDigest(l.Digest)
290304
}
291305

306+
func (b *containerdBlobImage) IndexDigest() (v1.Hash, error) {
307+
return b.indexDigest, nil
308+
}
309+
310+
func (b *containerdBlobImage) IndexManifest() (*v1.IndexManifest, error) {
311+
return b.index, nil
312+
}
313+
292314
// currently unused
293315

294316
func (b *containerdBlobImage) MediaType() (types.MediaType, error) {

0 commit comments

Comments
 (0)