Skip to content

Commit 6556200

Browse files
committed
Add FetchSociArtifacts test
This adds tests to verify that we correctly reject SOCI indexes if they are somehow modified by the registry. We have always done this, but this verifies it via a test that's run continuously. Signed-off-by: Kern Walster <walster@amazon.com>
1 parent 1499076 commit 6556200

File tree

1 file changed

+159
-4
lines changed

1 file changed

+159
-4
lines changed

fs/artifact_fetcher_test.go

Lines changed: 159 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,14 @@ package fs
1919
import (
2020
"bytes"
2121
"context"
22+
"errors"
2223
"fmt"
2324
"io"
2425
"net/http"
2526
"testing"
2627

28+
"github.com/awslabs/soci-snapshotter/soci"
29+
"github.com/awslabs/soci-snapshotter/soci/store"
2730
"github.com/containerd/containerd/reference"
2831
"github.com/google/go-cmp/cmp"
2932
"github.com/opencontainers/go-digest"
@@ -214,6 +217,49 @@ func TestArtifactFetcherFetchOnlyOnce(t *testing.T) {
214217
}
215218
}
216219

220+
func TestArtifactFetcherStore(t *testing.T) {
221+
testCases := []struct {
222+
name string
223+
contents []byte
224+
digest digest.Digest
225+
expectedError error
226+
}{
227+
{
228+
name: "correct digest succeeds on store",
229+
contents: []byte("test"),
230+
digest: digest.FromBytes([]byte("test")),
231+
expectedError: nil,
232+
},
233+
{
234+
name: "incorrect digest fails on store",
235+
contents: []byte("test"),
236+
digest: digest.FromBytes([]byte("different data")),
237+
expectedError: content.ErrMismatchedDigest,
238+
},
239+
}
240+
241+
for _, tc := range testCases {
242+
t.Run(tc.name, func(t *testing.T) {
243+
fetcher, err := newFakeArtifactFetcher(imageRef, tc.contents)
244+
if err != nil {
245+
t.Fatalf("could not create artifact fetcher: %v", err)
246+
}
247+
size := len(tc.contents)
248+
desc := ocispec.Descriptor{
249+
Digest: tc.digest,
250+
Size: int64(size),
251+
}
252+
ctx := context.Background()
253+
254+
err = fetcher.Store(ctx, desc, bytes.NewReader(tc.contents))
255+
if !errors.Is(err, tc.expectedError) {
256+
t.Fatalf("unexpected error, expected = %v, got = %v", tc.expectedError, err)
257+
}
258+
})
259+
}
260+
261+
}
262+
217263
func TestNewRemoteStore(t *testing.T) {
218264
client := http.Client{}
219265
testCases := []struct {
@@ -255,6 +301,80 @@ func TestNewRemoteStore(t *testing.T) {
255301
}
256302
}
257303

304+
func TestFetchSociArtifacts(t *testing.T) {
305+
fakeZtoc := []byte("test data")
306+
fakeZtocDesc := ocispec.Descriptor{
307+
Size: int64(len(fakeZtoc)),
308+
Digest: digest.FromBytes(fakeZtoc),
309+
}
310+
311+
blobs := []ocispec.Descriptor{
312+
{
313+
MediaType: soci.SociLayerMediaType,
314+
Digest: fakeZtocDesc.Digest,
315+
Size: fakeZtocDesc.Size,
316+
},
317+
}
318+
sociIndex := soci.NewIndex(soci.V2, blobs, nil, nil)
319+
sociBytes, err := soci.MarshalIndex(sociIndex)
320+
if err != nil {
321+
t.Fatalf("failed to serialize soci index: %v", err)
322+
}
323+
sociIndexDesc := ocispec.Descriptor{
324+
MediaType: ocispec.MediaTypeImageManifest,
325+
Size: int64(len(sociBytes)),
326+
Digest: digest.FromBytes(sociBytes),
327+
}
328+
329+
modifiedSociIndex := soci.NewIndex(soci.V2, blobs, nil, map[string]string{"a": "b"})
330+
modifiedSociBytes, err := soci.MarshalIndex(modifiedSociIndex)
331+
if err != nil {
332+
t.Fatalf("failed to serialize modified soci index: %v", err)
333+
}
334+
modifiedZtocBytes := []byte("modified test data")
335+
336+
tests := []struct {
337+
name string
338+
remoteContents map[digest.Digest][]byte
339+
expectedError error
340+
}{
341+
{
342+
name: "correct data succeeds",
343+
remoteContents: map[digest.Digest][]byte{
344+
sociIndexDesc.Digest: sociBytes,
345+
fakeZtocDesc.Digest: fakeZtoc,
346+
},
347+
expectedError: nil,
348+
},
349+
{
350+
name: "modified index data fails",
351+
remoteContents: map[digest.Digest][]byte{
352+
sociIndexDesc.Digest: modifiedSociBytes,
353+
fakeZtocDesc.Digest: fakeZtoc,
354+
},
355+
expectedError: content.ErrMismatchedDigest,
356+
},
357+
{
358+
name: "modified ztoc data fails",
359+
remoteContents: map[digest.Digest][]byte{
360+
sociIndexDesc.Digest: sociBytes,
361+
fakeZtocDesc.Digest: modifiedZtocBytes,
362+
},
363+
expectedError: content.ErrTrailingData,
364+
},
365+
}
366+
367+
for _, test := range tests {
368+
t.Run(test.name, func(t *testing.T) {
369+
ctx := context.Background()
370+
_, err = FetchSociArtifacts(ctx, reference.Spec{}, sociIndexDesc, newFakeLocalStore(), newFakeRemoteStoreWithContents(test.remoteContents))
371+
if !errors.Is(err, test.expectedError) {
372+
t.Fatalf("unexpected error, got: %v. expected: %v", err, test.expectedError)
373+
}
374+
})
375+
}
376+
}
377+
258378
func newFakeArtifactFetcher(ref string, contents []byte) (*artifactFetcher, error) {
259379
refspec, err := reference.Parse(ref)
260380
if err != nil {
@@ -263,20 +383,55 @@ func newFakeArtifactFetcher(ref string, contents []byte) (*artifactFetcher, erro
263383
return newArtifactFetcher(refspec, memory.New(), newFakeRemoteStore(contents))
264384
}
265385

386+
func newFakeLocalStore() store.Store {
387+
return &fakeLocalStore{
388+
Store: memory.New(),
389+
}
390+
}
391+
392+
type fakeLocalStore struct {
393+
*memory.Store
394+
}
395+
396+
func (s *fakeLocalStore) BatchOpen(ctx context.Context) (context.Context, store.CleanupFunc, error) {
397+
return ctx, func(ctx context.Context) error { return nil }, nil
398+
}
399+
400+
func (s *fakeLocalStore) Delete(_ context.Context, _ digest.Digest) error {
401+
return nil
402+
}
403+
404+
func (s *fakeLocalStore) Label(_ context.Context, _ ocispec.Descriptor, _, _ string) error {
405+
return nil
406+
}
407+
266408
func newFakeRemoteStore(contents []byte) resolverStorage {
267409
return &fakeRemoteStore{
268-
contents: contents,
410+
defaultContents: contents,
411+
contents: make(map[digest.Digest][]byte),
412+
}
413+
}
414+
415+
func newFakeRemoteStoreWithContents(contents map[digest.Digest][]byte) resolverStorage {
416+
return &fakeRemoteStore{
417+
defaultContents: []byte{},
418+
contents: contents,
269419
}
270420
}
271421

272422
type fakeRemoteStore struct {
273-
contents []byte
423+
defaultContents []byte
424+
contents map[digest.Digest][]byte
274425
}
275426

276427
var _ content.Storage = &fakeRemoteStore{}
277428

278429
func (f *fakeRemoteStore) Fetch(_ context.Context, desc ocispec.Descriptor) (io.ReadCloser, error) {
279-
return io.NopCloser(bytes.NewReader(f.contents)), nil
430+
if data, ok := f.contents[desc.Digest]; ok {
431+
return io.NopCloser(bytes.NewReader(data)), nil
432+
}
433+
434+
return io.NopCloser(bytes.NewReader(f.defaultContents)), nil
280435
}
281436

282437
func (f *fakeRemoteStore) Push(_ context.Context, desc ocispec.Descriptor, ra io.Reader) error {
@@ -289,6 +444,6 @@ func (f *fakeRemoteStore) Exists(_ context.Context, desc ocispec.Descriptor) (bo
289444

290445
func (f *fakeRemoteStore) Resolve(_ context.Context, ref string) (ocispec.Descriptor, error) {
291446
return ocispec.Descriptor{
292-
Size: int64(len(f.contents)),
447+
Size: int64(len(f.defaultContents)),
293448
}, nil
294449
}

0 commit comments

Comments
 (0)