Script 'mail_helper' called by obssrc Hello community, here is the log from the commit of package docker for openSUSE:Factory checked in at 2026-06-29 17:30:07 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/docker (Old) and /work/SRC/openSUSE:Factory/.docker.new.11887 (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "docker" Mon Jun 29 17:30:07 2026 rev:186 rq:1362244 version:unknown Changes: -------- --- /work/SRC/openSUSE:Factory/docker/docker.changes 2026-05-13 17:21:02.775544952 +0200 +++ /work/SRC/openSUSE:Factory/.docker.new.11887/docker.changes 2026-06-29 17:30:41.994730355 +0200 @@ -1,0 +2,19 @@ +Tue Jun 23 06:19:04 UTC 2026 - Madhankumar Chellamuthu <[email protected]> + +- Ensure correct certificate is used for TSA auth + (bsc#1262346, CVE-2026-39984) + * 0007-CVE-2026-39984-Ensure-correct-certificate-is-used-fo.patch + +- http2: prevent hanging Transport due to bad SETTINGS + (bsc#1265782, CVE-2026-33814) + * 0008-CVE-2026-33814-http2-prevent-hanging-Transport-due-t.patch + +- idna: update from x/text, fix ToUnicode and all-ASCII xn-- labels + (bsc#1266625, CVE-2026-39821) + * 0009-CVE-2026-39821-idna-update-from-x-text-fix-ToUnicode.patch + +- daemon: Decompress archives before entering container filesystem + (bsc#1267827, CVE-2026-41567) + * 0010-CVE-2026-41567-daemon-Decompress-archives-before-ent.patch + +------------------------------------------------------------------- New: ---- 0007-CVE-2026-39984-Ensure-correct-certificate-is-used-fo.patch 0008-CVE-2026-33814-http2-prevent-hanging-Transport-due-t.patch 0009-CVE-2026-39821-idna-update-from-x-text-fix-ToUnicode.patch 0010-CVE-2026-41567-daemon-Decompress-archives-before-ent.patch ----------(New B)---------- New: (bsc#1262346, CVE-2026-39984) * 0007-CVE-2026-39984-Ensure-correct-certificate-is-used-fo.patch New: (bsc#1265782, CVE-2026-33814) * 0008-CVE-2026-33814-http2-prevent-hanging-Transport-due-t.patch New: (bsc#1266625, CVE-2026-39821) * 0009-CVE-2026-39821-idna-update-from-x-text-fix-ToUnicode.patch New: (bsc#1267827, CVE-2026-41567) * 0010-CVE-2026-41567-daemon-Decompress-archives-before-ent.patch ----------(New E)---------- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ docker.spec ++++++ --- /var/tmp/diff_new_pack.oVIi6E/_old 2026-06-29 17:30:43.186770905 +0200 +++ /var/tmp/diff_new_pack.oVIi6E/_new 2026-06-29 17:30:43.190771042 +0200 @@ -112,6 +112,10 @@ Patch201: 0005-bsc1073877-apparmor-clobber-docker-default-profile-o.patch # UPSTREAM: Revert of upstream patches to make apparmor work on SLE 12. Patch202: 0006-SLE12-revert-apparmor-remove-version-conditionals-fr.patch +Patch203: 0007-CVE-2026-39984-Ensure-correct-certificate-is-used-fo.patch +Patch204: 0008-CVE-2026-33814-http2-prevent-hanging-Transport-due-t.patch +Patch205: 0009-CVE-2026-39821-idna-update-from-x-text-fix-ToUnicode.patch +Patch206: 0010-CVE-2026-41567-daemon-Decompress-archives-before-ent.patch BuildRequires: audit BuildRequires: bash-completion BuildRequires: ca-certificates @@ -390,6 +394,14 @@ %patch -P201 -p1 # Solves apparmor issues on SLE-12, but okay for newer SLE versions too. %patch -P202 -p1 +# bsc#1262346 +%patch -P203 -p1 +# bsc#1265782 +%patch -P204 -p1 +# bsc#1266625 +%patch -P205 -p1 +# bsc#1267827 +%patch -P206 -p1 %build %sysusers_generate_pre %{SOURCE160} %{name} docker.conf ++++++ 0007-CVE-2026-39984-Ensure-correct-certificate-is-used-fo.patch ++++++ >From 7a7cf4b5cc039e723d25ddc1a7e0a33d9ee5950a Mon Sep 17 00:00:00 2001 From: rcmadhankumar <[email protected]> Date: Wed, 3 Jun 2026 13:55:31 +0530 Subject: [PATCH 1/4] CVE-2026-39984: Ensure correct certificate is used for TSA auth checks (GHSA-xm5m-wgh2-rrg3) -- CVE-2026-39984 Sigstore Timestamp Authority is a service for issuing RFC 3161 timestamps. Versions 2.0.5 and below contain an authorization bypass vulnerability in the VerifyTimestampResponse function. VerifyTimestampResponse correctly verifies the certificate chain signature, but the TSA-specific constraint checks in VerifyLeafCert uses the first non-CA certificate from the PKCS#7 certificate bag instead of the leaf certificate from the verified chain. An attacker can exploit this by prepending a forged certificate to the certificate bag while the message is signed with an authorized key, causing the library to validate the signature against one certificate but perform authorization checks against another. This vulnerability only affects users of the timestamp-authority/v2/pkg/verification package and does not affect the timestamp-authority service itself or sigstore-go. The issue has been fixed in version 2.0.6. Fix: Ensure correct certificate is used for TSA auth checks (GHSA-xm5m-wgh2-rrg3) * Prevent verification with cert outside the cert chain Currently VerifyLeafCert and verifyTSRWithChain may disagree on which cert is the real leaf certificate (TSA certificate): VerifyLeafCert should use the leaf certificate identified by verifyTSRWithChain. * Return the signer cert from verifyTSRWithChain() so verifyLeafCert() can just use the correct cert * Make sure verifyTSRWithChain() ensures that we have signer cert (either embedded or provided as option) * Make sure verifyTSRWithChain() verifies that embedded and provided cert match if both are present * Modify verifyLeafCert() so it only operates on given leaf cert Signed-off-by: Jussi Kukkonen <[email protected]> * Remove unused function verifyEmbeddedLeafCert is now not needed: the check is already done in verifyTSRWithChain. Remove the related test, add test cases to cover the same situatation in verifyTSRWithChain. Signed-off-by: Jussi Kukkonen <[email protected]> --------- Signed-off-by: Jussi Kukkonen <[email protected]> Backport of <https://github.com/sigstore/timestamp-authority/pull/1333/changes/71f1330d0422b6f2ea0cffe7354bf6f65477c3ba> Fixes CVE-2026-39984 Fixes bsc#1262346 --- .../v2/pkg/verification/verify.go | 69 ++++++++----------- 1 file changed, 29 insertions(+), 40 deletions(-) diff --git a/vendor/github.com/sigstore/timestamp-authority/v2/pkg/verification/verify.go b/vendor/github.com/sigstore/timestamp-authority/v2/pkg/verification/verify.go index 4f6c77c792..a407fa8d83 100644 --- a/vendor/github.com/sigstore/timestamp-authority/v2/pkg/verification/verify.go +++ b/vendor/github.com/sigstore/timestamp-authority/v2/pkg/verification/verify.go @@ -81,14 +81,6 @@ func verifySubjectCommonName(cert *x509.Certificate, opts VerifyOpts) error { return nil } -// If embedded in the TSR, verify the TSR's leaf certificate matches a provided TSA certificate -func verifyEmbeddedLeafCert(tsaCert *x509.Certificate, opts VerifyOpts) error { - if opts.TSACertificate != nil && !opts.TSACertificate.Equal(tsaCert) { - return fmt.Errorf("certificate embedded in the TSR does not match the provided TSA certificate") - } - return nil -} - // Verify the leaf's EKU is set to critical, per RFC 3161 2.3 func verifyLeafCertCriticalEKU(cert *x509.Certificate) error { var criticalEKU bool @@ -104,33 +96,14 @@ func verifyLeafCertCriticalEKU(cert *x509.Certificate) error { return nil } -func verifyLeafCert(ts timestamp.Timestamp, opts VerifyOpts) error { - if len(ts.Certificates) == 0 && opts.TSACertificate == nil { - return fmt.Errorf("leaf certificate must be present the in TSR or as a verify option") +func verifyLeafCert(leafCert *x509.Certificate, opts VerifyOpts) error { + if leafCert == nil { + // should never happen + return fmt.Errorf("signer certificate is required") } errMsg := "failed to verify TSA certificate" - var leafCert *x509.Certificate - if len(ts.Certificates) != 0 { - for _, c := range ts.Certificates { - if !c.IsCA { - leafCert = c - break - } - } - if leafCert == nil { - return fmt.Errorf("no leaf certificate found in chain") - } - - err := verifyEmbeddedLeafCert(leafCert, opts) - if err != nil { - return fmt.Errorf("%s: %w", errMsg, err) - } - } else { - leafCert = opts.TSACertificate - } - err := verifyLeafCertCriticalEKU(leafCert) if err != nil { return fmt.Errorf("%s: %w", errMsg, err) @@ -251,7 +224,8 @@ func VerifyTimestampResponse(tsrBytes []byte, artifact io.Reader, opts VerifyOpt } // verify the timestamp response signature using the provided certificate pool - if err = verifyTSRWithChain(ts, opts); err != nil { + signerCert, err := verifyTSRWithChain(ts, opts) + if err != nil { return nil, err } @@ -263,7 +237,7 @@ func VerifyTimestampResponse(tsrBytes []byte, artifact io.Reader, opts VerifyOpt return nil, err } - if err = verifyLeafCert(*ts, opts); err != nil { + if err = verifyLeafCert(signerCert, opts); err != nil { return nil, err } @@ -276,15 +250,21 @@ func VerifyTimestampResponse(tsrBytes []byte, artifact io.Reader, opts VerifyOpt return ts, nil } -func verifyTSRWithChain(ts *timestamp.Timestamp, opts VerifyOpts) error { +// Returns the TSA signer certificate after verifying the certificate chain validity. +func verifyTSRWithChain(ts *timestamp.Timestamp, opts VerifyOpts) (*x509.Certificate, error) { p7Message, err := pkcs7.Parse(ts.RawToken) if err != nil { - return fmt.Errorf("error parsing hashed message: %w", err) + return nil, fmt.Errorf("error parsing hashed message: %w", err) } if len(opts.Roots) == 0 { - return fmt.Errorf("no root certificates provided for verifying the certificate chain") + return nil, fmt.Errorf("no root certificates provided for verifying the certificate chain") } + + if p7Message.Certificates == nil && opts.TSACertificate == nil { + return nil, fmt.Errorf("leaf certificate must be present in the TSR or as a verify option") + } + rootCertPool := x509.NewCertPool() for _, cert := range opts.Roots { if cert != nil { @@ -292,7 +272,7 @@ func verifyTSRWithChain(ts *timestamp.Timestamp, opts VerifyOpts) error { } } if rootCertPool.Equal(x509.NewCertPool()) { - return fmt.Errorf("no valid root certificates provided for verifying the certificate chain") + return nil, fmt.Errorf("no valid root certificates provided for verifying the certificate chain") } intermediateCertPool := x509.NewCertPool() for _, cert := range opts.Intermediates { @@ -312,16 +292,25 @@ func verifyTSRWithChain(ts *timestamp.Timestamp, opts VerifyOpts) error { // leaf certificate issuer and serial number information is already part of // the PKCS7 object, adding the leaf certificate to the Certificates field // will allow verification to pass - if p7Message.Certificates == nil && opts.TSACertificate != nil { + if p7Message.Certificates == nil { p7Message.Certificates = []*x509.Certificate{opts.TSACertificate} } err = p7Message.VerifyWithOpts(x509Opts) if err != nil { - return fmt.Errorf("error while verifying with chain: %w", err) + return nil, fmt.Errorf("error while verifying with chain: %w", err) } - return nil + signerCert := p7Message.GetOnlySigner() + if signerCert == nil { + return nil, fmt.Errorf("signer certificate was not found") + } + + if opts.TSACertificate != nil && !opts.TSACertificate.Equal(signerCert) { + return nil, fmt.Errorf("certificate embedded in the TSR does not match the provided TSA certificate") + } + + return signerCert, nil } // Verify that the TSR's hashed message matches the digest of the artifact to be timestamped -- 2.54.0 ++++++ 0008-CVE-2026-33814-http2-prevent-hanging-Transport-due-t.patch ++++++ >From b074eab0aeb88785e6d284cba1f76a938e192bff Mon Sep 17 00:00:00 2001 From: rcmadhankumar <[email protected]> Date: Wed, 3 Jun 2026 14:16:19 +0530 Subject: [PATCH 2/4] CVE-2026-33814: http2: prevent hanging Transport due to bad SETTINGS frame -- CVE-2026-33814 When processing HTTP/2 SETTINGS frames, transport will enter an infinite loop of writing CONTINUATION frames if it receives a SETTINGS_MAX_FRAME_SIZE with a value of 0. Fix: http2: prevent hanging Transport due to bad SETTINGS frame This CL backports https://go.dev/cl/761581 to x/net. Change-Id: Ied435a51fdd8664d41dae14d082c39c76a6a6964 Reviewed-on: https://go-review.googlesource.com/c/net/+/761640 LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Nicholas Husin <[email protected]> Reviewed-by: Damien Neil <[email protected]> Fixes bsc#1265782 Fixes golang/go#78476 Fixes CVE-2026-33814 Backport of <golang/net@1e71bd8> --- vendor/golang.org/x/net/http2/transport.go | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/vendor/golang.org/x/net/http2/transport.go b/vendor/golang.org/x/net/http2/transport.go index 2e9c2f6a52..8132233310 100644 --- a/vendor/golang.org/x/net/http2/transport.go +++ b/vendor/golang.org/x/net/http2/transport.go @@ -2861,6 +2861,9 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { var seenMaxConcurrentStreams bool err := f.ForeachSetting(func(s Setting) error { + if err := s.Valid(); err != nil { + return err + } switch s.ID { case SettingMaxFrameSize: cc.maxFrameSize = s.Val @@ -2892,9 +2895,6 @@ func (rl *clientConnReadLoop) processSettingsNoWrite(f *SettingsFrame) error { cc.henc.SetMaxDynamicTableSize(s.Val) cc.peerMaxHeaderTableSize = s.Val case SettingEnableConnectProtocol: - if err := s.Valid(); err != nil { - return err - } // If the peer wants to send us SETTINGS_ENABLE_CONNECT_PROTOCOL, // we require that it do so in the first SETTINGS frame. // -- 2.54.0 ++++++ 0009-CVE-2026-39821-idna-update-from-x-text-fix-ToUnicode.patch ++++++ ++++ 30075 lines (skipped) ++++++ 0010-CVE-2026-41567-daemon-Decompress-archives-before-ent.patch ++++++ >From aa34de9a2d67d15008e470fe1b2edbd65017cfa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pawe=C5=82=20Gronowski?= <[email protected]> Date: Thu, 26 Mar 2026 12:33:27 +0100 Subject: [PATCH 4/4] CVE-2026-41567: daemon: Decompress archives before entering container filesystem -- MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CVE-2026-41567 Moby is an open source container framework. In versions prior to 29.5.1 and in moby/moby v2 prior to v2.0.0-beta.14, when a compressed archive is uploaded to a container via `PUT /containers/{id}/archive` or piped through `docker cp -`, the daemon resolves decompression binaries (such as `xz` or `unpigz`) from the container's filesystem rather than the host's due to incorrect ordering of operations. A malicious container image containing a trojanized decompression binary can achieve arbitrary code execution with full daemon privileges, including host root UID and unrestricted capabilities, when a user uploads a compressed (xz or gzip) archive into that container. This issue is fixed in Docker Engine 29.5.1 and moby/moby v2.0.0-beta.14. Workarounds include only running containers from trusted images, using authorization plugins to restrict access to the `PUT /containers/{id}/archive` endpoint, and avoiding piping compressed archives into containers created from untrusted images Fix: daemon: Decompress archives before entering container filesystem -- Move decompression outside RunInFS to prevent executing attacker-controlled binaries from within the container filesystem. When dockerd handles `PUT /containers/{id}/archive`, it switches root into the container's filesystem before extracting the archive. Previously, archive.Untar was called inside RunInFS, which meant decompression binaries (xz, unpigz) were resolved via PATH inside the container's filesystem. A malicious binary at /usr/bin/xz in the container would be executed as host root. Fix by calling decompressing the archive before entering the container filesystem, then using unpacking the uncompressed tar stream inside RunInFS. This ensures decompression binaries are always resolved from the host filesystem. Signed-off-by: Paweł Gronowski <[email protected]> (cherry picked from commit 2022313ffe5a8c04890b5295bc52670ee6df8070) [vlefebvre: fixes bsc#1267827 CVE-2026-41567] --- daemon/archive_unix.go | 12 ++++- integration/container/copy_test.go | 81 ++++++++++++++++++++++++++++-- 2 files changed, 89 insertions(+), 4 deletions(-) diff --git a/daemon/archive_unix.go b/daemon/archive_unix.go index cc6264faca..9faf430aa3 100644 --- a/daemon/archive_unix.go +++ b/daemon/archive_unix.go @@ -9,6 +9,7 @@ import ( "path/filepath" "github.com/moby/go-archive" + "github.com/moby/go-archive/compression" containertypes "github.com/moby/moby/api/types/container" "github.com/moby/moby/api/types/events" "github.com/moby/moby/v2/daemon/container" @@ -101,6 +102,15 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path container.Lock() defer container.Unlock() + // Decompress the archive before switching into the container's + // filesystem to avoid executing decompression binaries (xz, unpigz) + // that may have been placed inside the container image. + decompressed, err := compression.DecompressStream(content) + if err != nil { + return err + } + defer decompressed.Close() + cfs, err := daemon.openContainerFS(container) if err != nil { return err @@ -143,7 +153,7 @@ func (daemon *Daemon) containerExtractToDir(container *container.Container, path } } - return archive.Untar(content, absPath, options) + return archive.UntarUncompressed(decompressed, absPath, options) }) if err != nil { return err diff --git a/integration/container/copy_test.go b/integration/container/copy_test.go index 6183ce02e0..410ceac106 100644 --- a/integration/container/copy_test.go +++ b/integration/container/copy_test.go @@ -14,11 +14,13 @@ import ( cerrdefs "github.com/containerd/errdefs" "github.com/moby/go-archive" - "github.com/moby/moby/api/types/build" + buildtypes "github.com/moby/moby/api/types/build" "github.com/moby/moby/api/types/jsonstream" "github.com/moby/moby/client" "github.com/moby/moby/client/pkg/jsonmessage" + "github.com/moby/moby/v2/integration/internal/build" "github.com/moby/moby/v2/integration/internal/container" + "github.com/moby/moby/v2/internal/testutil/daemon" "github.com/moby/moby/v2/internal/testutil/fakecontext" "gotest.tools/v3/assert" is "gotest.tools/v3/assert/cmp" @@ -216,7 +218,7 @@ func makeTestImage(ctx context.Context, t *testing.T) (imageID string) { defer resp.Body.Close() err = jsonmessage.DisplayStream(resp.Body, io.Discard, jsonmessage.WithAuxCallback(func(msg jsonstream.Message) { - var r build.Result + var r buildtypes.Result assert.NilError(t, json.Unmarshal(*msg.Aux, &r)) imageID = r.ID })) @@ -291,7 +293,7 @@ func TestCopyFromContainer(t *testing.T) { var imageID string err = jsonmessage.DisplayStream(resp.Body, io.Discard, jsonmessage.WithAuxCallback(func(msg jsonstream.Message) { - var r build.Result + var r buildtypes.Result assert.NilError(t, json.Unmarshal(*msg.Aux, &r)) imageID = r.ID })) @@ -362,3 +364,76 @@ func TestCopyFromContainer(t *testing.T) { }) } } + +// TestCopyToContainerXZBinaryNotExecutedOnDaemon tests that when +// uploading an xz-compressed archive to a container via the +// PUT /containers/{id}/archive API, the daemon does NOT execute the xz +// binary found inside the container's filesystem. +// This is a regression test for +// https://github.com/moby/moby/security/advisories/GHSA-x86f-5xw2-fm2r +func TestCopyToContainerXZBinaryNotExecutedOnDaemon(t *testing.T) { + skip.If(t, testEnv.DaemonInfo.OSType == "windows") + skip.If(t, testEnv.IsRemoteDaemon, "cannot start daemon on remote test run") + ctx := setupTest(t) + + const tokenEnv = "MOBY_EXPLOIT_TEST_TOKEN" + const tokenVal = "host-boundary-crossed" + + d := daemon.New(t) + defer d.Cleanup(t) + d.SetEnvVar(tokenEnv, tokenVal) + d.StartWithBusybox(ctx, t, "--iptables=false", "--ip6tables=false") + defer d.Stop(t) + + apiClient := d.NewClientT(t) + + dir := t.TempDir() + buildCtx := fakecontext.New(t, dir, + // The fake xz writes the daemon's secret env var to a marker file. + // A process inside the container would not have this env var. + fakecontext.WithFile("fake-xz", "#!/bin/sh\necho $"+tokenEnv+" > /xz-was-executed\n"), + fakecontext.WithDockerfile(`FROM busybox +COPY fake-xz /usr/bin/xz +RUN chmod +x /usr/bin/xz`), + ) + defer buildCtx.Close() + + imageID := build.Do(ctx, t, apiClient, buildCtx, client.ImageBuildOptions{}) + + cID := container.Run(ctx, t, apiClient, container.WithImage(imageID)) + defer container.Remove(ctx, t, apiClient, cID, client.ContainerRemoveOptions{Force: true}) + + // Craft a payload that starts with xz magic bytes so the daemon's + // DecompressStream identifies it as xz-compressed. The payload is + // deliberately invalid xz data; we only care whether the daemon + // attempts to run the container's /usr/bin/xz to decompress it. + xzMagic := []byte{0xFD, 0x37, 0x7A, 0x58, 0x5A, 0x00} + payload := append(xzMagic, []byte("not-real-xz-data")...) + + // CopyToContainer is expected to fail because the payload is not a + // valid xz archive. We don't care about the error; we care about + // whether the container's xz binary was invoked. + _, _ = apiClient.CopyToContainer(ctx, cID, client.CopyToContainerOptions{ + DestinationPath: "/tmp", + Content: bytes.NewReader(payload), + }) + + t.Run("binary not executed", func(t *testing.T) { + // If the container's /usr/bin/xz was executed, the marker file + // will exist, and this assertion will fail. + res, err := container.Exec(ctx, apiClient, cID, []string{"test", "-f", "/xz-was-executed"}) + assert.NilError(t, err) + assert.Check(t, is.Equal(res.ExitCode, 1), + "container's xz binary was executed by the daemon during archive extraction; marker file /xz-was-executed was created") + }) + + t.Run("runs container", func(t *testing.T) { + // If the binary ran, check that it ran in the daemon's process + // context by looking for the daemon's secret env var in the + // marker file. A container process would not have this env var. + res, err := container.Exec(ctx, apiClient, cID, []string{"cat", "/xz-was-executed"}) + assert.NilError(t, err) + assert.Check(t, !strings.Contains(res.Stdout(), tokenVal), + "container's xz binary was executed in the daemon's process context: marker file contains the daemon's secret env var") + }) +} -- 2.54.0
