Nuitka/Nuitka

Tool downloads are fetched and executed without integrity verification

Aberta

#3.997 aberto em 12 de ago. de 2026

 (3 comentários) (0 reação) (0 responsável)Python (565 forks)batch import
enhancementexcellent_reporthelp wanted

Métricas do repositório

Stars
 (10.351 estrelas)
Métricas de merge de PR
 (Métricas PR pendentes)

Description

🐛 Bug Description

Every external tool Nuitka fetches at build time goes through getCachedDownload() in nuitka/utils/Download.py, and that helper performs no integrity verification of any kind: it has no checksum parameter, and nothing in the module computes or compares a digest or signature after download.

The fetched files are executables that then run inside the build (ccache as the compiler wrapper on macOS, the winlibs MinGW64 gcc as the compiler itself on Windows, depends.exe, appimagetool, NSIS).

With --assume-yes-for-downloads this happens non-interactively in CI, so the integrity of every binary these pipelines execute rests entirely on the TLS connection to nuitka.net (or the respective mirror) at download time. A compromised host or an intercepting middlebox can substitute an executable that runs with full build privileges, inside pipelines whose output is often signed and published.

🖥️ Environment

1. Nuitka Version, Python Version, OS, and Platform

4.1.3
Commercial: None
Python: 3.14.6 (main, Jul 23 2026, 14:49:22) [Clang 22.1.3 ]
Flavor: Python Build Standalone
GIL: yes
Executable: .venv/bin/python
OS: Darwin
Arch: x86_64
macOSRelease: 26.6.1
Version C compiler: clang (clang 21.0.0).

The same behavior is confirmed by reading the 4.1.3 release source (links below), and observed on GitHub-hosted macos-26 and windows-2025 runners.

2. How Nuitka and Python were Installed

Installed from PyPI (uvx nuitka==4.1.3, equivalent to pip install nuitka==4.1.3) into a virtualenv.

3. Relevant PyPI Packages and Versions

Not applicable: no third-party package is involved. The report concerns Nuitka's own download helper.

🛠️ To Reproduce

1. "Hello World" Test (if applicable)

Not a compilation failure: compilation succeeds. The issue is what happens on the way.

2. Short, Self-Contained, Correct, Eligible (SSCCE) Example

Any compilation on a machine where a downloadable tool is missing triggers it. On a macOS machine without ccache on PATH:

# hello.py
print("hello world")

3. Nuitka Command Line Options

python -m nuitka --mode=onefile --assume-yes-for-downloads hello.py

The log shows the download and immediate use, with no verification step in between:

Nuitka: Downloading 'https://nuitka.net/ccache/v4.2.1/ccache-4.2.1.zip'.
Nuitka: Extracting to '~/Library/Caches/Nuitka/downloads/ccache/v4.2.1/ccache'
Nuitka-Scons: Backend C compiler: clang (clang 21.0.0).
Nuitka-Scons: Compiled 470 C files using ccache.

📉 Expected Behavior

Each downloaded artifact is verified against a digest pinned next to its URL before extraction and execution: getCachedDownload() grows a sha256 (or similar) parameter, each call site pins the expected digest of the exact version it requests, and a mismatch aborts with a clear error instead of running the file. Since every URL already pins an exact version (v4.2.1, a specific winlibs build), the digests are stable and only change when the pinned version does.

📄 Actual Behavior & Output

The helper's full signature in 4.1.3 has no integrity-related parameter, and the module contains no digest or signature check (no match for sha, md5, checksum, or verify anywhere in the file):

https://github.com/Nuitka/Nuitka/blob/95b12dd9ae354e83bf1807bfe017e78b46ba57a0/nuitka/utils/Download.py#L173-L185

def getCachedDownload(
    name,
    url,
    binary,
    unzip,
    flatten,
    is_arch_specific,
    specificity,
    message,
    reject,
    assume_yes_for_downloads,
    download_ok,
):

Call sites that fetch executables through it, none passing any digest:

↩️ Regression (if applicable)

Not a regression: the helper has behaved this way for as long as I can trace.

💡 Additional Context (Optional)

Two observations from running Nuitka in GitHub Actions that make TLS-only trust weaker than it looks:

  • Hosted runners sit behind TLS-intercepting infrastructure. I have a release job that failed on a one-off CERTIFICATE_VERIFY_FAILED: self-signed certificate from a runner's proxy while fetching an unrelated tool: the machine in the middle is not hypothetical, it is part of the platform.
  • These downloads execute inside pipelines that produce signed, attested, published artifacts. In my case (kdeldycke/repomatic compiles standalone binaries for six targets and attests them), I ended up passing --disable-ccache on macOS specifically to take the unverified download out of the supply chain, at the cost of losing ccache there.

Pinned digests at the call sites would remove the gap for everyone without changing any default behavior. If a PR adding a required digest parameter to getCachedDownload() and pinning the current artifacts' hashes would be welcome, I am happy to discuss the shape it should take.

Guia do colaborador