GitHub Actions workflow_run: Keep Untrusted Test Artifacts Out of a Release Job

A volunteer-built library kiosk runs pull-request tests in GitHub Actions. A separate workflow packages updates for the public kiosk. The workflow_run event can trigger a second workflow after the first completes, but the second run may have secrets and write permissions that the first did not. This creates a trust boundary around artifacts, caches and the triggering commit. The workflow below is a fictional security review, not a claim that a specific repository was audited.

Map the two trust levels before linking workflows

Treat pull-request code, logs and produced files as untrusted even when tests pass. A test result shows that checks ran under a given configuration; it does not prove the build artifact is safe to execute with production credentials. Draw the first workflow as a low-trust producer and the second as a potentially privileged consumer. List all data crossing between them.

A reviewer should see the triggering repository, branch, commit SHA, workflow name and conclusion alongside each artifact. If the subsequent release workflow has permission to deploy, it must not run scripts or configuration supplied by an untrusted pull request. Preserve the distinction between “workflow completed” and “approved for release” in titles and status messages.

Understand what workflow_run triggers actually grant

GitHub documents workflow_run activity types including completed, requested and in_progress. A downstream workflow can have access to secrets and write tokens even when the upstream workflow lacks them. That capability is useful for controlled stages but makes careless chaining dangerous. GitHub explicitly warns about running untrusted code, cache poisoning and unintended access to privileged capabilities.

Original schematic of GitHub Actions workflow_run security. Steps: Run untrusted tests → Identify run and SHA → Verify artifact origin → Approve protected release. Conceptual illustration; not a vendor test.

A downstream run should check the expected upstream workflow identity and event, the correct commit SHA, acceptable conclusion and authorized source context. Do not rely on a user-controlled artifact filename or a line in a log that says “tests passed.” The event payload and repository policy should be cross-checked before privileged processing continues.

Treat artifacts as evidence, never executable instructions

An untrusted workflow may upload useful coverage reports or test summaries, but those files can contain attacker-controlled content. A release job should not source shell scripts from the artifact, execute its binaries or trust embedded deployment destinations without review. If an artifact is only a report, parse it using a strict schema and an isolated process that cannot write to production.

When a build artifact must be promoted, the preferable architecture is often to rebuild from a verified, merged, trusted commit within a protected workflow. Preserve reproducible build inputs and dependency locks. Match the reviewed commit to the actual repository state used by the privileged build. Artifact hashes establish file identity, not that an untrusted source is safe.

Keep caches from crossing the privilege boundary

GitHub notes that caches can be restored across eligible runs and may contain attacker-influenced files. A cache is a performance optimization, not a trusted release source. Never put secrets in a cache or restore untrusted executable content in a job with elevated permissions. Review current trigger-specific cache-access restrictions rather than assuming all workflow runs are isolated.

Prefer a clean dependency install in a privileged release path, or caches that are populated only from trusted branches under a controlled scope. If the source of a cached dependency cannot be proven, discard it. An otherwise secure artifact review can be undermined when an unchecked cache supplies a malicious build tool before validation begins.

Illustrate the safe transition from report to release

The comparison diagram has three constructed states: a privileged job that blindly imports a PR artifact, a job that checks run identity and source metadata, and a separate protected release job. The first is intentionally a warning example, not a recommended procedure. These diagrams do not claim to show GitHub user-interface screenshots or a live security penetration test.

Original schematic of GitHub Actions workflow_run security. Comparison: Blind artifact import, Verified run metadata, Protected release gate. Conceptual illustration; not a vendor test.

Use a synthetic pull request to validate that privileged jobs refuse artifacts from an unexpected branch, a failed run and an unapproved commit. A malicious-looking artifact should be handled as data only, never executed. Also test the positive path from a trusted branch so the procedure is usable and reviewers know what successful behavior looks like.

Place protection exactly where production writes occur

Environment protection and required reviewers can help guard sensitive deployment jobs. Apply them to the job that receives credentials and performs the write, not only to the earlier test workflow. Keep permissions minimal: a report parser may need read access, while an actual release should receive temporary scoped rights at the last possible stage.

Require a human to review the intended target environment, commit and artifact checksum before approving a public deployment. Approval of a generic branch run should not imply approval of every subsequent artifact. When using cloud credentials, prefer short-lived, audience-limited trust where available, and retain the deployment audit record.

Verify the release boundary with negative tests

The review illustration highlights four tests: never execute untrusted PR payloads, verify run identity and SHA, keep secrets out of tests and make the actual release privileged only after review. Add an adversarial case where the first workflow publishes an artifact with a familiar name but the wrong digest. The privileged job must reject it or ignore it.

Original schematic of GitHub Actions workflow_run security. Review points: Never execute PR payload, Verify source and SHA, Keep secrets out of tests, Review privileged release. Conceptual illustration; not a vendor test.

A useful reviewer worksheet states each rejection condition, the independent signal checked and where rejection is logged. Do not report “safe” only because a vulnerability scanner found no code issues. A workflow’s permission structure and data flow are part of its security model, beyond the source language that CodeQL or dependency scanning can examine.

Make promotion reproducible and revocable

Document which branch and protected workflow produce the final release, which people can authorize it and how a rollback works. Retain the minimum provenance necessary to reconstruct a published artifact. If the boundary fails, rotate exposed credentials and review all downstream releases; deleting a bad cache alone may not resolve exposure.

In a small library project, a manually approved trusted-branch release may be simpler than an intricate cross-workflow promotion. workflow_run is a useful event, not a blanket endorsement of artifacts it connects. Keep low-trust testing fast and separate from any job that can publish code or alter the production kiosk.

How We Research

For this November 2026 editorial review, we consulted official vendor references: GitHub workflow_run event; GitHub Actions dependency caching; GitHub workflow security hardening. We mapped feature scope, input/output rules, failure conditions and developer guidance to a hypothetical community-service scenario. We did not execute paid API requests, upload private personal data, publish a real catalog record, run a security attack or measure service performance. The diagrams show original educational examples, not screenshots or recorded service results. Documentation, permissions, regional availability and service configuration can change; verify them in the current official reference and an authorized test before deployment.

Frequently Asked Questions

Does a successful test workflow make its artifacts safe?

No. A passing status does not authenticate or sanitize the artifacts or their executable content.

Can workflow_run access stronger credentials?

Yes. GitHub documents that a downstream workflow may access secrets and write tokens even when the triggering workflow could not.

Is comparing artifact SHA-256 enough?

No. A hash identifies file bytes, not whether an untrusted producer should be allowed to supply those bytes.

Should privileged jobs use pull-request caches?

Treat those caches as untrusted and follow current cache restrictions; a clean trusted build is safer.

Where should approval be enforced?

On the actual production write or deployment job, with the exact source commit and target reviewed.

Scroll to Top