Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

handle analysis of image with tag and digest #968

Merged
merged 1 commit into from
Sep 16, 2023
Merged
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
14 changes: 14 additions & 0 deletions docs/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -333,3 +333,17 @@ Following profilers are available:
* `mutex` enables mutex profiling
* `threads` enables thread creation profiling
* `block` enables block (contention) profiling

## Image with digest and `image:tag@digest` format

Analysis of an image with a digest but without tag will be done using `latest`
as a tag which could lead to false positives.

For example `crazymax/diun@sha256:fa80af32a7c61128ffda667344547805b3c5e7721ecbbafd70e35bb7bb7c989f`
is referring to `crazymax/diun:4.24.0` tag, so it's not correct to assume that
we want to analyze `crazymax/diun:latest`.

You can still pin an image to a specific digest and analyze the image if the
tag is specified using the `image:tag@digest` format. Taking the previous
example if we specify `crazymax/diun:4.24.0@sha256:fa80af32a7c61128ffda667344547805b3c5e7721ecbbafd70e35bb7bb7c989f`,
then `crazymax/diun:4.24.0` will be analyzed.
2 changes: 1 addition & 1 deletion internal/app/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ func (di *Diun) createJob(job model.Job) {
Str("image", job.Image.Name).
Logger()

// Validate image
// Parse image
prvImage, err = registry.ParseImage(registry.ParseImageOptions{
Name: job.Image.Name,
HubTpl: job.Image.HubTpl,
Expand Down
4 changes: 0 additions & 4 deletions internal/provider/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ var (

// ValidateImage returns a standard image through Docker labels
func ValidateImage(image string, metadata, labels map[string]string, watchByDef bool, imageDefaults model.Image) (img model.Image, err error) {
if i := strings.Index(image, "@sha256:"); i > 0 {
image = image[:i]
}

img = model.Image{
Name: image,
}
Expand Down
4 changes: 2 additions & 2 deletions internal/provider/common_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,11 @@ func TestValidateImage(t *testing.T) {
expectedErr interface{}
}{
{
name: "Test strip digest",
name: "Test with digest",
image: "myimg@sha256:1234567890abcdef",
watchByDef: true,
expectedImage: model.Image{
Name: "myimg",
Name: "myimg@sha256:1234567890abcdef",
},
expectedErr: nil,
},
Expand Down
13 changes: 4 additions & 9 deletions pkg/registry/manifest.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,10 @@ func (c *Client) Manifest(image Image, dbManifest Manifest) (Manifest, bool, err
return Manifest{}, false, errors.Wrap(err, "cannot parse reference")
}

// Retrieve remote digest through HEAD request or get one from image reference
var rmDigest digest.Digest
if len(image.Digest) > 0 {
rmDigest = image.Digest
} else {
rmDigest, err = docker.GetDigest(ctx, c.sysCtx, rmRef)
if err != nil {
return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
}
// Retrieve remote digest through HEAD request
rmDigest, err := docker.GetDigest(ctx, c.sysCtx, rmRef)
if err != nil {
return Manifest{}, false, errors.Wrap(err, "cannot get image digest from HEAD request")
}

// Digest match, returns db manifest
Expand Down
15 changes: 2 additions & 13 deletions pkg/registry/manifest_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -339,7 +339,7 @@ func TestManifestTaggedDigest(t *testing.T) {
assert.Equal(t, "linux/amd64", manifest.Platform)
}

func TestManifestTaggedDigestDummyTag(t *testing.T) {
func TestManifestTaggedDigestUnknownTag(t *testing.T) {
rc, err := New(Options{
CompareDigest: true,
ImageOs: "linux",
Expand All @@ -356,19 +356,8 @@ func TestManifestTaggedDigestDummyTag(t *testing.T) {
t.Error(err)
}

// download manifest
_, _, err = rc.Manifest(img, Manifest{})
assert.NoError(t, err)

// check manifest
manifest, updated, err := rc.Manifest(img, manifestCrazymaxDiun4250)
assert.NoError(t, err)
assert.Equal(t, false, updated)
assert.Equal(t, "docker.io/crazymax/diun", manifest.Name)
assert.Equal(t, "latest", manifest.Tag)
assert.Equal(t, "application/vnd.oci.image.index.v1+json", manifest.MIMEType)
assert.Equal(t, "sha256:3fca3dd86c2710586208b0f92d1ec4ce25382f4cad4ae76a2275db8e8bb24031", manifest.Digest.String())
assert.Equal(t, "linux/amd64", manifest.Platform)
assert.Error(t, err)
}

var manifestCrazymaxDiun4250 = Manifest{
Expand Down
25 changes: 11 additions & 14 deletions pkg/registry/ref.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,31 +39,28 @@ func namedReference(name string) (reference.Named, error) {
if err != nil {
return nil, errors.Wrapf(err, "normalizing tagged digested name %q", name)
}
return ref, nil
}
if _, hasDigest := ref.(reference.Digested); hasDigest {
return ref, nil
} else if _, hasDigest := ref.(reference.Digested); hasDigest {
ref = reference.TrimNamed(ref)
}

return reference.TagNameOnly(ref), nil
}

// normalizeTaggedDigestedNamed strips the tag off the specified named
// reference if it is tagged and digested. Note that the tag is entirely
// ignored.
// normalizeTaggedDigestedNamed strips the digest off the specified named
// reference if it is tagged and digested.
func normalizeTaggedDigestedNamed(named reference.Named) (reference.Named, error) {
_, isTagged := named.(reference.NamedTagged)
if !isTagged {
_, isDigested := named.(reference.Digested)
if !isDigested {
return named, nil
}
digested, isDigested := named.(reference.Digested)
if !isDigested {
tag, isTagged := named.(reference.NamedTagged)
if !isTagged {
return named, nil
}
// strip off the tag
// strip off the tag and digest
newNamed := reference.TrimNamed(named)
// re-add the digest
newNamed, err := reference.WithDigest(newNamed, digested.Digest())
// re-add the tag
newNamed, err := reference.WithTag(newNamed, tag.Tag())
if err != nil {
return named, err
}
Expand Down
8 changes: 6 additions & 2 deletions pkg/registry/ref_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,15 @@ func TestImageReference(t *testing.T) {
},
{
input: "busybox" + sha256digest,
expected: "docker.io/library/busybox" + sha256digest,
expected: "docker.io/library/busybox:latest",
},
{
input: "busybox:latest" + sha256digest,
expected: "docker.io/library/busybox" + sha256digest,
expected: "docker.io/library/busybox:latest",
},
{
input: "busybox:v1.0.0" + sha256digest,
expected: "docker.io/library/busybox:v1.0.0",
},
{
input: "UPPERCASEISINVALID",
Expand Down
2 changes: 1 addition & 1 deletion test/dockerfile2/mount/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ FROM alpine:latest
# diun.platform=linux/amd64
# diun.metadata.foo=bar
RUN --mount=type=bind,target=.,rw \
--mount=type=bind,from=crazymax/undock:0.5.0@sha256:736fdfde1268b93c2f733c53a7c45ece24e275318628fbb790bee7f89459961f,source=/usr/local/bin/undock,target=/usr/local/bin/undock \
--mount=type=bind,from=crazymax/undock@sha256:736fdfde1268b93c2f733c53a7c45ece24e275318628fbb790bee7f89459961f,source=/usr/local/bin/undock,target=/usr/local/bin/undock \
undock --version

# diun.platform=linux/amd64
Expand Down