Skip to content

Commit 1e1e1b5

Browse files
authored
feat: reject unsupported artifact types in remote image retrieval (#9052)
Signed-off-by: knqyf263 <knqyf263@gmail.com>
1 parent 7333c46 commit 1e1e1b5

2 files changed

Lines changed: 113 additions & 0 deletions

File tree

pkg/fanal/image/remote.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import (
77

88
"github.com/google/go-containerregistry/pkg/name"
99
v1 "github.com/google/go-containerregistry/pkg/v1"
10+
"golang.org/x/xerrors"
1011

1112
"github.com/aquasecurity/trivy/pkg/fanal/types"
1213
"github.com/aquasecurity/trivy/pkg/remote"
@@ -20,6 +21,11 @@ func tryRemote(ctx context.Context, imageName string, ref name.Reference, option
2021
if err != nil {
2122
return nil, cleanup, err
2223
}
24+
// ArtifactType being non-empty indicates this is not a regular container image
25+
// (e.g., Helm charts, WASM modules, or other OCI artifacts)
26+
if desc.ArtifactType != "" {
27+
return nil, cleanup, xerrors.Errorf("unsupported artifact type %q for image %q", desc.ArtifactType, imageName)
28+
}
2329
img, err := desc.Image()
2430
if err != nil {
2531
return nil, cleanup, err

pkg/fanal/image/remote_test.go

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
11
package image
22

33
import (
4+
"io"
5+
"log"
6+
"net/http/httptest"
7+
"net/url"
48
"testing"
59

610
"github.com/google/go-containerregistry/pkg/name"
11+
"github.com/google/go-containerregistry/pkg/registry"
12+
"github.com/google/go-containerregistry/pkg/v1/mutate"
13+
"github.com/google/go-containerregistry/pkg/v1/random"
14+
"github.com/google/go-containerregistry/pkg/v1/remote"
715
"github.com/stretchr/testify/assert"
816
"github.com/stretchr/testify/require"
17+
18+
"github.com/aquasecurity/trivy/pkg/fanal/types"
919
)
1020

1121
func Test_implicitReference_TagName(t *testing.T) {
@@ -82,3 +92,100 @@ func Test_implicitReference_RepositoryName(t *testing.T) {
8292
})
8393
}
8494
}
95+
96+
func Test_tryRemote(t *testing.T) {
97+
// Create a test image
98+
img, err := random.Image(1024, 5)
99+
require.NoError(t, err)
100+
101+
// Get the image digest for test expectations
102+
digest, err := img.Digest()
103+
require.NoError(t, err)
104+
105+
// Set up registry server with null logger to suppress log output
106+
nullLogger := log.New(io.Discard, "", 0)
107+
s := httptest.NewServer(registry.New(registry.Logger(nullLogger)))
108+
t.Cleanup(s.Close)
109+
110+
u, err := url.Parse(s.URL)
111+
require.NoError(t, err)
112+
113+
tests := []struct {
114+
name string
115+
imageName string
116+
setupImage func(t *testing.T, ref name.Reference)
117+
wantName string
118+
wantErr string
119+
}{
120+
{
121+
name: "successful image retrieval",
122+
imageName: "test/alpine:3.10",
123+
setupImage: func(t *testing.T, ref name.Reference) {
124+
err := remote.Write(ref, img)
125+
require.NoError(t, err)
126+
},
127+
wantName: "/test/alpine:3.10",
128+
},
129+
{
130+
name: "helm chart config media type",
131+
imageName: "test/helm:chart",
132+
setupImage: func(t *testing.T, ref name.Reference) {
133+
configFile, err := img.ConfigFile()
134+
require.NoError(t, err)
135+
136+
// Create a new config with helm chart media type
137+
imageToWrite, err := mutate.Config(img, configFile.Config)
138+
require.NoError(t, err)
139+
140+
imageToWrite = mutate.ConfigMediaType(imageToWrite, "application/vnd.cncf.helm.chart")
141+
142+
err = remote.Write(ref, imageToWrite)
143+
require.NoError(t, err)
144+
},
145+
wantErr: "unsupported artifact type",
146+
},
147+
{
148+
name: "image not found",
149+
imageName: "test/notfound:latest",
150+
wantErr: "NAME_UNKNOWN",
151+
setupImage: func(*testing.T, name.Reference) {},
152+
},
153+
}
154+
155+
for _, tt := range tests {
156+
t.Run(tt.name, func(t *testing.T) {
157+
// Parse the image name with the test server address
158+
fullImageName := u.Host + "/" + tt.imageName
159+
ref, err := name.ParseReference(fullImageName)
160+
require.NoError(t, err)
161+
162+
// Set up the image in registry if needed
163+
tt.setupImage(t, ref)
164+
165+
ctx := t.Context()
166+
got, cleanup, err := tryRemote(ctx, fullImageName, ref, types.ImageOptions{
167+
RegistryOptions: types.RegistryOptions{
168+
Insecure: true,
169+
},
170+
})
171+
t.Cleanup(cleanup)
172+
173+
if tt.wantErr != "" {
174+
assert.ErrorContains(t, err, tt.wantErr)
175+
return
176+
}
177+
178+
require.NoError(t, err)
179+
assert.NotNil(t, got)
180+
assert.Contains(t, got.Name(), tt.wantName)
181+
182+
// Verify RepoTags and RepoDigests contain expected values
183+
repoTags := got.RepoTags()
184+
repoDigests := got.RepoDigests()
185+
assert.Len(t, repoTags, 1)
186+
assert.Contains(t, repoTags[0], tt.imageName)
187+
assert.Len(t, repoDigests, 1)
188+
assert.Contains(t, repoDigests[0], digest.String())
189+
})
190+
}
191+
}

0 commit comments

Comments
 (0)