GitHub

Unmasking the Provenance Gap: Securing Your Software Supply Chain and Boosting Git Quality with Reliable GitHub Attestations

In the intricate world of modern software development, trust is the bedrock of every successful release. Ensuring the integrity and provenance of your build artifacts is not just a best practice; it's a critical component of supply chain security. GitHub's Artifacts Attestation feature aims to provide this crucial transparency, offering a cryptographic link between your source code and your deployed binaries. However, a recent discussion on the GitHub Community forum has unveiled a significant blind spot, one that could fundamentally undermine the very trust these attestations are designed to instill and directly impact your git quality.

This vulnerability, brought to light by 'amiller', exposes a gap in how GitHub-hosted runners are specified and attested, creating a scenario where a seemingly legitimate attestation could certify an artifact that was not built from its claimed source. For dev teams, product managers, delivery managers, and CTOs, this isn't merely a technical nuance; it's a potential threat to your software supply chain, your delivery confidence, and the overall reliability of your software development productivity tools.

The Provenance Gap: Malicious Runners and Misleading Attestations

The heart of the problem lies in the flexibility afforded to organizations on paid plans using GitHub-hosted larger runners. These organizations can select the specific image their runners boot. Critically, GitHub does not validate runner names against its own standard labels. This opens a door for malicious actors – or even unintentional misconfigurations – to create a custom runner, name it identically to a standard GitHub-provided runner (e.g., ubuntu-24.04), and then serve jobs pinned to that seemingly trusted label.

The alarming consequence? The resulting build attestation, meticulously designed to provide provenance, contains no record that would expose this substitution. The runnerEnvironment field, intended to differentiate between GitHub-controlled and self-hosted environments, misleadingly reports github-hosted even when an organization-controlled runner with a custom, potentially compromised, image is in play. This creates a dangerous blind spot for anyone attempting to verify the true origin of a build artifact.

Comparison of a standard GitHub runner versus a custom organizational runner, both appearing 'github-hosted' but producing different, misleading build outputs.
Comparison of a standard GitHub runner versus a custom organizational runner, both appearing 'github-hosted' but producing different, misleading build outputs.

A Concrete Example: When '4' Becomes '5'

To underscore the severity of this vulnerability, 'amiller' provided a compelling, real-world example:

  • A simple C program (hello.c) is written to print the number '4'.
  • A standard GitHub Actions workflow (build.yml) compiles this program and then uses actions/attest-build-provenance@v2 to attest the resulting binary, explicitly pinning the job to run on runs-on: ubuntu-24.04.

When this workflow executes on a genuine GitHub-provided shared runner, the resulting binary behaves exactly as expected, printing '4'. The attestation correctly links the binary to the source. However, the exact same workflow, from the same commit, was then run on an organization-created runner. This custom runner was configured with an image that subtly rewrote the source code before compilation, changing the '4' to a '5', then compiled it, and finally restored the original source file to leave no visible trace in the workspace.

The critical finding: When a consumer downloaded this 'compromised' artifact and verified it using gh attestation verify, the command reported success. Even with the --deny-self-hosted-runners flag, the verification passed, despite the binary printing '5' instead of '4'. This demonstrates a profound failure in the attestation chain to provide reliable provenance, directly impacting git quality and the trust in your CI/CD pipeline.

Detailed view of GitHub Actions job logs, highlighting the critical difference in 'runner_group_name' between a secure GitHub-shared runner and a potentially compromised custom organizational runner.
Detailed view of GitHub Actions job logs, highlighting the critical difference in 'runner_group_name' between a secure GitHub-shared runner and a potentially compromised custom organizational runner.

The Missing Link: What the Attestation Doesn't Tell You

The core problem is the lack of specific, immutable runner identity within the signed attestation material. Here’s what the investigation revealed:

  • The runner_environment field is too broad, reporting github-hosted for both genuine GitHub-shared runners and organization-created runners that merely run on GitHub's infrastructure. This is a crucial distinction that is lost.
  • Fields like builder.id point to the workflow file, not the specific machine or runner configuration. No field explicitly names the runner, its group, its size, or, most importantly, the image it booted from.
  • Even attempting to pin a workflow to GitHub's own shared runner group (GitHub Actions) fails, as this group is reserved and not directly addressable by name in runs-on.

This means that from the perspective of the attestation alone, the 'good' build and the 'malicious' build are indistinguishable, presenting a significant challenge for maintaining robust git quality and secure delivery.

Uncovering the Truth: The API Holds the Clues (For Now)

While the attestation itself falls short, the GitHub API does contain the necessary information to differentiate these runs. The attestation's Fulcio extension and runDetails.metadata.invocationId provide a direct link to the specific GitHub Actions run. From there, the Jobs API exposes critical fields:

  • runner_group_name: For genuine GitHub-shared runners, this is GitHub Actions. For organization-created runners, it's a group owned by the organization (e.g., default).
  • runner_group_id: GitHub's reserved group has an ID of 0. Organization groups have different IDs.
  • runner_name: Distinct names for GitHub's runners versus organization-created ones.

The job logs further confirm this, explicitly stating 'Runner Image: ubuntu-24.04' for GitHub's shared pool versus 'Source: Custom Name: shimcompile' for the substituted run.

This means a verifier can manually follow the invocationId to the API and check runner_group_id == 0 to confirm a truly GitHub-controlled environment. However, as 'ralftpaw' notes in the discussion, this is 'post-hoc API evidence' and not part of the cryptographically signed attestation. It introduces a dependency on mutable API state and adds complexity to the verification process, undermining the promise of self-contained, verifiable provenance.

The Path Forward: Securing Provenance and Boosting Trust

To close this critical provenance gap and restore full confidence in GitHub Artifacts Attestation as a leading software development productivity tool, the solution is clear: the runner's true identity must be embedded directly into the signed material.

Immediate Action (Verifier Side)

Until a platform-level fix is implemented, verifiers should treat runnerEnvironment: github-hosted as an insufficient claim. They must augment their verification process by using the invocationId to query the GitHub Jobs API and explicitly require runner_group_id == 0 (or runner_group_name == GitHub Actions). This should be a 'fail closed' requirement if the lookup is unavailable or inconsistent.

Longer-Term Solution (Platform Side)

The most robust fix involves integrating the runner_group_id and, ideally, an immutable runner-image digest/version directly into the Actions OIDC token, and subsequently into the certificate and the SLSA predicate. GitHub already computes and exposes this information via its API; including it in the attestation would provide an unforgeable, cryptographically verifiable link to the actual build environment.

Furthermore, ensuring that organization runner names cannot mimic GitHub's reserved runner names (e.g., by disallowing spaces or specific prefixes) would add another layer of defense.

Conclusion

This discussion highlights a crucial area for improvement in GitHub's build provenance capabilities. For CTOs, delivery managers, and dev teams, the integrity of your build artifacts is non-negotiable. Addressing this provenance gap is essential not only for enhancing supply chain security and compliance but also for upholding the fundamental git quality and trustworthiness of the software you deliver. By integrating robust runner identity into attestations, GitHub can further solidify its position as a trusted platform for secure and efficient software development.

Share:

|

Dashboards, alerts, and review-ready summaries built on your GitHub activity.

 Install GitHub App to Start
Dashboard with engineering activity trends