From 1d6ba2fc937e59cbca1406f9bfcf0969b4c8ca25 Mon Sep 17 00:00:00 2001 From: Tom Wieczorek Date: Mon, 9 Dec 2024 16:04:40 +0100 Subject: [PATCH] Verify blobs when downloading OCI artifacts Signed-off-by: Tom Wieczorek --- internal/oci/download.go | 15 ++++++---- internal/oci/testdata/mismatched-digest.yaml | 31 ++++++++++++++++++++ 2 files changed, 41 insertions(+), 5 deletions(-) create mode 100644 internal/oci/testdata/mismatched-digest.yaml diff --git a/internal/oci/download.go b/internal/oci/download.go index e97933d0a233..5d93ef282fed 100644 --- a/internal/oci/download.go +++ b/internal/oci/download.go @@ -89,18 +89,23 @@ func Download(ctx context.Context, url string, target io.Writer, options ...Down return fmt.Errorf("failed to find artifact: %w", err) } - // get a reader to the blob and copies it to the target. - reader, err := repo.Blobs().Fetch(ctx, source) + // Get a reader to the blob. + raw, err := repo.Blobs().Fetch(ctx, source) if err != nil { return fmt.Errorf("failed to fetch blob: %w", err) } - defer reader.Close() + defer func() { err = errors.Join(err, raw.Close()) }() - if _, err := io.Copy(target, reader); err != nil { + // Wrap the reader so that its length and digest will be verified. + verified := content.NewVerifyReader(raw, source) + + // Copy over the blob to its target. + if _, err := io.Copy(target, verified); err != nil { return fmt.Errorf("failed to copy blob: %w", err) } - return nil + // Verify the digest. + return verified.Verify() } // Fetches the manifest for the given reference and returns all of its successors. diff --git a/internal/oci/testdata/mismatched-digest.yaml b/internal/oci/testdata/mismatched-digest.yaml new file mode 100644 index 000000000000..bb61bace3580 --- /dev/null +++ b/internal/oci/testdata/mismatched-digest.yaml @@ -0,0 +1,31 @@ +manifest: | + { + "schemaVersion": 2, + "mediaType": "application/vnd.oci.image.manifest.v1+json", + "artifactType": "application/vnd.unknown.artifact.v1", + "config": { + "mediaType": "application/vnd.oci.empty.v1+json", + "digest": "sha256:44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a", + "size": 2 + }, + "layers": [ + { + "mediaType": "application/vnd.oci.image.layer.v1.tar", + "digest": "sha256:9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08", + "size": 4, + "annotations": { + "org.opencontainers.image.title": "file1" + } + } + ], + "annotations": { + "org.opencontainers.image.created": "2024-10-03T14:32:57Z" + } + } +artifacts: + 44136fa355b3678a1146ad16f7e8649e94fb4fc21fe77e8310c060f61caaff8a: "{}" + 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08: "nope" +authenticated: true +authUser: user +authPass: pass +error: mismatched digest