From cc7eacc28bdc6fffe66ab848785bab2d1ea4d8a0 Mon Sep 17 00:00:00 2001 From: Dmytro Danchenko Date: Thu, 30 Jan 2025 13:55:01 +0200 Subject: [PATCH] fix: parseImageRef tag parsing (#2411) --- pkg/plugins/trivy/plugin.go | 49 +++++-- pkg/plugins/trivy/plugin_test.go | 243 +++++++++++++++++++++++++++++++ 2 files changed, 282 insertions(+), 10 deletions(-) diff --git a/pkg/plugins/trivy/plugin.go b/pkg/plugins/trivy/plugin.go index d3dae0b4c..21808331f 100644 --- a/pkg/plugins/trivy/plugin.go +++ b/pkg/plugins/trivy/plugin.go @@ -4,11 +4,11 @@ import ( "encoding/json" "io" "path/filepath" + "strings" "github.com/aquasecurity/trivy-operator/pkg/exposedsecretreport" "github.com/aquasecurity/trivy-operator/pkg/sbomreport" "github.com/aquasecurity/trivy-operator/pkg/utils" - containerimage "github.com/google/go-containerregistry/pkg/name" "github.com/aquasecurity/trivy-operator/pkg/apis/aquasecurity/v1alpha1" @@ -154,10 +154,10 @@ func (p *plugin) ParseReportData(ctx trivyoperator.PluginContext, imageRef strin return vulnReport, secretReport, nil, err } - registry, artifact, err := p.parseImageRef(imageRef, reports.Metadata.ImageID) + registry, artifact, err := ParseImageRef(imageRef, reports.Metadata.ImageID) if err != nil { return vulnReport, secretReport, nil, err - } + } os := p.parseOSRef(reports) @@ -212,26 +212,55 @@ func (p *plugin) NewConfigForConfigAudit(ctx trivyoperator.PluginContext) (confi return getConfig(ctx) } -func (p *plugin) parseImageRef(imageRef string, imageID string) (v1alpha1.Registry, v1alpha1.Artifact, error) { - ref, err := containerimage.ParseReference(imageRef) +func ParseImageRef(imageRef string, imageID string) (v1alpha1.Registry, v1alpha1.Artifact, error) { + parts := strings.Split(imageRef, "@") + namePart := parts[0] + var hasDigest bool + var hasTag bool + if len(parts) > 1 { + hasDigest = true + } + if len(strings.Split(namePart,":"))>1{ + hasTag = true + } + + ref, err := containerimage.ParseReference(namePart, containerimage.WeakValidation) if err != nil { return v1alpha1.Registry{}, v1alpha1.Artifact{}, err } + registry := v1alpha1.Registry{ Server: ref.Context().RegistryStr(), } artifact := v1alpha1.Artifact{ Repository: ref.Context().RepositoryStr(), } - switch t := ref.(type) { - case containerimage.Tag: - artifact.Tag = t.TagStr() - case containerimage.Digest: - artifact.Digest = t.DigestStr() + + tagged, ok := ref.(containerimage.Tag) + switch { + case ok && hasTag: + artifact.Tag = tagged.TagStr() + case ok && !hasTag && !hasDigest: + artifact.Tag = tagged.TagStr() + case ok && hasTag && hasDigest: + artifact.Tag = tagged.TagStr() } + + if hasDigest { + digestRef, err := containerimage.ParseReference(imageRef, containerimage.WeakValidation) + if err != nil { + return v1alpha1.Registry{}, v1alpha1.Artifact{}, err + } + digested, ok := digestRef.(containerimage.Digest) + if ok { + artifact.Digest = digested.DigestStr() + } + } + if len(artifact.Digest) == 0 { artifact.Digest = imageID } + return registry, artifact, nil } diff --git a/pkg/plugins/trivy/plugin_test.go b/pkg/plugins/trivy/plugin_test.go index 48270d66b..a054945cc 100644 --- a/pkg/plugins/trivy/plugin_test.go +++ b/pkg/plugins/trivy/plugin_test.go @@ -7628,3 +7628,246 @@ func TestExcludeImages(t *testing.T) { }) } } + +func TestParseImageRef(t *testing.T) { + testCases := []struct { + name string + // args: + imageRef string + imageID string + // result: + registry v1alpha1.Registry + artifact v1alpha1.Artifact + err error + }{ + { + name: "1. repo with latest tag", + imageRef: "quay.io/prometheus-operator/prometheus-operator:latest", + imageID: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + registry: v1alpha1.Registry{ + Server: "quay.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "prometheus-operator/prometheus-operator", + Digest: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + Tag: "latest", + }, + err: nil, + }, + { + name: "2. with tag", + imageRef: "quay.io/prometheus-operator/prometheus-operator:v0.63.0", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "quay.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "prometheus-operator/prometheus-operator", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "v0.63.0", + }, + }, + { + name: "3. repo with digest", + imageRef: "quay.io/prometheus-operator/prometheus-operator@sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "quay.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "prometheus-operator/prometheus-operator", + Digest: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + Tag: "", + }, + err: nil, + }, + { + name: "4. incorrect input", + imageRef: "## some incorrect imput ###", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{}, + artifact: v1alpha1.Artifact{}, + err: fmt.Errorf("could not parse reference: ## some incorrect imput ###"), + }, + { + name: "5. short repo with tag", + imageRef: "prometheus-operator:local", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "library/prometheus-operator", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "local", + }, + err: nil, + }, + { + name: "6. short repo with default lib with latest tag", + imageRef: "library/nginx:latest", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "library/nginx", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "latest", + }, + err: nil, + }, + { + name: "7. short repo with private repo with tag", + imageRef: "my-private-repo.company.com/my-app:1.2.3", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "my-private-repo.company.com", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "1.2.3", + }, + err: nil, + }, + { + name: "8. repo with private repo with digest", + imageRef: "my-private-repo.company.com/my-app@sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "my-private-repo.company.com", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + Tag: "", + }, + err: nil, + }, + { + name: "9. short image ref with latest tag", + imageRef: "nginx:latest", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "library/nginx", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "latest", + }, + err: nil, + }, + { + name: "10. artifact registry image ref with tag", + imageRef: "europe-west4-docker.pkg.dev/my-project/my-repo/my-app:1.0.0", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "europe-west4-docker.pkg.dev", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-project/my-repo/my-app", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "1.0.0", + }, + err: nil, + }, + { + name: "11. aws registry image ref with latest tag", + imageRef: "123456789012.dkr.ecr.us-east-1.amazonaws.com/my-app:latest", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "123456789012.dkr.ecr.us-east-1.amazonaws.com", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "latest", + }, + err: nil, + }, + { + name: "12. azure registry image ref with tag", + imageRef: "myregistry.azurecr.io/my-app:v2.0.1", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "myregistry.azurecr.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "v2.0.1", + }, + err: nil, + }, + { + name: "13. docker registry image ref without tag", + imageRef: "docker.io/library/alpine", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "index.docker.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "library/alpine", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "latest", + }, + err: nil, + }, + { + name: "14. private registry image ref with digest", + imageRef: "my-private-repo.company.com/my-app@sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "my-private-repo.company.com", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + Tag: "", + }, + err: nil, + }, + { + name: "15. private registry image ref tag & with digest", + imageRef: "my-private-repo.company.com/my-app:some-tag@sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "my-private-repo.company.com", + }, + artifact: v1alpha1.Artifact{ + Repository: "my-app", + Digest: "sha256:1420cefd4b20014b3361951c22593de6e9a2476bbbadd1759464eab5bfc0d34f", + Tag: "some-tag", + }, + err: nil, + }, + { + name: "16. well known image without tag & digest", + imageRef: "quay.io/centos/centos", + imageID: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + registry: v1alpha1.Registry{ + Server: "quay.io", + }, + artifact: v1alpha1.Artifact{ + Repository: "centos/centos", + Digest: "sha256:2bc57c6bcb194869d18676e003dfed47b87d257fce49667557fb8eb1f324d5d6", + Tag: "latest", + }, + err: nil, + }, + }; + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + registry,artifact,err := trivy.ParseImageRef(tc.imageRef,tc.imageID) + assert.Equal(t, tc.registry, registry) + assert.Equal(t, tc.artifact, artifact) + if tc.err!=nil { + if !assert.Error(t,err) { + assert.Failf(t,"expected","%v but got %v",tc.err,err) + } + } + }) + } +}