updated vendor

This commit is contained in:
2026-06-16 08:02:19 +02:00
parent 2f7f99d3f0
commit 77299d0c64
1283 changed files with 67302 additions and 208958 deletions
+87 -54
View File
@@ -52,6 +52,16 @@ func (i *image) MediaType() (types.MediaType, error) {
return i.base.MediaType()
}
// isImageConfig reports whether the media type is a Docker or OCI image config.
func isImageConfig(mt types.MediaType) bool {
switch mt {
case types.DockerConfigJSON, types.OCIConfigJSON:
return true
default:
return false
}
}
func (i *image) compute() error {
i.Lock()
defer i.Unlock()
@@ -60,6 +70,24 @@ func (i *image) compute() error {
if i.computed {
return nil
}
m, err := i.base.Manifest()
if err != nil {
return err
}
manifest := m.DeepCopy()
diffIDMap := make(map[v1.Hash]v1.Layer)
digestMap := make(map[v1.Hash]v1.Layer)
// Determine effective config media type (user override takes precedence).
cfgMediaType := manifest.Config.MediaType
if i.configMediaType != nil {
cfgMediaType = *i.configMediaType
}
imageConfig := isImageConfig(cfgMediaType)
var configFile *v1.ConfigFile
if i.configFile != nil {
configFile = i.configFile
@@ -70,33 +98,32 @@ func (i *image) compute() error {
}
configFile = cf.DeepCopy()
}
diffIDs := configFile.RootFS.DiffIDs
history := configFile.History
diffIDMap := make(map[v1.Hash]v1.Layer)
digestMap := make(map[v1.Hash]v1.Layer)
// For image configs, update RootFS.DiffIDs and History from added layers.
// For artifacts, skip this: the config has no rootfs or history fields.
if imageConfig {
diffIDs := configFile.RootFS.DiffIDs
history := configFile.History
for _, add := range i.adds {
history = append(history, add.History)
if add.Layer != nil {
diffID, err := add.Layer.DiffID()
if err != nil {
return err
for _, add := range i.adds {
history = append(history, add.History)
if add.Layer != nil {
diffID, err := add.Layer.DiffID()
if err != nil {
return err
}
diffIDs = append(diffIDs, diffID)
diffIDMap[diffID] = add.Layer
}
diffIDs = append(diffIDs, diffID)
diffIDMap[diffID] = add.Layer
}
configFile.RootFS.DiffIDs = diffIDs
configFile.History = history
}
m, err := i.base.Manifest()
if err != nil {
return err
}
manifest := m.DeepCopy()
manifestLayers := manifest.Layers
for _, add := range i.adds {
if add.Layer == nil {
// Empty layers include only history in manifest.
continue
}
@@ -105,14 +132,12 @@ func (i *image) compute() error {
return err
}
// Fields in the addendum override the original descriptor.
if len(add.Annotations) != 0 {
desc.Annotations = add.Annotations
}
if len(add.URLs) != 0 {
desc.URLs = add.URLs
}
if add.MediaType != "" {
desc.MediaType = add.MediaType
}
@@ -120,42 +145,38 @@ func (i *image) compute() error {
manifestLayers = append(manifestLayers, *desc)
digestMap[desc.Digest] = add.Layer
}
configFile.RootFS.DiffIDs = diffIDs
configFile.History = history
manifest.Layers = manifestLayers
rcfg, err := json.Marshal(configFile)
if err != nil {
return err
}
d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg))
if err != nil {
return err
}
manifest.Config.Digest = d
manifest.Config.Size = sz
// For image configs, re-marshal the config and update the manifest digest.
// For artifacts, preserve the original config blob as-is to avoid
// corrupting the digest via re-marshaling.
if imageConfig {
rcfg, err := json.Marshal(configFile)
if err != nil {
return err
}
d, sz, err := v1.SHA256(bytes.NewBuffer(rcfg))
if err != nil {
return err
}
manifest.Config.Digest = d
manifest.Config.Size = sz
// If Data was set in the base image, we need to update it in the mutated image.
if m.Config.Data != nil {
manifest.Config.Data = rcfg
if m.Config.Data != nil {
manifest.Config.Data = rcfg
}
}
// If the user wants to mutate the media type of the config
if i.configMediaType != nil {
manifest.Config.MediaType = *i.configMediaType
}
if i.mediaType != nil {
manifest.MediaType = *i.mediaType
}
if i.annotations != nil {
if manifest.Annotations == nil {
manifest.Annotations = map[string]string{}
}
for k, v := range i.annotations {
manifest.Annotations[k] = v
}
@@ -173,29 +194,34 @@ func (i *image) compute() error {
// Layers returns the ordered collection of filesystem layers that comprise this image.
// The order of the list is oldest/base layer first, and most-recent/top layer last.
func (i *image) Layers() ([]v1.Layer, error) {
if err := i.compute(); errors.Is(err, stream.ErrNotComputed) {
// Image contains a streamable layer which has not yet been
// consumed. Just return the layers we have in case the caller
// is going to consume the layers.
if err := i.compute(); errors.Is(err, stream.ErrNotComputed) || (i.manifest != nil && !isImageConfig(i.manifest.Config.MediaType)) {
// Stream not yet consumed, or non-image OCI artifact (RootFS.DiffIDs
// is empty so partial.DiffIDs returns nothing). Fall back to the base
// layers plus any added layers.
layers, err := i.base.Layers()
if err != nil {
return nil, err
}
for _, add := range i.adds {
layers = append(layers, add.Layer)
if add.Layer != nil {
layers = append(layers, add.Layer)
}
}
return layers, nil
} else if err != nil {
return nil, err
}
diffIDs, err := partial.DiffIDs(i)
if err != nil {
return nil, err
}
ls := make([]v1.Layer, 0, len(diffIDs))
for _, h := range diffIDs {
l, err := i.LayerByDiffID(h)
// Walk manifest layer descriptors by digest rather than rootfs diff
// IDs. Two layers can legitimately share a diff ID — same uncompressed
// content, different compression — and produce distinct digests. The
// manifest preserves the per-occurrence digest; LayerByDiffID does not,
// which previously caused duplicate-diff-ID layers to collapse to a
// single entry in the returned slice and break blob upload for
// downstream pushers (see #2034).
ls := make([]v1.Layer, 0, len(i.manifest.Layers))
for _, desc := range i.manifest.Layers {
l, err := i.LayerByDigest(desc.Digest)
if err != nil {
return nil, err
}
@@ -220,11 +246,18 @@ func (i *image) ConfigFile() (*v1.ConfigFile, error) {
return i.configFile.DeepCopy(), nil
}
// RawConfigFile returns the serialized bytes of ConfigFile()
// RawConfigFile returns the serialized bytes of ConfigFile().
// For non-image OCI artifacts, returns the original raw config to preserve
// the config blob digest.
func (i *image) RawConfigFile() ([]byte, error) {
if err := i.compute(); err != nil {
return nil, err
}
// If the manifest config is not a standard image config, return the
// original raw bytes to avoid corrupting the digest via re-marshaling.
if i.manifest != nil && !isImageConfig(i.manifest.Config.MediaType) {
return i.base.RawConfigFile()
}
return json.Marshal(i.configFile)
}
+73 -72
View File
@@ -277,90 +277,91 @@ func extract(img v1.Image, w io.Writer) error {
// whiteout layers more efficient, since we can just keep track of the removed
// files as we see .wh. layers and ignore those in previous layers.
for i := len(layers) - 1; i >= 0; i-- {
layer := layers[i]
layerReader, err := layer.Uncompressed()
if err != nil {
return fmt.Errorf("reading layer contents: %w", err)
if err := extractLayer(tarWriter, fileMap, layers[i]); err != nil {
return err
}
defer layerReader.Close()
tarReader := tar.NewReader(layerReader)
for {
header, err := tarReader.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return fmt.Errorf("reading tar: %w", err)
}
}
return nil
}
// Some tools prepend everything with "./", so if we don't Clean the
// name, we may have duplicate entries, which angers tar-split.
header.Name = filepath.Clean(header.Name)
func extractLayer(tarWriter *tar.Writer, fileMap map[string]bool, layer v1.Layer) error {
layerReader, err := layer.Uncompressed()
if err != nil {
return fmt.Errorf("reading layer contents: %w", err)
}
defer layerReader.Close()
// Normalize absolute paths to relative to prevent writing outside
// the extraction root (Zip Slip / CVE-2018-15664 class).
// Many OCI tools emit absolute paths; stripping the leading slash
// preserves the entry while removing the danger.
if filepath.IsAbs(header.Name) {
header.Name = strings.TrimLeft(header.Name, "/")
}
// After normalization, reject any remaining path traversal.
if strings.HasPrefix(header.Name, "..") {
continue
}
tarReader := tar.NewReader(layerReader)
for {
header, err := tarReader.Next()
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return fmt.Errorf("reading tar: %w", err)
}
// Reject symlinks and hardlinks that point outside the extraction
// root. An attacker can create a symlink to /etc and then write
// files through it in a subsequent layer entry.
if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeLink {
linkTarget := filepath.Clean(header.Linkname)
if strings.HasPrefix(linkTarget, "..") || filepath.IsAbs(linkTarget) {
// Some tools prepend everything with "./", so if we don't Clean the
// name, we may have duplicate entries, which angers tar-split.
header.Name = filepath.Clean(header.Name)
// Reject relative symlinks and hardlinks whose targets escape the
// image rootfs. Relative targets are resolved against the symlink's
// own directory: if the clean result starts with ".." the link would
// leave the rootfs. Relative symlinks that stay within the rootfs
// (common for glibc, C toolchains, etc.) are preserved unchanged.
// Absolute targets are left as-is; see #2238 for ongoing discussion
// on whether they should be pruned.
if header.Typeflag == tar.TypeSymlink || header.Typeflag == tar.TypeLink {
if !filepath.IsAbs(header.Linkname) {
resolved := filepath.Clean(filepath.Join(filepath.Dir(header.Name), header.Linkname)) //nolint:gosec // G305: path is only used for validation, not file I/O
if strings.HasPrefix(resolved, "..") {
continue
}
}
}
// force PAX format to remove Name/Linkname length limit of 100 characters
// required by USTAR and to not depend on internal tar package guess which
// prefers USTAR over PAX
header.Format = tar.FormatPAX
// force PAX format to remove Name/Linkname length limit of 100 characters
// required by USTAR and to not depend on internal tar package guess which
// prefers USTAR over PAX
header.Format = tar.FormatPAX
basename := filepath.Base(header.Name)
dirname := filepath.Dir(header.Name)
tombstone := strings.HasPrefix(basename, whiteoutPrefix)
if tombstone {
basename = basename[len(whiteoutPrefix):]
basename := filepath.Base(header.Name)
dirname := filepath.Dir(header.Name)
tombstone := strings.HasPrefix(basename, whiteoutPrefix)
if tombstone {
basename = basename[len(whiteoutPrefix):]
}
// check if we have seen value before
// if we're checking a directory, don't filepath.Join names
var name string
if header.Typeflag == tar.TypeDir {
name = header.Name
} else {
name = filepath.Join(dirname, basename)
}
if _, ok := fileMap[name]; ok && !tombstone {
continue
}
// check for a whited out parent directory
if inWhiteoutDir(fileMap, name) {
continue
}
// mark file as handled. non-directory implicitly tombstones
// any entries with a matching (or child) name
fileMap[name] = tombstone || (header.Typeflag != tar.TypeDir)
if !tombstone {
if err := tarWriter.WriteHeader(header); err != nil {
return err
}
// check if we have seen value before
// if we're checking a directory, don't filepath.Join names
var name string
if header.Typeflag == tar.TypeDir {
name = header.Name
} else {
name = filepath.Join(dirname, basename)
}
if _, ok := fileMap[name]; ok && !tombstone {
continue
}
// check for a whited out parent directory
if inWhiteoutDir(fileMap, name) {
continue
}
// mark file as handled. non-directory implicitly tombstones
// any entries with a matching (or child) name
fileMap[name] = tombstone || (header.Typeflag != tar.TypeDir)
if !tombstone {
if err := tarWriter.WriteHeader(header); err != nil {
if header.Size > 0 {
if _, err := io.CopyN(tarWriter, tarReader, header.Size); err != nil {
return err
}
if header.Size > 0 {
if _, err := io.CopyN(tarWriter, tarReader, header.Size); err != nil {
return err
}
}
}
}
}