Skip to content

[Security] publish.yml — CycloneDX CLI, Inno Setup, and 7-Zip installers downloaded and executed without integrity verification #793

@Christophe-Rogiers

Description

@Christophe-Rogiers

Severity: Warning
File: .github/workflows/publish.yml lines 85-134

Description:

The publish pipeline downloads three third-party binaries from their GitHub release pages and either adds them to GITHUB_PATH or silently installs them, in each case without a SHA256 / signature check. This is distinct from #787 (which is about the Microsoft-hosted dotnet-install.ps1 in setup-dotnet/action.yml) — those three binaries come from different supply chains and each compromise vector is independent.

1. CycloneDX CLI (lines 85-97):

$v = $env:CYCLONEDX_CLI_VERSION
...
Invoke-WebRequest -Uri "https://github.com/CycloneDX/cyclonedx-cli/releases/download/$v/cyclonedx-win-x64.exe" `
                  -OutFile "$env:USERPROFILE\cyclonedx.exe" `
                  -ErrorAction Stop

Add-Content -Path $env:GITHUB_PATH -Value $env:USERPROFILE

The .exe is downloaded and then put on PATH. A subsequent SBOM-generation step invokes it directly, so compromise of github.com/CycloneDX/cyclonedx-cli release assets (release re-upload, tag-force-push, repo hijack) ends with arbitrary code running on the runner with access to all repo secrets (SIGNPATH_API_TOKEN, NUGET_API_KEY, etc.).

2. Inno Setup (lines 99-105):

$url = "https://github.com/jrsoftware/issrc/releases/download/is-6_6_1/innosetup-6.6.1.exe"
Invoke-WebRequest -Uri $url -OutFile "innosetup.exe" -ErrorAction Stop
Start-Process ".\innosetup.exe" -ArgumentList "/VERYSILENT", "/SUPPRESSMSGBOXES" -Wait
if (-not (Test-Path $env:ISCC)) { throw "ISCC.exe not found at $env:ISCC" }

A third-party installer is executed with /VERYSILENT. The post-check only verifies ISCC.exe exists at the expected path — a tampered installer could have already done anything else the build user is allowed to do (including hooking into later steps) before dropping the expected file.

3. 7-Zip (lines 107-133):

$version = "2600" # For version 26.00
$url = "https://github.com/ip7z/7zip/releases/download/26.00/7z$($version)-x64.exe"
$installer = "7z_setup.exe"
$installDir = "C:\7-Zip"
...
Invoke-WebRequest -Uri $url -OutFile $installer -ErrorAction Stop
...
Start-Process ".\$installer" -ArgumentList "/S", "/D=$installDir" -Wait

Same pattern: downloaded via Invoke-WebRequest, silently installed, used by the pipeline to package artifacts.

Threat model:

Each of the three upstream repos (CycloneDX/cyclonedx-cli, jrsoftware/issrc, ip7z/7zip) is an independent supply-chain dependency of every Servy release. A single release-asset compromise on any of them is enough to pivot into a signed Servy installer, because the publish job that calls SignPath runs after these steps on the same runner.

#607 addresses third-party Actions (uses: references) — that's a different attack surface. #787 addresses Microsoft-hosted dotnet-install.ps1 — also a different origin. These three binaries are not covered by either issue.

Suggested fix:

For each of the three downloads, pin an expected SHA256 and verify before execution:

$url = "https://github.com/CycloneDX/cyclonedx-cli/releases/download/$v/cyclonedx-win-x64.exe"
$expected = "<sha256 for this version, bumped in the same PR that bumps CYCLONEDX_CLI_VERSION>"
Invoke-WebRequest -Uri $url -OutFile cyclonedx.exe -ErrorAction Stop
$actual = (Get-FileHash cyclonedx.exe -Algorithm SHA256).Hash
if ($actual -ne $expected) {
    throw "cyclonedx.exe integrity check failed. Expected $expected, got $actual."
}

Same idea for Inno Setup and 7-Zip. Bump the expected hash in the same PR that bumps the version. Alternatively, vendor the installers into a private release of this repo and pull from there, matching the hardening stance of #607.

Low-effort interim option for Inno Setup / 7-Zip: verify Authenticode signature before executing (Get-AuthenticodeSignature), which at least guarantees the binary was signed by the respective project's cert and not swapped mid-flight.

Metadata

Metadata

Assignees

Labels

ciCI/CD

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions