buildah/0004-fix-CVE-2024-3727.patch
Jianmin 9652104e64 fix CVE-2024-3727 and rebuild to fix CVE-2024-24791
(cherry picked from commit 97ec1b7b0e13c1ee9b68c1f3f1c9a3715d833cba)
2025-01-14 11:58:04 +08:00

1228 lines
60 KiB
Diff
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

From 9c49ca12d641c8d75a0b70ee3f1989b81d760634 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Wed, 17 Jan 2024 23:29:52 +0100
Subject: [PATCH 1/6] Validate digests before using them
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
If doing it makes sense at all, it should happen before
the values are used.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
vendor/github.com/containers/image/v5/storage/storage_dest.go | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/vendor/github.com/containers/image/v5/storage/storage_dest.go b/vendor/github.com/containers/image/v5/storage/storage_dest.go
index 07e1d5e1f9..2a668aa595 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_dest.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_dest.go
@@ -324,6 +324,13 @@ func (s *storageImageDestination) TryReusingBlobWithOptions(ctx context.Context,
// tryReusingBlobAsPending implements TryReusingBlobWithOptions for (digest, size or -1), filling s.blobDiffIDs and other metadata.
// The caller must arrange the blob to be eventually committed using s.commitLayer().
func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest, size int64, options *private.TryReusingBlobOptions) (bool, private.ReusedBlob, error) {
+ if digest == "" {
+ return false, private.ReusedBlob{}, errors.New(`Can not check for a blob with unknown digest`)
+ }
+ if err := digest.Validate(); err != nil {
+ return false, private.ReusedBlob{}, fmt.Errorf("Can not check for a blob with invalid digest: %w", err)
+ }
+
// lock the entire method as it executes fairly quickly
s.lock.Lock()
defer s.lock.Unlock()
@@ -344,13 +351,6 @@ func (s *storageImageDestination) tryReusingBlobAsPending(digest digest.Digest,
}
}
- if digest == "" {
- return false, private.ReusedBlob{}, errors.New(`Can not check for a blob with unknown digest`)
- }
- if err := digest.Validate(); err != nil {
- return false, private.ReusedBlob{}, fmt.Errorf("Can not check for a blob with invalid digest: %w", err)
- }
-
// Check if we've already cached it in a file.
if size, ok := s.fileSizes[digest]; ok {
return true, private.ReusedBlob{
From af94ba17ceb184256966edd7d71b10aad2972156 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Wed, 17 Apr 2024 22:26:46 +0200
Subject: [PATCH 2/6] Call .Validate() before digest.Hex() / digest.Encoded()
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
... to prevent panics if the value does not contain a :, or other unexpected
values (e.g. a path traversal).
Don't bother on paths where we computed the digest ourselves, or it is already trusted
for other reasons.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
vendor/github.com/containers/image/v5/copy/progress_bars.go | 7 +++--
vendor/github.com/containers/image/v5/copy/single.go | 39 ++++++++++++++++++++-------
vendor/github.com/containers/image/v5/directory/directory_dest.go | 22 ++++++++++++---
vendor/github.com/containers/image/v5/directory/directory_src.go | 17 +++++++++---
vendor/github.com/containers/image/v5/directory/directory_transport.go | 25 +++++++++++------
vendor/github.com/containers/image/v5/docker/docker_image_dest.go | 11 +++++---
vendor/github.com/containers/image/v5/docker/docker_image_src.go | 10 +++++--
vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go | 12 +++++++--
vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go | 31 ++++++++++++++++-----
vendor/github.com/containers/image/v5/docker/registries_d.go | 7 +++--
vendor/github.com/containers/image/v5/ostree/ostree_dest.go | 10 +++++++
vendor/github.com/containers/image/v5/ostree/ostree_src.go | 4 ++-
vendor/github.com/containers/image/v5/storage/storage_dest.go | 6 ++++-
vendor/github.com/containers/image/v5/storage/storage_image.go | 7 +++--
vendor/github.com/containers/image/v5/storage/storage_src.go | 9 ++++++-
18 files changed, 210 insertions(+), 59 deletions(-)
diff --git a/vendor/github.com/containers/image/v5/copy/progress_bars.go b/vendor/github.com/containers/image/v5/copy/progress_bars.go
index ce078234cb..ba6a273891 100644
--- a/vendor/github.com/containers/image/v5/copy/progress_bars.go
+++ b/vendor/github.com/containers/image/v5/copy/progress_bars.go
@@ -48,10 +48,13 @@ type progressBar struct {
// As a convention, most users of progress bars should call mark100PercentComplete on full success;
// by convention, we don't leave progress bars in partial state when fully done
// (even if we copied much less data than anticipated).
-func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.BlobInfo, kind string, onComplete string) *progressBar {
+func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.BlobInfo, kind string, onComplete string) (*progressBar, error) {
// shortDigestLen is the length of the digest used for blobs.
const shortDigestLen = 12
+ if err := info.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return nil, err
+ }
prefix := fmt.Sprintf("Copying %s %s", kind, info.Digest.Encoded())
// Truncate the prefix (chopping of some part of the digest) to make all progress bars aligned in a column.
maxPrefixLen := len("Copying blob ") + shortDigestLen
@@ -104,7 +107,7 @@ func (c *copier) createProgressBar(pool *mpb.Progress, partial bool, info types.
return &progressBar{
Bar: bar,
originalSize: info.Size,
- }
+ }, nil
}
// printCopyInfo prints a "Copying ..." message on the copier if the output is
diff --git a/vendor/github.com/containers/image/v5/copy/single.go b/vendor/github.com/containers/image/v5/copy/single.go
index 67ca43f7bc..d36b8542d6 100644
--- a/vendor/github.com/containers/image/v5/copy/single.go
+++ b/vendor/github.com/containers/image/v5/copy/single.go
@@ -599,7 +599,10 @@ func (ic *imageCopier) copyConfig(ctx context.Context, src types.Image) error {
destInfo, err := func() (types.BlobInfo, error) { // A scope for defer
progressPool := ic.c.newProgressPool()
defer progressPool.Wait()
- bar := ic.c.createProgressBar(progressPool, false, srcInfo, "config", "done")
+ bar, err := ic.c.createProgressBar(progressPool, false, srcInfo, "config", "done")
+ if err != nil {
+ return types.BlobInfo{}, err
+ }
defer bar.Abort(false)
ic.c.printCopyInfo("config", srcInfo)
@@ -707,11 +710,17 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
}
if reused {
logrus.Debugf("Skipping blob %s (already present):", srcInfo.Digest)
- func() { // A scope for defer
- bar := ic.c.createProgressBar(pool, false, types.BlobInfo{Digest: reusedBlob.Digest, Size: 0}, "blob", "skipped: already exists")
+ if err := func() error { // A scope for defer
+ bar, err := ic.c.createProgressBar(pool, false, types.BlobInfo{Digest: reusedBlob.Digest, Size: 0}, "blob", "skipped: already exists")
+ if err != nil {
+ return err
+ }
defer bar.Abort(false)
bar.mark100PercentComplete()
- }()
+ return nil
+ }(); err != nil {
+ return types.BlobInfo{}, "", err
+ }
// Throw an event that the layer has been skipped
if ic.c.options.Progress != nil && ic.c.options.ProgressInterval > 0 {
@@ -730,8 +739,11 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
// Attempt a partial only when the source allows to retrieve a blob partially and
// the destination has support for it.
if canAvoidProcessingCompleteLayer && ic.c.rawSource.SupportsGetBlobAt() && ic.c.dest.SupportsPutBlobPartial() {
- if reused, blobInfo := func() (bool, types.BlobInfo) { // A scope for defer
- bar := ic.c.createProgressBar(pool, true, srcInfo, "blob", "done")
+ reused, blobInfo, err := func() (bool, types.BlobInfo, error) { // A scope for defer
+ bar, err := ic.c.createProgressBar(pool, true, srcInfo, "blob", "done")
+ if err != nil {
+ return false, types.BlobInfo{}, err
+ }
hideProgressBar := true
defer func() { // Note that this is not the same as defer bar.Abort(hideProgressBar); we need hideProgressBar to be evaluated lazily.
bar.Abort(hideProgressBar)
@@ -751,18 +763,25 @@ func (ic *imageCopier) copyLayer(ctx context.Context, srcInfo types.BlobInfo, to
bar.mark100PercentComplete()
hideProgressBar = false
logrus.Debugf("Retrieved partial blob %v", srcInfo.Digest)
- return true, updatedBlobInfoFromUpload(srcInfo, uploadedBlob)
+ return true, updatedBlobInfoFromUpload(srcInfo, uploadedBlob), nil
}
logrus.Debugf("Failed to retrieve partial blob: %v", err)
- return false, types.BlobInfo{}
- }(); reused {
+ return false, types.BlobInfo{}, nil
+ }()
+ if err != nil {
+ return types.BlobInfo{}, "", err
+ }
+ if reused {
return blobInfo, cachedDiffID, nil
}
}
// Fallback: copy the layer, computing the diffID if we need to do so
return func() (types.BlobInfo, digest.Digest, error) { // A scope for defer
- bar := ic.c.createProgressBar(pool, false, srcInfo, "blob", "done")
+ bar, err := ic.c.createProgressBar(pool, false, srcInfo, "blob", "done")
+ if err != nil {
+ return types.BlobInfo{}, "", err
+ }
defer bar.Abort(false)
srcStream, srcBlobSize, err := ic.c.rawSource.GetBlob(ctx, srcInfo, ic.c.blobInfoCache)
diff --git a/vendor/github.com/containers/image/v5/directory/directory_dest.go b/vendor/github.com/containers/image/v5/directory/directory_dest.go
index 222723a8f5..d32877e0ca 100644
--- a/vendor/github.com/containers/image/v5/directory/directory_dest.go
+++ b/vendor/github.com/containers/image/v5/directory/directory_dest.go
@@ -173,7 +173,10 @@ func (d *dirImageDestination) PutBlobWithOptions(ctx context.Context, stream io.
}
}
- blobPath := d.ref.layerPath(blobDigest)
+ blobPath, err := d.ref.layerPath(blobDigest)
+ if err != nil {
+ return private.UploadedBlob{}, err
+ }
// need to explicitly close the file, since a rename won't otherwise not work on Windows
blobFile.Close()
explicitClosed = true
@@ -196,7 +199,10 @@ func (d *dirImageDestination) TryReusingBlobWithOptions(ctx context.Context, inf
if info.Digest == "" {
return false, private.ReusedBlob{}, fmt.Errorf("Can not check for a blob with unknown digest")
}
- blobPath := d.ref.layerPath(info.Digest)
+ blobPath, err := d.ref.layerPath(info.Digest)
+ if err != nil {
+ return false, private.ReusedBlob{}, err
+ }
finfo, err := os.Stat(blobPath)
if err != nil && os.IsNotExist(err) {
return false, private.ReusedBlob{}, nil
@@ -216,7 +222,11 @@ func (d *dirImageDestination) TryReusingBlobWithOptions(ctx context.Context, inf
// If the destination is in principle available, refuses this manifest type (e.g. it does not recognize the schema),
// but may accept a different manifest type, the returned error must be an ManifestTypeRejectedError.
func (d *dirImageDestination) PutManifest(ctx context.Context, manifest []byte, instanceDigest *digest.Digest) error {
- return os.WriteFile(d.ref.manifestPath(instanceDigest), manifest, 0644)
+ path, err := d.ref.manifestPath(instanceDigest)
+ if err != nil {
+ return err
+ }
+ return os.WriteFile(path, manifest, 0644)
}
// PutSignaturesWithFormat writes a set of signatures to the destination.
@@ -229,7 +239,11 @@ func (d *dirImageDestination) PutSignaturesWithFormat(ctx context.Context, signa
if err != nil {
return err
}
- if err := os.WriteFile(d.ref.signaturePath(i, instanceDigest), blob, 0644); err != nil {
+ path, err := d.ref.signaturePath(i, instanceDigest)
+ if err != nil {
+ return err
+ }
+ if err := os.WriteFile(path, blob, 0644); err != nil {
return err
}
}
diff --git a/vendor/github.com/containers/image/v5/directory/directory_src.go b/vendor/github.com/containers/image/v5/directory/directory_src.go
index 5fc83bb6f9..6d725bcfaf 100644
--- a/vendor/github.com/containers/image/v5/directory/directory_src.go
+++ b/vendor/github.com/containers/image/v5/directory/directory_src.go
@@ -55,7 +55,11 @@ func (s *dirImageSource) Close() error {
// If instanceDigest is not nil, it contains a digest of the specific manifest instance to retrieve (when the primary manifest is a manifest list);
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
func (s *dirImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
- m, err := os.ReadFile(s.ref.manifestPath(instanceDigest))
+ path, err := s.ref.manifestPath(instanceDigest)
+ if err != nil {
+ return nil, "", err
+ }
+ m, err := os.ReadFile(path)
if err != nil {
return nil, "", err
}
@@ -66,7 +70,11 @@ func (s *dirImageSource) GetManifest(ctx context.Context, instanceDigest *digest
// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location.
func (s *dirImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) {
- r, err := os.Open(s.ref.layerPath(info.Digest))
+ path, err := s.ref.layerPath(info.Digest)
+ if err != nil {
+ return nil, -1, err
+ }
+ r, err := os.Open(path)
if err != nil {
return nil, -1, err
}
@@ -84,7 +92,10 @@ func (s *dirImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache
func (s *dirImageSource) GetSignaturesWithFormat(ctx context.Context, instanceDigest *digest.Digest) ([]signature.Signature, error) {
signatures := []signature.Signature{}
for i := 0; ; i++ {
- path := s.ref.signaturePath(i, instanceDigest)
+ path, err := s.ref.signaturePath(i, instanceDigest)
+ if err != nil {
+ return nil, err
+ }
sigBlob, err := os.ReadFile(path)
if err != nil {
if os.IsNotExist(err) {
diff --git a/vendor/github.com/containers/image/v5/directory/directory_transport.go b/vendor/github.com/containers/image/v5/directory/directory_transport.go
index 7e3068693d..4f7d596b44 100644
--- a/vendor/github.com/containers/image/v5/directory/directory_transport.go
+++ b/vendor/github.com/containers/image/v5/directory/directory_transport.go
@@ -161,25 +161,34 @@ func (ref dirReference) DeleteImage(ctx context.Context, sys *types.SystemContex
}
// manifestPath returns a path for the manifest within a directory using our conventions.
-func (ref dirReference) manifestPath(instanceDigest *digest.Digest) string {
+func (ref dirReference) manifestPath(instanceDigest *digest.Digest) (string, error) {
if instanceDigest != nil {
- return filepath.Join(ref.path, instanceDigest.Encoded()+".manifest.json")
+ if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly.
+ return "", err
+ }
+ return filepath.Join(ref.path, instanceDigest.Encoded()+".manifest.json"), nil
}
- return filepath.Join(ref.path, "manifest.json")
+ return filepath.Join(ref.path, "manifest.json"), nil
}
// layerPath returns a path for a layer tarball within a directory using our conventions.
-func (ref dirReference) layerPath(digest digest.Digest) string {
+func (ref dirReference) layerPath(digest digest.Digest) (string, error) {
+ if err := digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly.
+ return "", err
+ }
// FIXME: Should we keep the digest identification?
- return filepath.Join(ref.path, digest.Encoded())
+ return filepath.Join(ref.path, digest.Encoded()), nil
}
// signaturePath returns a path for a signature within a directory using our conventions.
-func (ref dirReference) signaturePath(index int, instanceDigest *digest.Digest) string {
+func (ref dirReference) signaturePath(index int, instanceDigest *digest.Digest) (string, error) {
if instanceDigest != nil {
- return filepath.Join(ref.path, fmt.Sprintf(instanceDigest.Encoded()+".signature-%d", index+1))
+ if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, and could possibly result in a path with ../, so validate explicitly.
+ return "", err
+ }
+ return filepath.Join(ref.path, fmt.Sprintf(instanceDigest.Encoded()+".signature-%d", index+1)), nil
}
- return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1))
+ return filepath.Join(ref.path, fmt.Sprintf("signature-%d", index+1)), nil
}
// versionPath returns a path for the version file within a directory using our conventions.
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
index a9a36f0a34..682954fd89 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
@@ -632,11 +632,13 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures []signature
// NOTE: Keep this in sync with docs/signature-protocols.md!
for i, signature := range signatures {
- sigURL := lookasideStorageURL(d.c.signatureBase, manifestDigest, i)
- err := d.putOneSignature(sigURL, signature)
+ sigURL, err := lookasideStorageURL(d.c.signatureBase, manifestDigest, i)
if err != nil {
return err
}
+ if err := d.putOneSignature(sigURL, signature); err != nil {
+ return err
+ }
}
// Remove any other signatures, if present.
// We stop at the first missing signature; if a previous deleting loop aborted
@@ -644,7 +646,10 @@ func (d *dockerImageDestination) putSignaturesToLookaside(signatures []signature
// is enough for dockerImageSource to stop looking for other signatures, so that
// is sufficient.
for i := len(signatures); ; i++ {
- sigURL := lookasideStorageURL(d.c.signatureBase, manifestDigest, i)
+ sigURL, err := lookasideStorageURL(d.c.signatureBase, manifestDigest, i)
+ if err != nil {
+ return err
+ }
missing, err := d.c.deleteOneSignature(sigURL)
if err != nil {
return err
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go
index f9d4d6030f..b78b9b5163 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go
@@ -462,7 +462,10 @@ func (s *dockerImageSource) getSignaturesFromLookaside(ctx context.Context, inst
return nil, fmt.Errorf("server provided %d signatures, assuming that's unreasonable and a server error", maxLookasideSignatures)
}
- sigURL := lookasideStorageURL(s.c.signatureBase, manifestDigest, i)
+ sigURL, err := lookasideStorageURL(s.c.signatureBase, manifestDigest, i)
+ if err != nil {
+ return nil, err
+ }
signature, missing, err := s.getOneSignature(ctx, sigURL)
if err != nil {
return nil, err
@@ -660,7 +663,10 @@ func deleteImage(ctx context.Context, sys *types.SystemContext, ref dockerRefere
}
for i := 0; ; i++ {
- sigURL := lookasideStorageURL(c.signatureBase, manifestDigest, i)
+ sigURL, err := lookasideStorageURL(c.signatureBase, manifestDigest, i)
+ if err != nil {
+ return err
+ }
missing, err := c.deleteOneSignature(sigURL)
if err != nil {
return err
diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go
index 7507d85595..106490cb39 100644
--- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go
+++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/dest.go
@@ -111,11 +111,19 @@ func (d *Destination) PutBlobWithOptions(ctx context.Context, stream io.Reader,
return private.UploadedBlob{}, fmt.Errorf("reading Config file stream: %w", err)
}
d.config = buf
- if err := d.archive.sendFileLocked(d.archive.configPath(inputInfo.Digest), inputInfo.Size, bytes.NewReader(buf)); err != nil {
+ configPath, err := d.archive.configPath(inputInfo.Digest)
+ if err != nil {
+ return private.UploadedBlob{}, err
+ }
+ if err := d.archive.sendFileLocked(configPath, inputInfo.Size, bytes.NewReader(buf)); err != nil {
return private.UploadedBlob{}, fmt.Errorf("writing Config file: %w", err)
}
} else {
- if err := d.archive.sendFileLocked(d.archive.physicalLayerPath(inputInfo.Digest), inputInfo.Size, stream); err != nil {
+ layerPath, err := d.archive.physicalLayerPath(inputInfo.Digest)
+ if err != nil {
+ return private.UploadedBlob{}, err
+ }
+ if err := d.archive.sendFileLocked(layerPath, inputInfo.Size, stream); err != nil {
return private.UploadedBlob{}, err
}
}
diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
index df7b2c0906..2d83254d78 100644
--- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
+++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
@@ -95,7 +95,10 @@ func (w *Writer) ensureSingleLegacyLayerLocked(layerID string, layerDigest diges
if !w.legacyLayers.Contains(layerID) {
// Create a symlink for the legacy format, where there is one subdirectory per layer ("image").
// See also the comment in physicalLayerPath.
- physicalLayerPath := w.physicalLayerPath(layerDigest)
+ physicalLayerPath, err := w.physicalLayerPath(layerDigest)
+ if err != nil {
+ return err
+ }
if err := w.sendSymlinkLocked(filepath.Join(layerID, legacyLayerFileName), filepath.Join("..", physicalLayerPath)); err != nil {
return fmt.Errorf("creating layer symbolic link: %w", err)
}
@@ -204,12 +207,20 @@ func checkManifestItemsMatch(a, b *ManifestItem) error {
func (w *Writer) ensureManifestItemLocked(layerDescriptors []manifest.Schema2Descriptor, configDigest digest.Digest, repoTags []reference.NamedTagged) error {
layerPaths := []string{}
for _, l := range layerDescriptors {
- layerPaths = append(layerPaths, w.physicalLayerPath(l.Digest))
+ p, err := w.physicalLayerPath(l.Digest)
+ if err != nil {
+ return err
+ }
+ layerPaths = append(layerPaths, p)
}
var item *ManifestItem
+ configPath, err := w.configPath(configDigest)
+ if err != nil {
+ return err
+ }
newItem := ManifestItem{
- Config: w.configPath(configDigest),
+ Config: configPath,
RepoTags: []string{},
Layers: layerPaths,
Parent: "", // We dont have this information
@@ -294,21 +305,27 @@ func (w *Writer) Close() error {
// configPath returns a path we choose for storing a config with the specified digest.
// NOTE: This is an internal implementation detail, not a format property, and can change
// any time.
-func (w *Writer) configPath(configDigest digest.Digest) string {
- return configDigest.Hex() + ".json"
+func (w *Writer) configPath(configDigest digest.Digest) (string, error) {
+ if err := configDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in unexpected paths, so validate explicitly.
+ return "", err
+ }
+ return configDigest.Hex() + ".json", nil
}
// physicalLayerPath returns a path we choose for storing a layer with the specified digest
// (the actual path, i.e. a regular file, not a symlink that may be used in the legacy format).
// NOTE: This is an internal implementation detail, not a format property, and can change
// any time.
-func (w *Writer) physicalLayerPath(layerDigest digest.Digest) string {
+func (w *Writer) physicalLayerPath(layerDigest digest.Digest) (string, error) {
+ if err := layerDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in unexpected paths, so validate explicitly.
+ return "", err
+ }
// Note that this can't be e.g. filepath.Join(l.Digest.Hex(), legacyLayerFileName); due to the way
// writeLegacyMetadata constructs layer IDs differently from inputinfo.Digest values (as described
// inside it), most of the layers would end up in subdirectories alone without any metadata; (docker load)
// tries to load every subdirectory as an image and fails if the config is missing. So, keep the layers
// in the root of the tarball.
- return layerDigest.Hex() + ".tar"
+ return layerDigest.Hex() + ".tar", nil
}
type tarFI struct {
diff --git a/vendor/github.com/containers/image/v5/docker/registries_d.go b/vendor/github.com/containers/image/v5/docker/registries_d.go
index c7b884ab3c..9d651d9bd2 100644
--- a/vendor/github.com/containers/image/v5/docker/registries_d.go
+++ b/vendor/github.com/containers/image/v5/docker/registries_d.go
@@ -286,8 +286,11 @@ func (ns registryNamespace) signatureTopLevel(write bool) string {
// lookasideStorageURL returns an URL usable for accessing signature index in base with known manifestDigest.
// base is not nil from the caller
// NOTE: Keep this in sync with docs/signature-protocols.md!
-func lookasideStorageURL(base lookasideStorageBase, manifestDigest digest.Digest, index int) *url.URL {
+func lookasideStorageURL(base lookasideStorageBase, manifestDigest digest.Digest, index int) (*url.URL, error) {
+ if err := manifestDigest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, and could possibly result in a path with ../, so validate explicitly.
+ return nil, err
+ }
sigURL := *base
sigURL.Path = fmt.Sprintf("%s@%s=%s/signature-%d", sigURL.Path, manifestDigest.Algorithm(), manifestDigest.Hex(), index+1)
- return &sigURL
+ return &sigURL, nil
}
diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go
index d00a0cdf86..29177f11a3 100644
--- a/vendor/github.com/containers/image/v5/ostree/ostree_dest.go
+++ b/vendor/github.com/containers/image/v5/ostree/ostree_dest.go
@@ -345,6 +345,10 @@ func (d *ostreeImageDestination) TryReusingBlobWithOptions(ctx context.Context,
}
d.repo = repo
}
+
+ if err := info.Digest.Validate(); err != nil { // digest.Digest.Hex() panics on failure, so validate explicitly.
+ return false, private.ReusedBlob{}, err
+ }
branch := fmt.Sprintf("ociimage/%s", info.Digest.Hex())
found, data, err := readMetadata(d.repo, branch, "docker.uncompressed_digest")
@@ -470,12 +474,18 @@ func (d *ostreeImageDestination) Commit(context.Context, types.UnparsedImage) er
return nil
}
for _, layer := range d.schema.LayersDescriptors {
+ if err := layer.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return err
+ }
hash := layer.Digest.Hex()
if err = checkLayer(hash); err != nil {
return err
}
}
for _, layer := range d.schema.FSLayers {
+ if err := layer.BlobSum.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return err
+ }
hash := layer.BlobSum.Hex()
if err = checkLayer(hash); err != nil {
return err
diff --git a/vendor/github.com/containers/image/v5/ostree/ostree_src.go b/vendor/github.com/containers/image/v5/ostree/ostree_src.go
index 9983acc0a6..a9568c2d32 100644
--- a/vendor/github.com/containers/image/v5/ostree/ostree_src.go
+++ b/vendor/github.com/containers/image/v5/ostree/ostree_src.go
@@ -286,7 +286,9 @@ func (s *ostreeImageSource) readSingleFile(commit, path string) (io.ReadCloser,
// The Digest field in BlobInfo is guaranteed to be provided, Size may be -1 and MediaType may be optionally provided.
// May update BlobInfoCache, preferably after it knows for certain that a blob truly exists at a specific location.
func (s *ostreeImageSource) GetBlob(ctx context.Context, info types.BlobInfo, cache types.BlobInfoCache) (io.ReadCloser, int64, error) {
-
+ if err := info.Digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return nil, -1, err
+ }
blob := info.Digest.Hex()
// Ensure s.compressed is initialized. It is build by LayerInfosForCopy.
diff --git a/vendor/github.com/containers/image/v5/storage/storage_dest.go b/vendor/github.com/containers/image/v5/storage/storage_dest.go
index 2a668aa595..b65be2e147 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_dest.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_dest.go
@@ -831,8 +831,12 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t
})
}
for instanceDigest, signatures := range s.signatureses {
+ key, err := signatureBigDataKey(instanceDigest)
+ if err != nil {
+ return err
+ }
options.BigData = append(options.BigData, storage.ImageBigDataOption{
- Key: signatureBigDataKey(instanceDigest),
+ Key: key,
Data: signatures,
Digest: digest.Canonical.FromBytes(signatures),
})
diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go
index ac09f3dbbc..b734c41291 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_image.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_image.go
@@ -32,8 +32,11 @@ func manifestBigDataKey(digest digest.Digest) string {
// signatureBigDataKey returns a key suitable for recording the signatures associated with the manifest with the specified digest using storage.Store.ImageBigData and related functions.
// If a specific manifest digest is explicitly requested by the user, the key returned by this function should be used preferably;
-func signatureBigDataKey(digest digest.Digest) string {
- return "signature-" + digest.Encoded()
+func signatureBigDataKey(digest digest.Digest) (string, error) {
+ if err := digest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return "", err
+ }
+ return "signature-" + digest.Encoded(), nil
}
// Size() returns the previously-computed size of the image, with no error.
diff --git a/vendor/github.com/containers/image/v5/storage/storage_src.go b/vendor/github.com/containers/image/v5/storage/storage_src.go
index f1ce0861e0..29cce56355 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_src.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_src.go
@@ -329,7 +329,14 @@ func (s *storageImageSource) GetSignaturesWithFormat(ctx context.Context, instan
instance := "default instance"
if instanceDigest != nil {
signatureSizes = s.SignaturesSizes[*instanceDigest]
- key = signatureBigDataKey(*instanceDigest)
+ k, err := signatureBigDataKey(*instanceDigest)
+ if err != nil {
+ return nil, err
+ }
+ key = k
+ if err := instanceDigest.Validate(); err != nil { // digest.Digest.Encoded() panics on failure, so validate explicitly.
+ return nil, err
+ }
instance = instanceDigest.Encoded()
}
if len(signatureSizes) > 0 {
From 7b58b430ec8e64876d9b9dbd81989666293ffe87 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Thu, 18 Apr 2024 00:39:54 +0200
Subject: [PATCH 3/6] Refactor the error handling path of saveStream
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use defer() to remove the temporary file, instead
of duplicating the call.
Should not change behavior.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
pkg/blobcache/dest.go | 51 ++++++++++++++++++++++++-------------------
1 file changed, 28 insertions(+), 23 deletions(-)
diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
index 9bda085158..ac07cd3f2a 100644
--- a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
+++ b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
@@ -80,6 +80,17 @@ func (d *blobCacheDestination) IgnoresEmbeddedDockerReference() bool {
// and this new file.
func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os.File, compressedFilename string, compressedDigest digest.Digest, isConfig bool, alternateDigest *digest.Digest) {
defer wg.Done()
+
+ succeeded := false
+ defer func() {
+ if !succeeded {
+ // Remove the temporary file.
+ if err := os.Remove(tempFile.Name()); err != nil {
+ logrus.Debugf("error cleaning up temporary file %q for decompressed copy of blob %q: %v", tempFile.Name(), compressedDigest.String(), err)
+ }
+ }
+ }()
+
// Decompress from and digest the reading end of that pipe.
decompressed, err3 := archive.DecompressStream(decompressReader)
digester := digest.Canonical.Digester()
@@ -96,31 +107,25 @@ func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader i
decompressReader.Close()
decompressed.Close()
tempFile.Close()
+
// Determine the name that we should give to the uncompressed copy of the blob.
decompressedFilename := d.reference.blobPath(digester.Digest(), isConfig)
- if err3 == nil {
- // Rename the temporary file.
- if err3 = os.Rename(tempFile.Name(), decompressedFilename); err3 != nil {
- logrus.Debugf("error renaming new decompressed copy of blob %q into place at %q: %v", digester.Digest().String(), decompressedFilename, err3)
- // Remove the temporary file.
- if err3 = os.Remove(tempFile.Name()); err3 != nil {
- logrus.Debugf("error cleaning up temporary file %q for decompressed copy of blob %q: %v", tempFile.Name(), compressedDigest.String(), err3)
- }
- } else {
- *alternateDigest = digester.Digest()
- // Note the relationship between the two files.
- if err3 = ioutils.AtomicWriteFile(decompressedFilename+compressedNote, []byte(compressedDigest.String()), 0600); err3 != nil {
- logrus.Debugf("error noting that the compressed version of %q is %q: %v", digester.Digest().String(), compressedDigest.String(), err3)
- }
- if err3 = ioutils.AtomicWriteFile(compressedFilename+decompressedNote, []byte(digester.Digest().String()), 0600); err3 != nil {
- logrus.Debugf("error noting that the decompressed version of %q is %q: %v", compressedDigest.String(), digester.Digest().String(), err3)
- }
- }
- } else {
- // Remove the temporary file.
- if err3 = os.Remove(tempFile.Name()); err3 != nil {
- logrus.Debugf("error cleaning up temporary file %q for decompressed copy of blob %q: %v", tempFile.Name(), compressedDigest.String(), err3)
- }
+ if err3 != nil {
+ return
+ }
+ // Rename the temporary file.
+ if err := os.Rename(tempFile.Name(), decompressedFilename); err != nil {
+ logrus.Debugf("error renaming new decompressed copy of blob %q into place at %q: %v", digester.Digest().String(), decompressedFilename, err)
+ return
+ }
+ succeeded = true
+ *alternateDigest = digester.Digest()
+ // Note the relationship between the two files.
+ if err := ioutils.AtomicWriteFile(decompressedFilename+compressedNote, []byte(compressedDigest.String()), 0600); err != nil {
+ logrus.Debugf("error noting that the compressed version of %q is %q: %v", digester.Digest().String(), compressedDigest.String(), err)
+ }
+ if err := ioutils.AtomicWriteFile(compressedFilename+decompressedNote, []byte(digester.Digest().String()), 0600); err != nil {
+ logrus.Debugf("error noting that the decompressed version of %q is %q: %v", compressedDigest.String(), digester.Digest().String(), err)
}
}
From 0860c58afa8561e31ab5bddbc75d193a7305622e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Thu, 18 Apr 2024 00:47:09 +0200
Subject: [PATCH 4/6] Refactor the error handling further
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Use defer, a nested function, and early returns.
Besides being a bit more directly related to what
we want to achieve, this now does not call decompressed.Close()
on a nil value if DecompressStream fails.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
pkg/blobcache/dest.go | 33 ++++++++++++++++++---------------
1 file changed, 18 insertions(+), 15 deletions(-)
diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
index ac07cd3f2a..fde95974e9 100644
--- a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
+++ b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
@@ -80,6 +80,7 @@ func (d *blobCacheDestination) IgnoresEmbeddedDockerReference() bool {
// and this new file.
func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader io.ReadCloser, tempFile *os.File, compressedFilename string, compressedDigest digest.Digest, isConfig bool, alternateDigest *digest.Digest) {
defer wg.Done()
+ defer decompressReader.Close()
succeeded := false
defer func() {
@@ -91,28 +92,30 @@ func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader i
}
}()
- // Decompress from and digest the reading end of that pipe.
- decompressed, err3 := archive.DecompressStream(decompressReader)
digester := digest.Canonical.Digester()
- if err3 == nil {
+ if err := func() error { // A scope for defer
+ defer tempFile.Close()
+
+ // Decompress from and digest the reading end of that pipe.
+ decompressed, err := archive.DecompressStream(decompressReader)
+ if err != nil {
+ // Drain the pipe to keep from stalling the PutBlob() thread.
+ if _, err2 := io.Copy(io.Discard, decompressReader); err2 != nil {
+ logrus.Debugf("error draining the pipe: %v", err2)
+ }
+ return err
+ }
+ defer decompressed.Close()
// Read the decompressed data through the filter over the pipe, blocking until the
// writing end is closed.
- _, err3 = io.Copy(io.MultiWriter(tempFile, digester.Hash()), decompressed)
- } else {
- // Drain the pipe to keep from stalling the PutBlob() thread.
- if _, err := io.Copy(io.Discard, decompressReader); err != nil {
- logrus.Debugf("error draining the pipe: %v", err)
- }
+ _, err = io.Copy(io.MultiWriter(tempFile, digester.Hash()), decompressed)
+ return err
+ }(); err != nil {
+ return
}
- decompressReader.Close()
- decompressed.Close()
- tempFile.Close()
// Determine the name that we should give to the uncompressed copy of the blob.
decompressedFilename := d.reference.blobPath(digester.Digest(), isConfig)
- if err3 != nil {
- return
- }
// Rename the temporary file.
if err := os.Rename(tempFile.Name(), decompressedFilename); err != nil {
logrus.Debugf("error renaming new decompressed copy of blob %q into place at %q: %v", digester.Digest().String(), decompressedFilename, err)
From 086c7606314e6fa7638c36147a0e514c77a12008 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Thu, 18 Apr 2024 00:14:38 +0200
Subject: [PATCH 5/6] Call .Validate() before digest.Digest.String() if
necessary
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
... to prevent unexpected behavior on invalid values.
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
docker/docker_client.go | 20 +++++++++++++++++---
docker/docker_image_dest.go | 11 ++++++++++-
docker/docker_image_src.go | 8 ++++++++
docker/internal/tarfile/writer.go | 3 +++
openshift/openshift_src.go | 3 +++
pkg/blobcache/blobcache.go | 12 +++++++++---
pkg/blobcache/dest.go | 15 ++++++++++++---
pkg/blobcache/src.go | 16 ++++++++++++----
storage/storage_dest.go | 12 ++++++++++--
storage/storage_image.go | 7 +++++--
storage/storage_reference.go | 10 ++++++++--
storage/storage_src.go | 10 ++++++++--
12 files changed, 105 insertions(+), 22 deletions(-)
diff --git a/vendor/github.com/containers/image/v5/docker/docker_client.go b/vendor/github.com/containers/image/v5/docker/docker_client.go
index 6ce8f70083..d03f87a02e 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_client.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_client.go
@@ -952,6 +952,8 @@ func (c *dockerClient) detectProperties(ctx context.Context) error {
return c.detectPropertiesError
}
+// fetchManifest fetches a manifest for (the repo of ref) + tagOrDigest.
+// The caller is responsible for ensuring tagOrDigest uses the expected format.
func (c *dockerClient) fetchManifest(ctx context.Context, ref dockerReference, tagOrDigest string) ([]byte, string, error) {
path := fmt.Sprintf(manifestPath, reference.Path(ref.ref), tagOrDigest)
headers := map[string][]string{
@@ -1034,6 +1036,9 @@ func (c *dockerClient) getBlob(ctx context.Context, ref dockerReference, info ty
}
}
+ if err := info.Digest.Validate(); err != nil { // Make sure info.Digest.String() does not contain any unexpected characters
+ return nil, 0, err
+ }
path := fmt.Sprintf(blobsPath, reference.Path(ref.ref), info.Digest.String())
logrus.Debugf("Downloading %s", path)
res, err := c.makeRequest(ctx, http.MethodGet, path, nil, nil, v2Auth, nil)
@@ -1097,7 +1102,10 @@ func isManifestUnknownError(err error) bool {
// digest in ref.
// It returns (nil, nil) if the manifest does not exist.
func (c *dockerClient) getSigstoreAttachmentManifest(ctx context.Context, ref dockerReference, digest digest.Digest) (*manifest.OCI1, error) {
- tag := sigstoreAttachmentTag(digest)
+ tag, err := sigstoreAttachmentTag(digest)
+ if err != nil {
+ return nil, err
+ }
sigstoreRef, err := reference.WithTag(reference.TrimNamed(ref.ref), tag)
if err != nil {
return nil, err
@@ -1130,6 +1138,9 @@ func (c *dockerClient) getSigstoreAttachmentManifest(ctx context.Context, ref do
// getExtensionsSignatures returns signatures from the X-Registry-Supports-Signatures API extension,
// using the original data structures.
func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerReference, manifestDigest digest.Digest) (*extensionSignatureList, error) {
+ if err := manifestDigest.Validate(); err != nil { // Make sure manifestDigest.String() does not contain any unexpected characters
+ return nil, err
+ }
path := fmt.Sprintf(extensionsSignaturePath, reference.Path(ref.ref), manifestDigest)
res, err := c.makeRequest(ctx, http.MethodGet, path, nil, nil, v2Auth, nil)
if err != nil {
@@ -1153,8 +1164,11 @@ func (c *dockerClient) getExtensionsSignatures(ctx context.Context, ref dockerRe
}
// sigstoreAttachmentTag returns a sigstore attachment tag for the specified digest.
-func sigstoreAttachmentTag(d digest.Digest) string {
- return strings.Replace(d.String(), ":", "-", 1) + ".sig"
+func sigstoreAttachmentTag(d digest.Digest) (string, error) {
+ if err := d.Validate(); err != nil { // Make sure d.String() doesnt contain any unexpected characters
+ return "", err
+ }
+ return strings.Replace(d.String(), ":", "-", 1) + ".sig", nil
}
// Close removes resources associated with an initialized dockerClient, if any.
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
index 682954fd89..0c0505a8d9 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image_dest.go
@@ -229,6 +229,9 @@ func (d *dockerImageDestination) PutBlobWithOptions(ctx context.Context, stream
// If the destination does not contain the blob, or it is unknown, blobExists ordinarily returns (false, -1, nil);
// it returns a non-nil error only on an unexpected failure.
func (d *dockerImageDestination) blobExists(ctx context.Context, repo reference.Named, digest digest.Digest, extraScope *authScope) (bool, int64, error) {
+ if err := digest.Validate(); err != nil { // Make sure digest.String() does not contain any unexpected characters
+ return false, -1, err
+ }
checkPath := fmt.Sprintf(blobsPath, reference.Path(repo), digest.String())
logrus.Debugf("Checking %s", checkPath)
res, err := d.c.makeRequest(ctx, http.MethodHead, checkPath, nil, nil, v2Auth, extraScope)
@@ -466,6 +469,7 @@ func (d *dockerImageDestination) PutManifest(ctx context.Context, m []byte, inst
// particular instance.
refTail = instanceDigest.String()
// Double-check that the manifest we've been given matches the digest we've been given.
+ // This also validates the format of instanceDigest.
matches, err := manifest.MatchesDigest(m, *instanceDigest)
if err != nil {
return fmt.Errorf("digesting manifest in PutManifest: %w", err)
@@ -780,8 +784,12 @@ func (d *dockerImageDestination) putSignaturesToSigstoreAttachments(ctx context.
if err != nil {
return err
}
+ attachmentTag, err := sigstoreAttachmentTag(manifestDigest)
+ if err != nil {
+ return err
+ }
logrus.Debugf("Uploading sigstore attachment manifest")
- return d.uploadManifest(ctx, manifestBlob, sigstoreAttachmentTag(manifestDigest))
+ return d.uploadManifest(ctx, manifestBlob, attachmentTag)
}
func layerMatchesSigstoreSignature(layer imgspecv1.Descriptor, mimeType string,
@@ -897,6 +905,7 @@ func (d *dockerImageDestination) putSignaturesToAPIExtension(ctx context.Context
return err
}
+ // manifestDigest is known to be valid because it was not rejected by getExtensionsSignatures above.
path := fmt.Sprintf(extensionsSignaturePath, reference.Path(d.ref.ref), manifestDigest.String())
res, err := d.c.makeRequest(ctx, http.MethodPut, path, nil, bytes.NewReader(body), v2Auth, nil)
if err != nil {
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image_src.go b/vendor/github.com/containers/image/v5/docker/docker_image_src.go
index b78b9b5163..274cd6dd2c 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image_src.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image_src.go
@@ -194,6 +194,9 @@ func simplifyContentType(contentType string) string {
// this never happens if the primary manifest is not a manifest list (e.g. if the source never returns manifest lists).
func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
if instanceDigest != nil {
+ if err := instanceDigest.Validate(); err != nil { // Make sure instanceDigest.String() does not contain any unexpected characters
+ return nil, "", err
+ }
return s.fetchManifest(ctx, instanceDigest.String())
}
err := s.ensureManifestIsLoaded(ctx)
@@ -203,6 +206,8 @@ func (s *dockerImageSource) GetManifest(ctx context.Context, instanceDigest *dig
return s.cachedManifest, s.cachedManifestMIMEType, nil
}
+// fetchManifest fetches a manifest for tagOrDigest.
+// The caller is responsible for ensuring tagOrDigest uses the expected format.
func (s *dockerImageSource) fetchManifest(ctx context.Context, tagOrDigest string) ([]byte, string, error) {
return s.c.fetchManifest(ctx, s.physicalRef, tagOrDigest)
}
@@ -352,6 +357,9 @@ func (s *dockerImageSource) GetBlobAt(ctx context.Context, info types.BlobInfo,
return nil, nil, fmt.Errorf("external URLs not supported with GetBlobAt")
}
+ if err := info.Digest.Validate(); err != nil { // Make sure info.Digest.String() does not contain any unexpected characters
+ return nil, nil, err
+ }
path := fmt.Sprintf(blobsPath, reference.Path(s.physicalRef.ref), info.Digest.String())
logrus.Debugf("Downloading %s", path)
res, err := s.c.makeRequest(ctx, http.MethodGet, path, headers, nil, v2Auth, nil)
diff --git a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
index 2d83254d78..7f6bd0e6be 100644
--- a/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
+++ b/vendor/github.com/containers/image/v5/docker/internal/tarfile/writer.go
@@ -142,6 +142,9 @@ func (w *Writer) writeLegacyMetadataLocked(layerDescriptors []manifest.Schema2De
}
// This chainID value matches the computation in docker/docker/layer.CreateChainID …
+ if err := l.Digest.Validate(); err != nil { // This should never fail on this code path, still: make sure the chainID computation is unambiguous.
+ return err
+ }
if chainID == "" {
chainID = l.Digest
} else {
diff --git a/vendor/github.com/containers/image/v5/openshift/openshift_src.go b/vendor/github.com/containers/image/v5/openshift/openshift_src.go
index 0ac0127ee7..62774afbb7 100644
--- a/vendor/github.com/containers/image/v5/openshift/openshift_src.go
+++ b/vendor/github.com/containers/image/v5/openshift/openshift_src.go
@@ -109,6 +109,9 @@ func (s *openshiftImageSource) GetSignaturesWithFormat(ctx context.Context, inst
}
imageStreamImageName = s.imageStreamImageName
} else {
+ if err := instanceDigest.Validate(); err != nil { // Make sure instanceDigest.String() does not contain any unexpected characters
+ return nil, err
+ }
imageStreamImageName = instanceDigest.String()
}
image, err := s.client.getImage(ctx, imageStreamImageName)
diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/blobcache.go b/vendor/github.com/containers/image/v5/pkg/blobcache/blobcache.go
index 2bbf48848a..f4de6cebfe 100644
--- a/vendor/github.com/containers/image/v5/pkg/blobcache/blobcache.go
+++ b/vendor/github.com/containers/image/v5/pkg/blobcache/blobcache.go
@@ -78,12 +78,15 @@ func (b *BlobCache) DeleteImage(ctx context.Context, sys *types.SystemContext) e
}
// blobPath returns the path appropriate for storing a blob with digest.
-func (b *BlobCache) blobPath(digest digest.Digest, isConfig bool) string {
+func (b *BlobCache) blobPath(digest digest.Digest, isConfig bool) (string, error) {
+ if err := digest.Validate(); err != nil { // Make sure digest.String() does not contain any unexpected characters
+ return "", err
+ }
baseName := digest.String()
if isConfig {
baseName += ".config"
}
- return filepath.Join(b.directory, baseName)
+ return filepath.Join(b.directory, baseName), nil
}
// findBlob checks if we have a blob for info in cache (whether a config or not)
@@ -95,7 +98,10 @@ func (b *BlobCache) findBlob(info types.BlobInfo) (string, int64, bool, error) {
}
for _, isConfig := range []bool{false, true} {
- path := b.blobPath(info.Digest, isConfig)
+ path, err := b.blobPath(info.Digest, isConfig)
+ if err != nil {
+ return "", -1, false, err
+ }
fileInfo, err := os.Stat(path)
if err == nil && (info.Size == -1 || info.Size == fileInfo.Size()) {
return path, fileInfo.Size(), isConfig, nil
diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
index fde95974e9..bae167584e 100644
--- a/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
+++ b/vendor/github.com/containers/image/v5/pkg/blobcache/dest.go
@@ -115,7 +115,10 @@ func (d *blobCacheDestination) saveStream(wg *sync.WaitGroup, decompressReader i
}
// Determine the name that we should give to the uncompressed copy of the blob.
- decompressedFilename := d.reference.blobPath(digester.Digest(), isConfig)
+ decompressedFilename, err := d.reference.blobPath(digester.Digest(), isConfig)
+ if err != nil {
+ return
+ }
// Rename the temporary file.
if err := os.Rename(tempFile.Name(), decompressedFilename); err != nil {
logrus.Debugf("error renaming new decompressed copy of blob %q into place at %q: %v", digester.Digest().String(), decompressedFilename, err)
@@ -153,7 +156,10 @@ func (d *blobCacheDestination) PutBlobWithOptions(ctx context.Context, stream io
needToWait := false
compression := archive.Uncompressed
if inputInfo.Digest != "" {
- filename := d.reference.blobPath(inputInfo.Digest, options.IsConfig)
+ filename, err2 := d.reference.blobPath(inputInfo.Digest, options.IsConfig)
+ if err2 != nil {
+ return private.UploadedBlob{}, err2
+ }
tempfile, err = os.CreateTemp(filepath.Dir(filename), filepath.Base(filename))
if err == nil {
stream = io.TeeReader(stream, tempfile)
@@ -282,7 +288,10 @@ func (d *blobCacheDestination) PutManifest(ctx context.Context, manifestBytes []
if err != nil {
logrus.Warnf("error digesting manifest %q: %v", string(manifestBytes), err)
} else {
- filename := d.reference.blobPath(manifestDigest, false)
+ filename, err := d.reference.blobPath(manifestDigest, false)
+ if err != nil {
+ return err
+ }
if err = ioutils.AtomicWriteFile(filename, manifestBytes, 0600); err != nil {
logrus.Warnf("error saving manifest as %q: %v", filename, err)
}
diff --git a/vendor/github.com/containers/image/v5/pkg/blobcache/src.go b/vendor/github.com/containers/image/v5/pkg/blobcache/src.go
index 2fe108cda1..600d2fa7a5 100644
--- a/vendor/github.com/containers/image/v5/pkg/blobcache/src.go
+++ b/vendor/github.com/containers/image/v5/pkg/blobcache/src.go
@@ -56,7 +56,10 @@ func (s *blobCacheSource) Close() error {
func (s *blobCacheSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) ([]byte, string, error) {
if instanceDigest != nil {
- filename := s.reference.blobPath(*instanceDigest, false)
+ filename, err := s.reference.blobPath(*instanceDigest, false)
+ if err != nil {
+ return nil, "", err
+ }
manifestBytes, err := os.ReadFile(filename)
if err == nil {
s.cacheHits++
@@ -136,8 +139,10 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest
replacedInfos := make([]types.BlobInfo, 0, len(infos))
for _, info := range infos {
var replaceDigest []byte
- var err error
- blobFile := s.reference.blobPath(info.Digest, false)
+ blobFile, err := s.reference.blobPath(info.Digest, false)
+ if err != nil {
+ return nil, err
+ }
var alternate string
switch s.reference.compress {
case types.Compress:
@@ -148,7 +153,10 @@ func (s *blobCacheSource) LayerInfosForCopy(ctx context.Context, instanceDigest
replaceDigest, err = os.ReadFile(alternate)
}
if err == nil && digest.Digest(replaceDigest).Validate() == nil {
- alternate = s.reference.blobPath(digest.Digest(replaceDigest), false)
+ alternate, err = s.reference.blobPath(digest.Digest(replaceDigest), false)
+ if err != nil {
+ return nil, err
+ }
fileInfo, err := os.Stat(alternate)
if err == nil {
switch info.MediaType {
diff --git a/vendor/github.com/containers/image/v5/storage/storage_dest.go b/vendor/github.com/containers/image/v5/storage/storage_dest.go
index b65be2e147..6b59be1fd9 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_dest.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_dest.go
@@ -803,8 +803,12 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t
if err != nil {
return fmt.Errorf("digesting top-level manifest: %w", err)
}
+ key, err := manifestBigDataKey(manifestDigest)
+ if err != nil {
+ return err
+ }
options.BigData = append(options.BigData, storage.ImageBigDataOption{
- Key: manifestBigDataKey(manifestDigest),
+ Key: key,
Data: toplevelManifest,
Digest: manifestDigest,
})
@@ -812,8 +816,12 @@ func (s *storageImageDestination) Commit(ctx context.Context, unparsedToplevel t
// Set up to save the image's manifest. Allow looking it up by digest by using the key convention defined by the Store.
// Record the manifest twice: using a digest-specific key to allow references to that specific digest instance,
// and using storage.ImageDigestBigDataKey for future users that dont specify any digest and for compatibility with older readers.
+ key, err := manifestBigDataKey(s.manifestDigest)
+ if err != nil {
+ return err
+ }
options.BigData = append(options.BigData, storage.ImageBigDataOption{
- Key: manifestBigDataKey(s.manifestDigest),
+ Key: key,
Data: s.manifest,
Digest: s.manifestDigest,
})
diff --git a/vendor/github.com/containers/image/v5/storage/storage_image.go b/vendor/github.com/containers/image/v5/storage/storage_image.go
index b734c41291..ba25a0cba7 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_image.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_image.go
@@ -26,8 +26,11 @@ type storageImageCloser struct {
// manifestBigDataKey returns a key suitable for recording a manifest with the specified digest using storage.Store.ImageBigData and related functions.
// If a specific manifest digest is explicitly requested by the user, the key returned by this function should be used preferably;
// for compatibility, if a manifest is not available under this key, check also storage.ImageDigestBigDataKey
-func manifestBigDataKey(digest digest.Digest) string {
- return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String()
+func manifestBigDataKey(digest digest.Digest) (string, error) {
+ if err := digest.Validate(); err != nil { // Make sure info.Digest.String() uses the expected format and does not collide with other BigData keys.
+ return "", err
+ }
+ return storage.ImageDigestManifestBigDataNamePrefix + "-" + digest.String(), nil
}
// signatureBigDataKey returns a key suitable for recording the signatures associated with the manifest with the specified digest using storage.Store.ImageBigData and related functions.
diff --git a/vendor/github.com/containers/image/v5/storage/storage_reference.go b/vendor/github.com/containers/image/v5/storage/storage_reference.go
index a55e34054a..6b7565fd85 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_reference.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_reference.go
@@ -73,7 +73,10 @@ func multiArchImageMatchesSystemContext(store storage.Store, img *storage.Image,
// We don't need to care about storage.ImageDigestBigDataKey because
// manifests lists are only stored into storage by c/image versions
// that know about manifestBigDataKey, and only using that key.
- key := manifestBigDataKey(manifestDigest)
+ key, err := manifestBigDataKey(manifestDigest)
+ if err != nil {
+ return false // This should never happen, manifestDigest comes from a reference.Digested, and that validates the format.
+ }
manifestBytes, err := store.ImageBigData(img.ID, key)
if err != nil {
return false
@@ -95,7 +98,10 @@ func multiArchImageMatchesSystemContext(store storage.Store, img *storage.Image,
if err != nil {
return false
}
- key = manifestBigDataKey(chosenInstance)
+ key, err = manifestBigDataKey(chosenInstance)
+ if err != nil {
+ return false
+ }
_, err = store.ImageBigData(img.ID, key)
return err == nil // true if img.ID is based on chosenInstance.
}
diff --git a/vendor/github.com/containers/image/v5/storage/storage_src.go b/vendor/github.com/containers/image/v5/storage/storage_src.go
index 29cce56355..7e4b69f222 100644
--- a/vendor/github.com/containers/image/v5/storage/storage_src.go
+++ b/vendor/github.com/containers/image/v5/storage/storage_src.go
@@ -202,7 +202,10 @@ func (s *storageImageSource) getBlobAndLayerID(digest digest.Digest, layers []st
// GetManifest() reads the image's manifest.
func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *digest.Digest) (manifestBlob []byte, mimeType string, err error) {
if instanceDigest != nil {
- key := manifestBigDataKey(*instanceDigest)
+ key, err := manifestBigDataKey(*instanceDigest)
+ if err != nil {
+ return nil, "", err
+ }
blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key)
if err != nil {
return nil, "", fmt.Errorf("reading manifest for image instance %q: %w", *instanceDigest, err)
@@ -214,7 +217,10 @@ func (s *storageImageSource) GetManifest(ctx context.Context, instanceDigest *di
// Prefer the manifest corresponding to the user-specified digest, if available.
if s.imageRef.named != nil {
if digested, ok := s.imageRef.named.(reference.Digested); ok {
- key := manifestBigDataKey(digested.Digest())
+ key, err := manifestBigDataKey(digested.Digest())
+ if err != nil {
+ return nil, "", err
+ }
blob, err := s.imageRef.transport.store.ImageBigData(s.image.ID, key)
if err != nil && !os.IsNotExist(err) { // os.IsNotExist is true if the image exists but there is no data corresponding to key
return nil, "", err
From 6e25805f4bce855c85bb944b7dd1c0246dd02ad0 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Miloslav=20Trma=C4=8D?= <mitr@redhat.com>
Date: Thu, 18 Apr 2024 19:10:39 +0200
Subject: [PATCH 6/6] Validate the tags returned by a registry
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
Signed-off-by: Miloslav Trmač <mitr@redhat.com>
---
docker/docker_image.go | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/vendor/github.com/containers/image/v5/docker/docker_image.go b/vendor/github.com/containers/image/v5/docker/docker_image.go
index 93160480ea..4c80bb2b52 100644
--- a/vendor/github.com/containers/image/v5/docker/docker_image.go
+++ b/vendor/github.com/containers/image/v5/docker/docker_image.go
@@ -88,7 +88,12 @@ func GetRepositoryTags(ctx context.Context, sys *types.SystemContext, ref types.
if err = json.NewDecoder(res.Body).Decode(&tagsHolder); err != nil {
return nil, err
}
- tags = append(tags, tagsHolder.Tags...)
+ for _, tag := range tagsHolder.Tags {
+ if _, err := reference.WithTag(dr.ref, tag); err != nil { // Ensure the tag does not contain unexpected values
+ return nil, fmt.Errorf("registry returned invalid tag %q: %w", tag, err)
+ }
+ tags = append(tags, tag)
+ }
link := res.Header.Get("Link")
if link == "" {