Package: release.debian.org Severity: normal Tags: trixie X-Debbugs-Cc: [email protected] Control: affects -1 + src:flatpak-builder User: [email protected] Usertags: pu
[ Reason ] Fixes for CVE-2026-86320 and GHSA-484h-v688-jq5j [ Impact ] Code execution and file access on the host when building flatpaks. [ Tests ] The fix for GHSA-484h-v688-jq5j has a unit test that is run as part of the autopkgtest. CVE-2026-86320 has a PoC that I've manually verified. [ Risks ] The largest change has good test coverage. The rest are relatively simple fixes. [ Checklist ] [x] *all* changes are documented in the d/changelog [x] I reviewed all changes and I approve them [x] attach debdiff against the package in (old)stable [x] the issue is verified as fixed in unstable [ Changes ] Backport of the upstream security fixes.
diff -Nru flatpak-builder-1.4.4/debian/changelog flatpak-builder-1.4.4/debian/changelog --- flatpak-builder-1.4.4/debian/changelog 2025-03-03 18:16:06.000000000 +0100 +++ flatpak-builder-1.4.4/debian/changelog 2026-09-17 21:31:21.000000000 +0200 @@ -1,3 +1,13 @@ +flatpak-builder (1.4.4-2+deb13u1) trixie; urgency=medium + + * Backport security fixes from 1.4.11 + - Disable git hooks when extracting module source + (CVE-2026-86320, GHSA-j5p8-jgjc-f3xv) (Closes: #1147700) + - Prevent file:// path traversal when run with --sandbox + (no known CVE ID, GHSA-484h-v688-jq5j) (Closes: #1147701) + + -- Felix Geyer <[email protected]> Thu, 17 Sep 2026 21:31:21 +0200 + flatpak-builder (1.4.4-2) unstable; urgency=medium [ Simon McVittie ] diff -Nru flatpak-builder-1.4.4/debian/patches/CVE-2026-86320.patch flatpak-builder-1.4.4/debian/patches/CVE-2026-86320.patch --- flatpak-builder-1.4.4/debian/patches/CVE-2026-86320.patch 1970-01-01 01:00:00.000000000 +0100 +++ flatpak-builder-1.4.4/debian/patches/CVE-2026-86320.patch 2026-09-17 21:31:21.000000000 +0200 @@ -0,0 +1,34 @@ +From de7e1a0580707638b21d46d0b9401088133f3a5c Mon Sep 17 00:00:00 2001 +From: bbhtt <[email protected]> +Date: Mon, 10 Aug 2026 07:20:29 +0530 +Subject: [PATCH] builder-source-patch: Disable git hooks completely when + applying patches + +git runs unsandboxed on host in this stage and a malicious source +can include a post-applypatch which will cause a code execution on host + +A better fix would be to sandbox these external commands but f-b is +not currently structured like that. +--- + src/builder-source-patch.c | 4 ++++ + 1 file changed, 4 insertions(+) + +diff --git a/src/builder-source-patch.c b/src/builder-source-patch.c +index eedd7b29..a20ecafb 100644 +--- a/src/builder-source-patch.c ++++ b/src/builder-source-patch.c +@@ -250,10 +250,14 @@ patch (GFile *dir, + args = g_ptr_array_new (); + if (use_git) { + g_ptr_array_add (args, "git"); ++ g_ptr_array_add (args, "-c"); ++ g_ptr_array_add (args, "core.hooksPath=/dev/null"); + g_ptr_array_add (args, "apply"); + g_ptr_array_add (args, "-v"); + } else if (use_git_am) { + g_ptr_array_add (args, "git"); ++ g_ptr_array_add (args, "-c"); ++ g_ptr_array_add (args, "core.hooksPath=/dev/null"); + g_ptr_array_add (args, "am"); + g_ptr_array_add (args, "--keep-cr"); + } else { diff -Nru flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-1.patch flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-1.patch --- flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-1.patch 1970-01-01 01:00:00.000000000 +0100 +++ flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-1.patch 2026-09-17 21:20:56.000000000 +0200 @@ -0,0 +1,74 @@ +From f4a082e6ba1f2613f850508e3e895e4bf66a2011 Mon Sep 17 00:00:00 2001 +From: bbhtt <[email protected]> +Date: Wed, 29 Jul 2026 09:19:58 +0530 +Subject: [PATCH] builder-source-bzr,svn: Validate existence of URL during + parsing + +Previously it was deep inside download methods. +--- + src/builder-source-bzr.c | 13 +++++++++++++ + src/builder-source-svn.c | 13 +++++++++++++ + 2 files changed, 26 insertions(+) + +diff --git a/src/builder-source-bzr.c b/src/builder-source-bzr.c +index 244db912..b67a45bb 100644 +--- a/src/builder-source-bzr.c ++++ b/src/builder-source-bzr.c +@@ -365,6 +365,18 @@ builder_source_bzr_update (BuilderSource *source, + return TRUE; + } + ++static gboolean ++builder_source_bzr_validate (BuilderSource *source, ++ GError **error) ++{ ++ BuilderSourceBzr *self = BUILDER_SOURCE_BZR (source); ++ ++ if (self->url == NULL) ++ return flatpak_fail (error, "URL not specified for bzr source"); ++ ++ return TRUE; ++} ++ + static void + builder_source_bzr_class_init (BuilderSourceBzrClass *klass) + { +@@ -380,6 +392,7 @@ builder_source_bzr_class_init (BuilderSourceBzrClass *klass) + source_class->bundle = builder_source_bzr_bundle; + source_class->update = builder_source_bzr_update; + source_class->checksum = builder_source_bzr_checksum; ++ source_class->validate = builder_source_bzr_validate; + + g_object_class_install_property (object_class, + PROP_URL, +diff --git a/src/builder-source-svn.c b/src/builder-source-svn.c +index c329f50b..905ebc50 100644 +--- a/src/builder-source-svn.c ++++ b/src/builder-source-svn.c +@@ -390,6 +390,18 @@ builder_source_svn_update (BuilderSource *source, + return TRUE; + } + ++static gboolean ++builder_source_svn_validate (BuilderSource *source, ++ GError **error) ++{ ++ BuilderSourceSvn *self = BUILDER_SOURCE_SVN (source); ++ ++ if (self->url == NULL) ++ return flatpak_fail (error, "URL not specified for svn source"); ++ ++ return TRUE; ++} ++ + static void + builder_source_svn_class_init (BuilderSourceSvnClass *klass) + { +@@ -405,6 +417,7 @@ builder_source_svn_class_init (BuilderSourceSvnClass *klass) + source_class->bundle = builder_source_svn_bundle; + source_class->update = builder_source_svn_update; + source_class->checksum = builder_source_svn_checksum; ++ source_class->validate = builder_source_svn_validate; + + g_object_class_install_property (object_class, + PROP_URL, diff -Nru flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-2.patch flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-2.patch --- flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-2.patch 1970-01-01 01:00:00.000000000 +0100 +++ flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-2.patch 2026-09-17 21:31:21.000000000 +0200 @@ -0,0 +1,873 @@ +From 99d344ee02c8597ee0a6bbb22be94bfb88e54080 Mon Sep 17 00:00:00 2001 +From: bbhtt <[email protected]> +Date: Wed, 15 Jul 2026 11:22:23 +0530 +Subject: [PATCH] builder-source: Ensure no source can escape base_dir in + sandboxed mode + +This extends the features provided by sandboxed mode flag to source +subclasses missing it previously. +--- + src/builder-context.c | 53 ++ + src/builder-context.h | 8 + + src/builder-source-archive.c | 37 +- + src/builder-source-bzr.c | 12 +- + src/builder-source-file.c | 12 + + src/builder-source-git.c | 24 + + src/builder-source-patch.c | 24 +- + src/builder-source-svn.c | 12 +- + tests/Makefile.am.inc | 1 + + tests/meson.build | 1 + + .../test-sandboxed-mode-source-resolution.sh | 485 ++++++++++++++++++ + 11 files changed, 654 insertions(+), 15 deletions(-) + create mode 100755 tests/test-sandboxed-mode-source-resolution.sh + +diff --git a/src/builder-context.c b/src/builder-context.c +index 1467cc7c..d2153b83 100644 +--- a/src/builder-context.c ++++ b/src/builder-context.c +@@ -1257,6 +1257,59 @@ builder_context_get_as_url_policy (BuilderContext *self) + return self->sdk_config; + } + ++gboolean ++builder_context_resolve_source_uri (BuilderContext *self, ++ const char *url, ++ GFile **out_file, ++ GError **error) ++{ ++ const char *scheme; ++ ++ *out_file = NULL; ++ ++ scheme = g_uri_peek_scheme (url); ++ if (scheme == NULL) ++ return flatpak_fail (error, "Failed to get scheme from URL: '%s'", url); ++ ++ if (g_strcmp0 (scheme, "file") == 0) ++ { ++ g_autoptr(GFile) file = g_file_new_for_uri (url); ++ ++ if (!builder_context_ensure_file_sandboxed (self, file, error)) ++ { ++ g_prefix_error (error, "Unable to use local URL '%s': ", url); ++ return FALSE; ++ } ++ ++ *out_file = g_steal_pointer (&file); ++ } ++ ++ return TRUE; ++} ++ ++char * ++builder_context_resolve_repo_location (BuilderContext *self, ++ const char *url, ++ GError **error) ++{ ++ ++ g_autoptr(GFile) local_file = NULL; ++ ++ if (url == NULL) ++ { ++ g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified"); ++ return NULL; ++ } ++ ++ if (!builder_context_resolve_source_uri (self, url, &local_file, error)) ++ return NULL; ++ ++ if (local_file != NULL) ++ return g_file_get_path (local_file); ++ ++ return g_strdup (url); ++} ++ + BuilderContext * + builder_context_new (GFile *run_dir, + GFile *app_dir, +diff --git a/src/builder-context.h b/src/builder-context.h +index f6c12329..21a9132c 100644 +--- a/src/builder-context.h ++++ b/src/builder-context.h +@@ -190,6 +190,14 @@ BuilderSdkConfig * builder_context_get_sdk_config (BuilderContext *self); + + BuilderSdkConfig * builder_context_get_sdk_config (BuilderContext *self); + ++gboolean builder_context_resolve_source_uri (BuilderContext *self, ++ const char *url, ++ GFile **out_file, ++ GError **error); ++char * builder_context_resolve_repo_location (BuilderContext *self, ++ const char *url, ++ GError **error); ++ + G_DEFINE_AUTOPTR_CLEANUP_FUNC (BuilderContext, g_object_unref) + + G_END_DECLS +diff --git a/src/builder-source-archive.c b/src/builder-source-archive.c +index b45b17bf..3f1eb308 100644 +--- a/src/builder-source-archive.c ++++ b/src/builder-source-archive.c +@@ -406,14 +406,35 @@ get_source_file (BuilderSourceArchive *self, + + if (self->url != NULL && self->url[0] != 0) + { ++ g_autoptr(GFile) local_file = NULL; ++ ++ if (!builder_context_resolve_source_uri (context, self->url, &local_file, error)) ++ return NULL; ++ ++ if (local_file != NULL) ++ { ++ *is_local = TRUE; ++ return g_steal_pointer (&local_file); ++ } ++ + *is_local = FALSE; + return get_download_location (self, context, is_local, error); + } + + if (self->path != NULL && self->path[0] != 0) + { ++ g_autoptr(GFile) file = NULL; ++ + *is_local = TRUE; +- return g_file_resolve_relative_path (base_dir, self->path); ++ file = g_file_resolve_relative_path (base_dir, self->path); ++ ++ if (!builder_context_ensure_parent_dir_sandboxed (context, file, error)) ++ { ++ g_prefix_error (error, "Unable to get source file '%s': ", self->path); ++ return NULL; ++ } ++ ++ return g_steal_pointer (&file); + } + + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "source file path or url not specified"); +@@ -472,6 +493,20 @@ builder_source_archive_download (BuilderSource *source, + return FALSE; + } + ++ if (self->mirror_urls != NULL) ++ { ++ for (size_t i = 0; self->mirror_urls[i] != NULL; i++) ++ { ++ g_autoptr(GFile) mirror_local_file = NULL; ++ ++ if (!builder_context_resolve_source_uri (context, self->mirror_urls[i], &mirror_local_file, error)) ++ { ++ g_prefix_error (error, "Invalid mirror-urls entry '%s': ", self->mirror_urls[i]); ++ return FALSE; ++ } ++ } ++ } ++ + if (!builder_context_download_uri (context, + self->url, + (const char **)self->mirror_urls, +diff --git a/src/builder-source-bzr.c b/src/builder-source-bzr.c +index b67a45bb..b35fbdd6 100644 +--- a/src/builder-source-bzr.c ++++ b/src/builder-source-bzr.c +@@ -174,12 +174,12 @@ builder_source_bzr_download (BuilderSource *source, + BuilderSourceBzr *self = BUILDER_SOURCE_BZR (source); + + g_autoptr(GFile) mirror_dir = NULL; ++ g_autofree char *repo_location = NULL; + +- if (self->url == NULL) +- { +- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified"); +- return FALSE; +- } ++ repo_location = builder_context_resolve_repo_location (context, self->url, error); ++ ++ if (repo_location == NULL) ++ return FALSE; + + mirror_dir = get_mirror_dir (self, context); + +@@ -205,7 +205,7 @@ builder_source_bzr_download (BuilderSource *source, + mirror_dir_tmp = g_file_new_for_path (path_tmp); + filename_tmp = g_file_get_basename (mirror_dir_tmp); + +- branch_source = self->url; ++ branch_source = repo_location; + + cached_bzr_dir = builder_context_find_in_sources_dirs (context, "bzr", filename, NULL); + if (cached_bzr_dir != NULL) +diff --git a/src/builder-source-file.c b/src/builder-source-file.c +index 25434491..d4e57291 100644 +--- a/src/builder-source-file.c ++++ b/src/builder-source-file.c +@@ -321,6 +321,18 @@ get_source_file (BuilderSourceFile *self, + + if (self->url != NULL && self->url[0] != 0) + { ++ g_autoptr(GFile) local_file = NULL; ++ ++ if (!builder_context_resolve_source_uri (context, self->url, &local_file, error)) ++ return NULL; ++ ++ if (local_file != NULL) ++ { ++ *is_local = TRUE; ++ *is_inline = FALSE; ++ return g_steal_pointer (&local_file); ++ } ++ + *is_local = FALSE; + return get_download_location (self, is_inline, context, error); + } +diff --git a/src/builder-source-git.c b/src/builder-source-git.c +index cf26ff9b..8d204706 100644 +--- a/src/builder-source-git.c ++++ b/src/builder-source-git.c +@@ -235,14 +235,38 @@ get_url_or_path (BuilderSourceGit *self, + if (scheme == NULL) + { + repo = g_file_resolve_relative_path (base_dir, self->url); ++ ++ if (!builder_context_ensure_parent_dir_sandboxed (context, repo, error)) ++ { ++ g_prefix_error (error, "Unable to get source for git url '%s': ", self->url); ++ return NULL; ++ } ++ + return g_file_get_uri (repo); + } + ++ if (g_strcmp0 (scheme, "file") == 0) ++ { ++ g_autoptr(GFile) local_file = NULL; ++ ++ if (!builder_context_resolve_source_uri (context, self->url, &local_file, error)) ++ return NULL; ++ ++ return g_file_get_uri (local_file); ++ } ++ + return g_strdup (self->url); + } + + repo = g_file_resolve_relative_path (base_dir, + self->path); ++ ++ if (!builder_context_ensure_parent_dir_sandboxed (context, repo, error)) ++ { ++ g_prefix_error (error, "Unable to get source file '%s': ", self->path); ++ return NULL; ++ } ++ + return g_file_get_path (repo); + } + +diff --git a/src/builder-source-patch.c b/src/builder-source-patch.c +index a20ecafb..48a9ace3 100644 +--- a/src/builder-source-patch.c ++++ b/src/builder-source-patch.c +@@ -168,12 +168,32 @@ get_source_files (BuilderSourcePatch *self, + int i; + + if ((self->path != NULL && self->path[0] != 0)) +- g_ptr_array_add (res, g_file_resolve_relative_path (base_dir, self->path)); ++ { ++ g_autoptr(GFile) file = g_file_resolve_relative_path (base_dir, self->path); ++ ++ if (!builder_context_ensure_file_sandboxed (context, file, error)) ++ { ++ g_prefix_error (error, "Unable to get source file '%s': ", self->path); ++ return NULL; ++ } ++ ++ g_ptr_array_add (res, g_steal_pointer (&file)); ++ } + + if (self->paths != NULL) + { + for (i = 0; self->paths[i] != NULL; i++) +- g_ptr_array_add (res, g_file_resolve_relative_path (base_dir, self->paths[i])); ++ { ++ g_autoptr(GFile) file = g_file_resolve_relative_path (base_dir, self->paths[i]); ++ ++ if (!builder_context_ensure_file_sandboxed (context, file, error)) ++ { ++ g_prefix_error (error, "Unable to get source file '%s': ", self->paths[i]); ++ return NULL; ++ } ++ ++ g_ptr_array_add (res, g_steal_pointer (&file)); ++ } + } + + if (res->len == 0) +diff --git a/src/builder-source-svn.c b/src/builder-source-svn.c +index 905ebc50..773f5b20 100644 +--- a/src/builder-source-svn.c ++++ b/src/builder-source-svn.c +@@ -208,14 +208,14 @@ builder_source_svn_download (BuilderSource *source, + BuilderSourceSvn *self = BUILDER_SOURCE_SVN (source); + g_autoptr(GFile) parent = NULL; + g_autofree char *filename = NULL; ++ g_autofree char *repo_location = NULL; + + g_autoptr(GFile) mirror_dir = NULL; + +- if (self->url == NULL) +- { +- g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "URL not specified"); +- return FALSE; +- } ++ repo_location = builder_context_resolve_repo_location (context, self->url, error); ++ ++ if (repo_location == NULL) ++ return FALSE; + + mirror_dir = get_mirror_dir (self, context, self->revision); + parent = g_file_get_parent (mirror_dir); +@@ -259,7 +259,7 @@ builder_source_svn_download (BuilderSource *source, + if (!svn (parent, NULL, error, + "checkout", "--non-interactive", + "-r", self->revision ? self->revision : "HEAD", +- self->url, filename_tmp, NULL)) ++ repo_location, filename_tmp, NULL)) + return FALSE; + } + +diff --git a/tests/Makefile.am.inc b/tests/Makefile.am.inc +index 8cd31ae6..86a03d0d 100644 +--- a/tests/Makefile.am.inc ++++ b/tests/Makefile.am.inc +@@ -90,6 +90,7 @@ dist_test_scripts = \ + tests/test-builder.sh \ + tests/test-builder-deprecated.sh \ + tests/test-builder-python.sh \ ++ tests/test-sandboxed-mode-source-resolution.sh \ + $(NULL) + + @VALGRIND_CHECK_RULES@ +diff --git a/tests/meson.build b/tests/meson.build +index aec77d17..66ae6dbc 100644 +--- a/tests/meson.build ++++ b/tests/meson.build +@@ -14,6 +14,7 @@ test_names = [ + 'test-builder', + 'test-builder-deprecated', + 'test-builder-python', ++ 'test-sandboxed-mode-source-resolution', + ] + + tap_test = find_program( +diff --git a/tests/test-sandboxed-mode-source-resolution.sh b/tests/test-sandboxed-mode-source-resolution.sh +new file mode 100755 +index 00000000..e945e0f5 +--- /dev/null ++++ b/tests/test-sandboxed-mode-source-resolution.sh +@@ -0,0 +1,506 @@ ++#!/bin/bash ++# ++# Copyright (C) 2026 Boudhayan Bhattacharya <[email protected]> ++# ++# This library is free software; you can redistribute it and/or ++# modify it under the terms of the GNU Lesser General Public ++# License as published by the Free Software Foundation; either ++# version 2 of the License, or (at your option) any later version. ++# ++# This library is distributed in the hope that it will be useful, ++# but WITHOUT ANY WARRANTY; without even the implied warranty of ++# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU ++# Lesser General Public License for more details. ++# ++# You should have received a copy of the GNU Lesser General Public ++# License along with this library; if not, write to the ++# Free Software Foundation, Inc., 59 Temple Place - Suite 330, ++# Boston, MA 02111-1307, USA. ++ ++ ++set -euo pipefail ++ ++. $(dirname $0)/libtest.sh ++ ++skip_without_fuse ++ ++echo "1..15" ++ ++setup_repo ++install_repo ++setup_sdk_repo ++install_sdk_repo ++ ++cd "$TEST_DATA_DIR" ++ ++_run_build () { ++ if [ -n "${BUILD_LOG:-}" ]; then ++ ${FLATPAK_BUILDER} --force-clean \ ++ "appdir" "$@" >&2 2>"$BUILD_LOG" ++ else ++ ${FLATPAK_BUILDER} --force-clean \ ++ "appdir" "$@" >&2 ++ fi ++} ++ ++run_build () { ++ _run_build "$@" ++} ++ ++run_build_fail () { ++ if _run_build "$@"; then ++ echo "build of ${!#} unexpectedly succeeded" >&2 ++ exit 1 ++ fi ++} ++ ++ ++mkdir file-workdir ++cd file-workdir ++ ++CURDIR="$(pwd)" ++ ++echo "test" > test.txt ++ ++SHA=$(sha256sum test.txt | awk '{print $1}') ++ ++cat > test-path-inside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_path_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp test.txt /app/test.txt" ++ ], ++ "sources": [{ ++ "type": "file", ++ "path": "test.txt" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-path-inside.json ++assert_has_file appdir/files/test.txt ++assert_file_has_content appdir/files/test.txt 'test' ++ ++echo "ok path source inside manifest tree works for file type in sandboxed mode" ++ ++cat > test-file-url-inside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_file_url_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp test.txt /app/test.txt" ++ ], ++ "sources": [{ ++ "type": "file", ++ "url": "file://$CURDIR/test.txt", ++ "sha256": "$SHA" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-file-url-inside.json ++assert_has_file appdir/files/test.txt ++assert_file_has_content appdir/files/test.txt 'test' ++ ++echo "ok file URI source inside manifest tree works for file type in sandboxed mode" ++ ++mkdir tmp_1 ++cd tmp_1 ++ ++cat > test-path-outside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_path_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "file", ++ "path": "../test.txt" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-file-path-outside run_build_fail --sandbox \ ++ test-path-outside.json ++assert_file_has_content build-log-file-path-outside 'not inside manifest directory' ++ ++echo "ok path source outside manifest tree is rejected for file type in sandboxed mode" ++ ++cat > test-file-url-outside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_file_url_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "file", ++ "url": "file://$CURDIR/test.txt", ++ "sha256": "$SHA" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-file-url-outside run_build_fail --sandbox \ ++ test-file-url-outside.json ++assert_file_has_content build-log-file-url-outside 'not inside manifest directory' ++ ++echo "ok file URI source outside manifest tree is rejected for file type in sandboxed mode" ++ ++cd "$TEST_DATA_DIR" ++mkdir archive-workdir ++cd archive-workdir ++ ++CURDIR="$(pwd)" ++ ++mkdir -p archive-src ++echo "archived content" > archive-src/file.txt ++tar -czf archive-src.tar.gz -C archive-src . ++ ++SHA=$(sha256sum archive-src.tar.gz | awk '{print $1}') ++ ++cat > test-archive-path-inside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_archive_path_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp file.txt /app/file.txt" ++ ], ++ "sources": [{ ++ "type": "archive", ++ "path": "archive-src.tar.gz" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-archive-path-inside.json ++assert_has_file appdir/files/file.txt ++assert_file_has_content appdir/files/file.txt 'archived content' ++ ++echo "ok path source inside manifest tree works for archive type in sandboxed mode" ++ ++cat > test-archive-file_uri-inside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_archive_file_uri_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp file.txt /app/file.txt" ++ ], ++ "sources": [{ ++ "type": "archive", ++ "url": "file://$CURDIR/archive-src.tar.gz", ++ "sha256": "$SHA" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-archive-file_uri-inside.json ++assert_has_file appdir/files/file.txt ++assert_file_has_content appdir/files/file.txt 'archived content' ++ ++echo "ok file URI source inside manifest tree works for archive type in sandboxed mode" ++ ++mkdir tmp_1 ++cd tmp_1 ++ ++cat > test-archive-path-outside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_archive_path_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "archive", ++ "path": "../archive-src.tar.gz" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-archive-path-outside run_build_fail --sandbox \ ++ test-archive-path-outside.json ++assert_file_has_content build-log-archive-path-outside 'not inside manifest directory' ++ ++echo "ok path source outside manifest tree is rejected for archive type in sandboxed mode" ++ ++cat > test-archive-file_uri-outside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_archive_file_uri_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "archive", ++ "url": "file://$CURDIR/archive-src.tar.gz", ++ "sha256": "$SHA" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-archive-file_uri-outside run_build_fail --sandbox \ ++ test-archive-file_uri-outside.json ++assert_file_has_content build-log-archive-file_uri-outside 'not inside manifest directory' ++ ++echo "ok file URI source outside manifest tree is rejected for archive type in sandboxed mode" ++ ++cp ../archive-src.tar.gz . ++ ++cat > test-archive-mirror_uri-outside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_archive_mirror_uri_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "archive", ++ "url": "https://example.org/archive-src.tar.gz", ++ "mirror-urls": ["file://$CURDIR/archive-src.tar.gz"], ++ "sha256": "$SHA" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-archive-mirror_uri-outside run_build_fail --sandbox \ ++ test-archive-mirror_uri-outside.json ++assert_file_has_content build-log-archive-mirror_uri-outside 'not inside manifest directory' ++ ++echo "ok mirror URI source outside manifest tree is rejected for archive type in sandboxed mode" ++ ++cd "$TEST_DATA_DIR" ++mkdir patch-workdir ++cd patch-workdir ++ ++mkdir -p archive-src ++echo "archived content" > archive-src/file.txt ++tar -czf archive-src.tar.gz -C archive-src . ++ ++cat > fix.patch <<'EOF' ++--- a/file.txt +++++ b/file.txt ++@@ -1 +1 @@ ++-archived content +++patched content ++EOF ++ ++cat > test-patch-path-inside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_patch_path_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp file.txt /app" ++ ], ++ "sources": [ ++ { ++ "type": "archive", ++ "path": "archive-src.tar.gz" ++ }, ++ { ++ "type": "patch", ++ "path": "fix.patch" ++ } ++ ] ++ }] ++} ++EOF ++ ++run_build --sandbox test-patch-path-inside.json ++assert_has_file appdir/files/file.txt ++assert_file_has_content appdir/files/file.txt 'patched content' ++ ++echo "ok path source inside manifest tree works for patch type in sandboxed mode" ++ ++mkdir tmp_1 ++cp archive-src.tar.gz tmp_1/ ++cd tmp_1 ++ ++cat > test-patch-path-outside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_patch_path_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [ ++ { ++ "type": "archive", ++ "path": "archive-src.tar.gz" ++ }, ++ { ++ "type": "patch", ++ "path": "../fix.patch" ++ } ++ ] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-patch-path-outside run_build_fail --sandbox \ ++ test-patch-path-outside.json ++assert_file_has_content build-log-patch-path-outside 'not inside manifest directory' ++ ++echo "ok path source outside manifest tree is rejected for patch type in sandboxed mode" ++ ++cd "$TEST_DATA_DIR" ++mkdir git-workdir ++cd git-workdir ++ ++CURDIR="$(pwd)" ++ ++mkdir git-repo ++( cd git-repo \ ++ && git -c init.defaultBranch=test init -q \ ++ && git config --local user.email "[email protected]" \ ++ && git config --local user.name "test" \ ++ && echo "git content" > file.txt \ ++ && git add file.txt \ ++ && git commit -q -m "Add file" ) ++ ++ ++cat > test-git-path-inside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_git_path_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp file.txt /app/file.txt" ++ ], ++ "sources": [{ ++ "type": "git", ++ "path": "git-repo", ++ "branch": "test" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-git-path-inside.json ++assert_has_file appdir/files/file.txt ++assert_file_has_content appdir/files/file.txt 'git content' ++ ++echo "ok path source inside manifest tree works for git type in sandboxed mode" ++ ++cat > test-git-file_uri-inside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_git_file_uri_inside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [ ++ "cp file.txt /app/file.txt" ++ ], ++ "sources": [{ ++ "type": "git", ++ "url": "file://$CURDIR/git-repo", ++ "branch": "test" ++ }] ++ }] ++} ++EOF ++ ++run_build --sandbox test-git-file_uri-inside.json ++assert_has_file appdir/files/file.txt ++assert_file_has_content appdir/files/file.txt 'git content' ++ ++echo "ok file URI source inside manifest tree works for git type in sandboxed mode" ++ ++mkdir tmp_1 ++cd tmp_1 ++ ++cat > test-git-path-outside.json <<'EOF' ++{ ++ "app-id": "org.test.sandbox_git_path_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "git", ++ "path": "../git-repo", ++ "branch": "test" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-git-path-outside run_build_fail --sandbox \ ++ test-git-path-outside.json ++assert_file_has_content build-log-git-path-outside 'not inside manifest directory' ++ ++echo "ok path source outside manifest tree is rejected for git type in sandboxed mode" ++ ++cat > test-git-file_uri-outside.json <<EOF ++{ ++ "app-id": "org.test.sandbox_git_file_uri_outside", ++ "runtime": "org.test.Platform", ++ "sdk": "org.test.Sdk", ++ "modules": [{ ++ "name": "test", ++ "buildsystem": "simple", ++ "build-commands": [], ++ "sources": [{ ++ "type": "git", ++ "url": "file://$CURDIR/git-repo", ++ "branch": "test" ++ }] ++ }] ++} ++EOF ++ ++BUILD_LOG=build-log-git-file_uri-outside run_build_fail --sandbox \ ++ test-git-file_uri-outside.json ++assert_file_has_content build-log-git-file_uri-outside 'not inside manifest directory' ++ ++echo "ok file URI source outside manifest tree is rejected for git type in sandboxed mode" ++ diff -Nru flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-3.patch flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-3.patch --- flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-3.patch 1970-01-01 01:00:00.000000000 +0100 +++ flatpak-builder-1.4.4/debian/patches/GHSA-484h-v688-jq5j-3.patch 2026-09-17 21:30:47.000000000 +0200 @@ -0,0 +1,114 @@ +From f18fcd4069ed5c33566527fea5a18634b1d6f3f4 Mon Sep 17 00:00:00 2001 +From: bbhtt <[email protected]> +Date: Sat, 12 Sep 2026 16:33:47 +0530 +Subject: [PATCH] builder-source-archive,file: Abort on invalid checksum + strings + +The checksum string is user controlled and used in various places +to control paths. We should verify the string early to make sure +it matches the expected hash format and length, to avoid potential +path traversal issues. +--- + src/builder-source-archive.c | 2 +- + src/builder-source-file.c | 2 +- + src/builder-utils.c | 43 ++++++++++++++++++++++++++++++++++++ + src/builder-utils.h | 9 ++++++++ + 4 files changed, 54 insertions(+), 2 deletions(-) + +diff --git a/src/builder-source-archive.c b/src/builder-source-archive.c +index 3f1eb308..1db159b1 100644 +--- a/src/builder-source-archive.c ++++ b/src/builder-source-archive.c +@@ -320,7 +320,7 @@ builder_source_archive_validate (BuilderSource *source, + strchr (self->dest_filename, '/') != NULL) + return flatpak_fail (error, "No slashes allowed in dest-filename, use dest property for directory"); + +- return TRUE; ++ return builder_validate_source_checksums (self->md5, self->sha1, self->sha256, self->sha512, error); + } + + static GUri * +diff --git a/src/builder-source-file.c b/src/builder-source-file.c +index d4e57291..d2dcdfa1 100644 +--- a/src/builder-source-file.c ++++ b/src/builder-source-file.c +@@ -231,7 +231,7 @@ builder_source_file_validate (BuilderSource *source, + strchr (self->dest_filename, '/') != NULL) + return flatpak_fail (error, "No slashes allowed in dest-filename, use dest property for directory"); + +- return TRUE; ++ return builder_validate_source_checksums (self->md5, self->sha1, self->sha256, self->sha512, error); + } + + +diff --git a/src/builder-utils.c b/src/builder-utils.c +index 5a097250..6913c017 100644 +--- a/src/builder-utils.c ++++ b/src/builder-utils.c +@@ -2088,3 +2088,46 @@ appstream_has_version (int major, + + return FALSE; + } ++ ++gboolean ++builder_validate_checksum_string (const char *arg, ++ gsize len) ++{ ++ if (arg == NULL || strlen (arg) != len) ++ return FALSE; ++ ++ while (*arg != '\0') ++ { ++ if (!g_ascii_isxdigit (*arg)) ++ return FALSE; ++ arg++; ++ } ++ ++ return TRUE; ++} ++ ++gboolean ++builder_validate_source_checksums (const char *md5, ++ const char *sha1, ++ const char *sha256, ++ const char *sha512, ++ GError **error) ++{ ++ if (md5 != NULL && md5[0] != '\0' && ++ !builder_validate_checksum_string (md5, 32)) ++ return flatpak_fail (error, "Invalid md5 checksum string"); ++ ++ if (sha1 != NULL && sha1[0] != '\0' && ++ !builder_validate_checksum_string (sha1, 40)) ++ return flatpak_fail (error, "Invalid sha1 checksum string"); ++ ++ if (sha256 != NULL && sha256[0] != '\0' && ++ !builder_validate_checksum_string (sha256, 64)) ++ return flatpak_fail (error, "Invalid sha256 checksum string"); ++ ++ if (sha512 != NULL && sha512[0] != '\0' && ++ !builder_validate_checksum_string (sha512, 128)) ++ return flatpak_fail (error, "Invalid sha512 checksum string"); ++ ++ return TRUE; ++} +diff --git a/src/builder-utils.h b/src/builder-utils.h +index 5e4a11d7..e2a5cfb8 100644 +--- a/src/builder-utils.h ++++ b/src/builder-utils.h +@@ -224,6 +224,15 @@ gboolean appstream_has_version (int major, + int minor, + int micro); + ++gboolean builder_validate_checksum_string (const char *arg, ++ gsize len); ++ ++gboolean builder_validate_source_checksums (const char *md5, ++ const char *sha1, ++ const char *sha256, ++ const char *sha512, ++ GError **error); ++ + G_DEFINE_AUTOPTR_CLEANUP_FUNC (FlatpakXml, flatpak_xml_free); + + G_END_DECLS diff -Nru flatpak-builder-1.4.4/debian/patches/series flatpak-builder-1.4.4/debian/patches/series --- flatpak-builder-1.4.4/debian/patches/series 1970-01-01 01:00:00.000000000 +0100 +++ flatpak-builder-1.4.4/debian/patches/series 2026-09-17 21:31:21.000000000 +0200 @@ -0,0 +1,4 @@ +CVE-2026-86320.patch +GHSA-484h-v688-jq5j-1.patch +GHSA-484h-v688-jq5j-2.patch +GHSA-484h-v688-jq5j-3.patch

