Version in base suite: 6.0.4-2+deb13u8 Version in overlay suite: 6.0.4-2+deb13u9 Base version: incus_6.0.4-2+deb13u9 Target version: incus_6.0.4-2+deb13u10 Base file: /srv/ftp-master.debian.org/ftp/pool/main/i/incus/incus_6.0.4-2+deb13u9.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/i/incus/incus_6.0.4-2+deb13u10.dsc changelog | 18 ++++-- patches/147-CVE-2026-81500.patch | 91 ++++++++++++++++++++++++++++++ patches/148-CVE-2026-81501.patch | 115 +++++++++++++++++++++++++++++++++++++++ patches/series | 2 4 files changed, 221 insertions(+), 5 deletions(-) dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpz642o53m/incus_6.0.4-2+deb13u9.dsc: no acceptable signature found dpkg-source: warning: cannot verify inline signature for /srv/release.debian.org/tmp/tmpz642o53m/incus_6.0.4-2+deb13u10.dsc: no acceptable signature found diff -Nru incus-6.0.4/debian/changelog incus-6.0.4/debian/changelog --- incus-6.0.4/debian/changelog 2026-07-30 22:57:51.000000000 +0000 +++ incus-6.0.4/debian/changelog 2026-09-02 16:39:29.000000000 +0000 @@ -1,3 +1,11 @@ +incus (6.0.4-2+deb13u10) trixie; urgency=medium + + * Cherry-pick fixes for the following security issues + - CVE-2026-81500 / GHSA-9pqw-c7m4-xvg7 + - CVE-2026-81501 / GHSA-c6wx-8679-hpr9 + + -- Mathias Gibbens Wed, 02 Sep 2026 16:39:29 +0000 + incus (6.0.4-2+deb13u9) trixie-security; urgency=high * Cherry-pick upstream fix for large nft ruleset performance @@ -8,11 +16,11 @@ - CVE-2026-62941 / GHSA-mq9x-prm8-3vpw - CVE-2026-63125 / GHSA-6rqx-22hc-qm36 - CVE-2026-63343 / GHSA-fmjx-5j3g-997p - - GHSA-26gp-p5fw-3r2h - - GHSA-4qxq-p5hm-3q3p - - GHSA-67qw-68v3-36h6 - - GHSA-m3j6-p3v3-qmjv - - GHSA-p2v3-6wvc-cv3p + - CVE-2026-81493 / GHSA-p2v3-6wvc-cv3p + - CVE-2026-81495 / GHSA-67qw-68v3-36h6 + - CVE-2026-81496 / GHSA-26gp-p5fw-3r2h + - CVE-2026-81497 / GHSA-4qxq-p5hm-3q3p + - CVE-2026-81498 / GHSA-m3j6-p3v3-qmjv * Cherry-pick four additional security fixes not assigned CVEs -- Mathias Gibbens Thu, 30 Jul 2026 22:57:51 +0000 diff -Nru incus-6.0.4/debian/patches/147-CVE-2026-81500.patch incus-6.0.4/debian/patches/147-CVE-2026-81500.patch --- incus-6.0.4/debian/patches/147-CVE-2026-81500.patch 1970-01-01 00:00:00.000000000 +0000 +++ incus-6.0.4/debian/patches/147-CVE-2026-81500.patch 2026-09-02 16:39:28.000000000 +0000 @@ -0,0 +1,91 @@ +From d4d7badf6597274320b78575e77aec5720163c9c Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Sun, 23 Aug 2026 17:26:26 -0400 +Subject: [PATCH] client/images: Prevent path traversal in downloaded image + name +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +The local filename for an exported image came from server-controlled +data (Content-Disposition for unified images, the simplestreams index +path) and was joined with the target directory. Basename it. + +This addresses GHSA-9pqw-c7m4-xvg7 (CVE pending) + +Signed-off-by: Stéphane Graber +Rebased-by: Mathias Gibbens +--- + client/incus_images.go | 4 +++- + client/simplestreams_images.go | 13 +++++++------ + 2 files changed, 10 insertions(+), 7 deletions(-) + +diff --git a/client/incus_images.go b/client/incus_images.go +index ed48d08be..3a0afccb2 100644 +--- a/client/incus_images.go ++++ b/client/incus_images.go +@@ -9,6 +9,7 @@ import ( + "net/http" + "net/url" + "os" ++ "path/filepath" + "slices" + "strings" + "time" +@@ -325,7 +326,8 @@ func incusDownloadImage(fingerprint string, uri string, userAgent string, do fun + } + + resp.MetaSize = size +- resp.MetaName = filename ++ // Basename the server-provided name to prevent path traversal. ++ resp.MetaName = filepath.Base(filename) + + // Check the hash + hash := fmt.Sprintf("%x", sha256.Sum(nil)) +diff --git a/client/simplestreams_images.go b/client/simplestreams_images.go +index 00cc35409..6bbfbe9d4 100644 +--- a/client/simplestreams_images.go ++++ b/client/simplestreams_images.go +@@ -10,6 +10,7 @@ import ( + "net/url" + "os" + "os/exec" ++ "path/filepath" + "strings" + "time" + +@@ -143,8 +144,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe + return nil, err + } + +- parts := strings.Split(meta.Path, "/") +- resp.MetaName = parts[len(parts)-1] ++ // Basename the server-provided name to prevent path traversal. ++ resp.MetaName = filepath.Base(meta.Path) + resp.MetaSize = size + } + +@@ -205,8 +206,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe + return nil, err + } + +- parts := strings.Split(rootfs.Path, "/") +- resp.RootfsName = parts[len(parts)-1] ++ // Basename the server-provided name to prevent path traversal. ++ resp.RootfsName = filepath.Base(rootfs.Path) + resp.RootfsSize = size + downloaded = true + } +@@ -219,8 +220,8 @@ func (r *ProtocolSimpleStreams) GetImageFile(fingerprint string, req ImageFileRe + return nil, err + } + +- parts := strings.Split(rootfs.Path, "/") +- resp.RootfsName = parts[len(parts)-1] ++ // Basename the server-provided name to prevent path traversal. ++ resp.RootfsName = filepath.Base(rootfs.Path) + resp.RootfsSize = size + } + } +-- +2.47.3 diff -Nru incus-6.0.4/debian/patches/148-CVE-2026-81501.patch incus-6.0.4/debian/patches/148-CVE-2026-81501.patch --- incus-6.0.4/debian/patches/148-CVE-2026-81501.patch 1970-01-01 00:00:00.000000000 +0000 +++ incus-6.0.4/debian/patches/148-CVE-2026-81501.patch 2026-09-02 16:39:28.000000000 +0000 @@ -0,0 +1,115 @@ +From a04abf23169d0597a544c6d96044a0f7aa9f19bf Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?St=C3=A9phane=20Graber?= +Date: Sun, 23 Aug 2026 17:26:26 -0400 +Subject: [PATCH] incusd/images: Check access before reusing cross-project + image +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +imageDownload reused an image from another project without checking the +caller could view it, letting a client that knew a private fingerprint +import it. Only reuse it directly when public or viewable, otherwise +download it (proving access) and dedupe against the on-disk copy. + +This addresses GHSA-c6wx-8679-hpr9 (CVE pending) + +Signed-off-by: Stéphane Graber +Rebased-by: Mathias Gibbens +--- + cmd/incusd/daemon_images.go | 38 +++++++++++++++++++++++++++++++----- + internal/server/db/images.go | 1 + + 2 files changed, 34 insertions(+), 5 deletions(-) + +diff --git a/cmd/incusd/daemon_images.go b/cmd/incusd/daemon_images.go +index 8eafb6de4..c26e1540e 100644 +--- a/cmd/incusd/daemon_images.go ++++ b/cmd/incusd/daemon_images.go +@@ -14,6 +14,7 @@ import ( + + incus "github.com/lxc/incus/v6/client" + internalIO "github.com/lxc/incus/v6/internal/io" ++ "github.com/lxc/incus/v6/internal/server/auth" + "github.com/lxc/incus/v6/internal/server/db" + "github.com/lxc/incus/v6/internal/server/db/cluster" + "github.com/lxc/incus/v6/internal/server/locking" +@@ -195,13 +196,37 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope + } + } + } else if response.IsNotFoundError(err) { ++ var otherImg *api.Image + err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error { + // Check if the image already exists in some other project. +- _, imgInfo, err = tx.GetImageFromAnyProject(ctx, fp) ++ _, otherImg, err = tx.GetImageFromAnyProject(ctx, fp) + + return err + }) + if err == nil { ++ // Only reuse another project's image when the caller may see it, ++ // otherwise download it (proving access) and dedupe on disk. ++ reuse := otherImg.Public || r == nil ++ if !reuse { ++ err = s.Authorizer.CheckPermission(ctx, r, auth.ObjectImage(otherImg.Project, otherImg.Fingerprint), auth.EntitlementCanView) ++ if err == nil { ++ reuse = true ++ } else if !api.StatusErrorCheck(err, http.StatusForbidden) { ++ return nil, false, err ++ } ++ ++ err = nil ++ } ++ ++ if reuse { ++ imgInfo = otherImg ++ } else if args.Server == "" { ++ // No source to prove access against. ++ return nil, false, api.StatusErrorf(http.StatusNotFound, "Image not found") ++ } ++ } ++ ++ if err == nil && imgInfo != nil { + var nodeAddress string + + err = s.DB.Cluster.Transaction(ctx, func(ctx context.Context, tx *db.ClusterTx) error { +@@ -317,9 +342,9 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope + return nil, false, fmt.Errorf("Invalid image fingerprint") + } + +- // Cleanup any leftover from a past attempt ++ // Download to a temporary name so an existing on-disk copy isn't overwritten. + destDir := internalUtil.VarPath("images") +- destName := filepath.Join(destDir, fp) ++ destName := filepath.Join(destDir, fp+".download") + + failure := true + cleanup := func() { +@@ -576,9 +601,12 @@ func ImageDownload(ctx context.Context, r *http.Request, s *state.State, op *ope + return nil, false, fmt.Errorf("Invalid image fingerprint") + } + +- // Check if the image path changed (private images) ++ // Reuse an existing on-disk copy if present, otherwise move ours into place. + newDestName := filepath.Join(destDir, fp) +- if newDestName != destName { ++ if util.PathExists(newDestName) { ++ _ = os.Remove(destName) ++ _ = os.Remove(destName + ".rootfs") ++ } else { + err = internalUtil.FileMove(destName, newDestName) + if err != nil { + return nil, false, err +diff --git a/internal/server/db/images.go b/internal/server/db/images.go +index 2a730e596..b3719becf 100644 +--- a/internal/server/db/images.go ++++ b/internal/server/db/images.go +@@ -436,6 +436,7 @@ func (c *ClusterTx) GetImageFromAnyProject(ctx context.Context, fingerprint stri + + object = images[0] + ++ image.Project = object.Project + image.Fingerprint = object.Fingerprint + image.Filename = object.Filename + image.Size = object.Size +-- +2.47.3 diff -Nru incus-6.0.4/debian/patches/series incus-6.0.4/debian/patches/series --- incus-6.0.4/debian/patches/series 2026-07-30 22:23:09.000000000 +0000 +++ incus-6.0.4/debian/patches/series 2026-09-02 16:39:28.000000000 +0000 @@ -54,3 +54,5 @@ 144-GHSA-m3j6-p3v3-qmjv.patch 145-GHSA-p2v3-6wvc-cv3p.patch 146-incus-7.3-fixes.patch +147-CVE-2026-81500.patch +148-CVE-2026-81501.patch