Skip to content

Release integrity: unpinned Deno, no asset existence checks, silently ignored PowerShell failures, unpinned trivy action #517

Description

@thcp

Four verified gaps in the release path. Grouped because they share a theme: things that ship without being checked.

1. Deno is fetched from a mutable latest URL with no checksum, into every published image

build/Dockerfile:38:

curl -fsSL -o /tmp/deno.zip "https://github.com/denoland/deno/releases/latest/download/deno-${DENO_ARCH}.zip"

No version pin, no SHA256. Every GHCR image build takes whatever Deno published that day and ships it to users. Deno is not incidental here -- yt-dlp uses it as the JS runtime for YouTube format extraction, so it executes against untrusted input.

This is inconsistent with the project's own standard. scripts/macos/make-runtime-pack.sh:186-194 pins QuickJS by asset and SHA256 and verifies it:

QJS_SHA256="f6200e9856c45578a5d42ac873a32f3f994b421e29df9f63b452d9c7145015fc"
echo "${QJS_SHA256}  ${QJS_DIR}/qjs" | shasum -a 256 -c - >/dev/null || { ... exit 1; }

The scripts even carry the rationale in a comment: an unverified binary fetched at package time is a supply-chain hole whether or not it is small. Docker/Deno is the one path that does not follow it.

Fix: pin a Deno version and verify its SHA256, matching the QuickJS pattern.

2. Linux and Windows releases can publish an incomplete asset set and stay green

linux-release.yml:145, windows-release.yml:96 use softprops/action-gh-release with no fail_on_unmatched_files (it defaults to false) and no pre-upload existence assertions.

Verified asymmetry: macos-release.yml has 8 test -f checks before uploading; the Linux and Windows workflows have none.

The updater assets are produced only by the one invocation carrying PUBLISH_UPDATER_ASSETS=1 / -PublishUpdaterAssets. If that step is edited, reordered, or its build leg fails in a way the script swallows (see 3), StemDeck-Linux-x64-app.tar.gz, *-app.zip and *-runtime-version.json never appear -- the release publishes without them, the job goes green, and the in-app updater 404s for every installed user.

Fix: add test -f assertions mirroring the macOS job, and/or set fail_on_unmatched_files: true.

3. The Windows packaging script silently ignores failed native commands

scripts/windows/make-portable.ps1:12 sets:

$PSNativeCommandErrorActionPreference = "Stop"

That variable only exists in PowerShell 7+. windows-release.yml:54,60 invokes the script with powershell -NoProfile -ExecutionPolicy Bypass -File ... -- Windows PowerShell 5.1, where it does nothing, and where $ErrorActionPreference does not apply to native commands either.

Consequence: the CPU-variant torch swap (pip install torch==2.6.0+cpu ... --force-reinstall --no-deps) can fail on a transient network error and the script continues. Whatever torch was already resolved stays installed, and the later import checks still pass because torch imports fine. The zip labelled CPU ships a non-CPU torch. The same applies to pip install "$Root" and pip install --upgrade pip.

Fix: invoke with pwsh (PowerShell 7+), or check $LASTEXITCODE after each native command.

4. trivy-action@master is the only unpinned action in the repo

.github/workflows/ci.yml:181 and :193. Every other action is SHA-pinned (e.g. actions/checkout@3d3c42e5..., softprops/action-gh-release@3d0d9888...). These two resolve master at run time on every PR and push to main.

Fix: SHA-pin both, consistent with the rest of the repo.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ciCI/CD pipeline and automationsecuritySecurity vulnerability or hardening

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions