Skip to content

Add verifiable CI builds with signed provenance attestations - #40

Open
BillSkiCO wants to merge 1 commit into
stianhoiland:mainfrom
BillSkiCO:verifiable-builds-upstream
Open

BillSkiCO wants to merge 1 commit into
stianhoiland:mainfrom
BillSkiCO:verifiable-builds-upstream

Conversation

@BillSkiCO

@BillSkiCO BillSkiCO commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Release binaries are currently built and uploaded from a local machine, so users have no way to prove a download corresponds to the published source. This makes cmdtab hard to adopt in security-conscious and corporate environments, where third-party binaries must be traceable to auditable source through a transparent build pipeline.

This adds a GitHub Actions workflow that:

  • Builds cmdtab.exe from source with MSVC on GitHub-hosted runners on every push, PR, and version tag
  • Fails a tagged build if the tag disagrees with the FileVersion in res/cmdtab.rc
  • Packages the release zip and a SHA256SUMS file
  • Signs SLSA build provenance attestations for the exe and zip, verifiable with gh attestation verify
  • Drafts a GitHub Release with the artifacts on v* tags, leaving publishing in the maintainer's hands

The workflow follows supply-chain best practices: all third-party actions pinned to full commit SHAs, least-privilege permissions per job, and no third-party release action in the trust chain.

Also passes /MANIFEST:NO to the MSVC linker: cmdtab.rc already embeds cmdtab.manifest as RT_MANIFEST id 1, and current MSVC toolchains fail the link with CVT1100 (duplicate resource) when the CMake-default linker manifest is also embedded.

README gains a Verifying releases section documenting verification.

@stianhoiland

Copy link
Copy Markdown
Owner

This is very interesting and educational for me. When I have responsibly reviewed this and understood it, I will go ahead and merge this. I will need to have an intimate understanding before I can stamp my approval on this, because this is effectively a certificate of trust.

As an aside, I use GCC and mingw-w64 along with POSIX sh and make for my own builds. This workflow uses MSVC, PowerShell, and CMake.

I'm a little torn on which to use for this workflow. I will continue developing using my stack, so the binaries I will be testing locally will not be MSVC-produced binaries. This could cause subtle issues. I also like that you have set up a Microsoft-native workflow, since cmdtab is, after all, a completely Windows application.

Any thoughts on this?

@BillSkiCO
BillSkiCO force-pushed the verifiable-builds-upstream branch 2 times, most recently from fbf08dd to 393dfbd Compare July 13, 2026 20:33
Release binaries are currently built and uploaded from a local machine,
so users have no way to prove a download corresponds to the published
source. This makes cmdtab hard to adopt in security-conscious and
corporate environments, where third-party binaries must be traceable to
auditable source through a transparent build pipeline.

This adds a GitHub Actions workflow that:

- Builds cmdtab.exe from source with mingw-w64 GCC on GitHub-hosted
  runners on every push, PR, and version tag, mirroring the flags of
  the Makefile's release target so the published binary matches local
  development builds
- Fails a tagged build if the tag disagrees with the FileVersion in
  res/cmdtab.rc
- Packages the release zip and a SHA256SUMS file
- Signs SLSA build provenance attestations for the exe and zip,
  verifiable with 'gh attestation verify'
- Drafts a GitHub Release with the artifacts on v* tags, leaving
  publishing in the maintainer's hands

The toolchain is the MSYS2 preinstalled on GitHub's Windows runners;
no third-party toolchain actions are added, all actions are pinned to
full commit SHAs, and jobs run with least-privilege permissions.
README gains a 'Verifying releases' section documenting verification,
and a Dependabot config keeps the SHA-pinned actions current by
opening a PR whenever a pinned action publishes a new release.
@BillSkiCO
BillSkiCO force-pushed the verifiable-builds-upstream branch from 393dfbd to 3ec18b7 Compare July 13, 2026 21:07
@BillSkiCO

Copy link
Copy Markdown
Contributor Author

Yeah, I went back and forth on this too. I've switched the PR to build with your stack 😊

The workflow now uses the MSYS2 that's already on GitHub's Windows runners and calls windres + gcc with the same flags as your Makefile's release target.

One thing to flag: I used the UCRT64 environment (the newer CRT, Windows 10+). If your local gcc targets msvcrt and you want CI to match it exactly, that's a two line change, just let me know and I can update it.

While setting this up I also hit an unrelated MSVC link bug and pulled the fix into its own small PR (#41), so this one stays purely about CI. The diff here is now the workflow file, a README section, and a six-line Dependabot config (more on that below).

Since you mentioned this is new territory, here's a walkthrough of the four parts that actually carry the trust. Each one links to the exact lines in this PR and the doc I'd read for it.

  1. Actions pinned to commit SHAs. The workflow pulls in four pieces of code that live outside this repo, all from GitHub's own actions org. Most workflows reference these by tag, like @v7. The catch is that a tag is a movable pointer: whoever controls the action's repo can point it at different code, and your workflow silently runs the new code next push. A full commit SHA can't be moved, so what you review once is what runs until a diff visibly changes the pin. The # v7.0.0 comments say which version each SHA is. Each line below links to the workflow line and then to the exact code that line pulls in:

  2. Least privilege for each job. Every job gets a GITHUB_TOKEN. The workflow makes everything read-only by default, gives the build job just the two scopes that attestation signing needs, and only the tag-triggered release job can create releases. If a build ever runs something malicious, that's the whole blast radius. It can't push code or publish anything.

  3. The attestation step. This is the heart of it. It produces SLSA build provenance, a signed statement recording the exact commit, workflow, and runner that produced the binaries, logged in a public transparency log. That's what gh attestation verify cmdtab.exe --repo stianhoiland/cmdtab checks. If a binary wasn't built by your workflow from your code, the check fails.

  4. Draft releases. On a version tag the workflow creates the release as a draft, so nothing goes public until you press publish. It also uses the gh CLI built into the runner instead of another third-party action, so there's nothing extra to trust. The automation builds and proves; you still decide what ships.

No rush on any of this. Here's a green run of the workflow on my fork so you can poke at a real one, including the attestation step: https://github.com/BillSkiCO/cmdtab/actions/runs/29281871729

stianhoiland added a commit that referenced this pull request Aug 19, 2026
- Add zip, install, start, stop recipes to Makefile
- Grab version number from .rc file. Inspired by #40, thanks @BillSkiCO!
- Build with Os instead of Oz
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants