Skip to content

Commit 286af41

Browse files
authored
Merge pull request #968 from crazy-max/analyze-tag-digest
handle analysis of image with tag and digest
2 parents 281e28e + 522c8e9 commit 286af41

File tree

9 files changed

+41
-46
lines changed

9 files changed

+41
-46
lines changed

docs/faq.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -333,3 +333,17 @@ Following profilers are available:
333333
* `mutex` enables mutex profiling
334334
* `threads` enables thread creation profiling
335335
* `block` enables block (contention) profiling
336+
337+
## Image with digest and `image:tag@digest` format
338+
339+
Analysis of an image with a digest but without tag will be done using `latest`
340+
as a tag which could lead to false positives.
341+
342+
For example `crazymax/diun@sha256:fa80af32a7c61128ffda667344547805b3c5e7721ecbbafd70e35bb7bb7c989f`
343+
is referring to `crazymax/diun:4.24.0` tag, so it's not correct to assume that
344+
we want to analyze `crazymax/diun:latest`.
345+
346+
You can still pin an image to a specific digest and analyze the image if the
347+
tag is specified using the `image:tag@digest` format. Taking the previous
348+
example if we specify `crazymax/diun:4.24.0@sha256:fa80af32a7c61128ffda667344547805b3c5e7721ecbbafd70e35bb7bb7c989f`,
349+
then `crazymax/diun:4.24.0` will be analyzed.

internal/app/job.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ func (di *Diun) createJob(job model.Job) {
2222
Str("image", job.Image.Name).
2323
Logger()
2424

25-
// Validate image
25+
// Parse image
2626
prvImage, err = registry.ParseImage(registry.ParseImageOptions{
2727
Name: job.Image.Name,
2828
HubTpl: job.Image.HubTpl,

internal/provider/common.go

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,6 @@ var (
1919

2020
// ValidateImage returns a standard image through Docker labels
2121
func ValidateImage(image string, metadata, labels map[string]string, watchByDef bool, imageDefaults model.Image) (img model.Image, err error) {
22-
if i := strings.Index(image, "@sha256:"); i > 0 {
23-
image = image[:i]
24-
}
25-
2622
img = model.Image{
2723
Name: image,
2824
}

internal/provider/common_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,11 @@ func TestValidateImage(t *testing.T) {
2121
expectedErr interface{}
2222
}{
2323
{
24-
name: "Test strip digest",
24+
name: "Test with digest",
2525
image: "myimg@sha256:1234567890abcdef",
2626
watchByDef: true,
2727
expectedImage: model.Image{
28-
Name: "myimg",
28+
Name: "myimg@sha256:1234567890abcdef",
2929
},
3030
expectedErr: nil,
3131
},

pkg/registry/manifest.go

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,15 +35,10 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
3535
return Manifest{}, false, errors.Wrap(err, "cannot parse reference")
3636
}
3737

38-
// Retrieve remote digest through HEAD request or get one from image reference
39-
var rmDigest digest.Digest
40-
if len(image.Digest) > 0 {
41-
rmDigest = image.Digest
42-
} else {
43-
rmDigest, err = docker.GetDigest(ctx, c.sysCtx, rmRef)
44-
if err != nil {
45-
return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
46-
}
38+
// Retrieve remote digest through HEAD request
39+
rmDigest, err := docker.GetDigest(ctx, c.sysCtx, rmRef)
40+
if err != nil {
41+
return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
4742
}
4843

4944
// Digest match, returns db manifest

pkg/registry/manifest_test.go

Lines changed: 2 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -339,7 +339,7 @@ func TestManifestTaggedDigest(t *testing.T) {
339339
assert.Equal(t, "linux/amd64", manifest.Platform)
340340
}
341341

342-
func TestManifestTaggedDigestDummyTag(t *testing.T) {
342+
func TestManifestTaggedDigestUnknownTag(t *testing.T) {
343343
rc, err := New(Options{
344344
CompareDigest: true,
345345
ImageOs: "linux",
@@ -356,19 +356,8 @@ func TestManifestTaggedDigestDummyTag(t *testing.T) {
356356
t.Error(err)
357357
}
358358

359-
// download manifest
360359
_, _, err = rc.Manifest(img, Manifest{})
361-
assert.NoError(t, err)
362-
363-
// check manifest
364-
manifest, updated, err := rc.Manifest(img, manifestCrazymaxDiun4250)
365-
assert.NoError(t, err)
366-
assert.Equal(t, false, updated)
367-
assert.Equal(t, "docker.io/crazymax/diun", manifest.Name)
368-
assert.Equal(t, "latest", manifest.Tag)
369-
assert.Equal(t, "application/vnd.oci.image.index.v1+json", manifest.MIMEType)
370-
assert.Equal(t, "sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031", manifest.Digest.String())
371-
assert.Equal(t, "linux/amd64", manifest.Platform)
360+
assert.Error(t, err)
372361
}
373362

374363
var manifestCrazymaxDiun4250 = Manifest{

pkg/registry/ref.go

Lines changed: 11 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -39,31 +39,28 @@ func namedReference(name string) (reference.Named, error) {
3939
if err != nil {
4040
return nil, errors.Wrapf(err, "normalizing tagged digested name %q", name)
4141
}
42-
return ref, nil
43-
}
44-
if _, hasDigest := ref.(reference.Digested); hasDigest {
45-
return ref, nil
42+
} else if _, hasDigest := ref.(reference.Digested); hasDigest {
43+
ref = reference.TrimNamed(ref)
4644
}
4745

4846
return reference.TagNameOnly(ref), nil
4947
}
5048

51-
// normalizeTaggedDigestedNamed strips the tag off the specified named
52-
// reference if it is tagged and digested. Note that the tag is entirely
53-
// ignored.
49+
// normalizeTaggedDigestedNamed strips the digest off the specified named
50+
// reference if it is tagged and digested.
5451
func normalizeTaggedDigestedNamed(named reference.Named) (reference.Named, error) {
55-
_, isTagged := named.(reference.NamedTagged)
56-
if !isTagged {
52+
_, isDigested := named.(reference.Digested)
53+
if !isDigested {
5754
return named, nil
5855
}
59-
digested, isDigested := named.(reference.Digested)
60-
if !isDigested {
56+
tag, isTagged := named.(reference.NamedTagged)
57+
if !isTagged {
6158
return named, nil
6259
}
63-
// strip off the tag
60+
// strip off the tag and digest
6461
newNamed := reference.TrimNamed(named)
65-
// re-add the digest
66-
newNamed, err := reference.WithDigest(newNamed, digested.Digest())
62+
// re-add the tag
63+
newNamed, err := reference.WithTag(newNamed, tag.Tag())
6764
if err != nil {
6865
return named, err
6966
}

pkg/registry/ref_test.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,15 @@ func TestImageReference(t *testing.T) {
3636
},
3737
{
3838
input: "busybox" + sha256digest,
39-
expected: "docker.io/library/busybox" + sha256digest,
39+
expected: "docker.io/library/busybox:latest",
4040
},
4141
{
4242
input: "busybox:latest" + sha256digest,
43-
expected: "docker.io/library/busybox" + sha256digest,
43+
expected: "docker.io/library/busybox:latest",
44+
},
45+
{
46+
input: "busybox:v1.0.0" + sha256digest,
47+
expected: "docker.io/library/busybox:v1.0.0",
4448
},
4549
{
4650
input: "UPPERCASEISINVALID",

test/dockerfile2/mount/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ FROM alpine:latest
66
# diun.platform=linux/amd64
77
# diun.metadata.foo=bar
88
RUN --mount=type=bind,target=.,rw \
9-
--mount=type=bind,from=crazymax/undock:0.5.0@sha256:736fdfde1268b93c2f733c53a7c45ece24e275318628fbb790bee7f89459961f,source=/usr/local/bin/undock,target=/usr/local/bin/undock \
9+
--mount=type=bind,from=crazymax/undock@sha256:736fdfde1268b93c2f733c53a7c45ece24e275318628fbb790bee7f89459961f,source=/usr/local/bin/undock,target=/usr/local/bin/undock \
1010
undock --version
1111

1212
# diun.platform=linux/amd64

0 commit comments

Comments
 (0)