Skip to content

ci: validate workflow_run origin before consuming the E2E artifact (fixes fork artifact poisoning)#27753

Open
adilburaksen wants to merge 1 commit into
google-gemini:mainfrom
adilburaksen:fix/e2e-workflow-run-artifact-poisoning
Open

ci: validate workflow_run origin before consuming the E2E artifact (fixes fork artifact poisoning)#27753
adilburaksen wants to merge 1 commit into
google-gemini:mainfrom
adilburaksen:fix/e2e-workflow-run-artifact-poisoning

Conversation

@adilburaksen

@adilburaksen adilburaksen commented Jun 9, 2026

Copy link
Copy Markdown

Summary

The chained E2E pipeline is vulnerable to workflow_run artifact poisoning, allowing a fork PR to run attacker-controlled code with repository secrets.

  • trigger_e2e.yml runs on pull_request (including forks) and writes the PR head repo.full_name and sha into the repo_name artifact.
  • chained_e2e.yml runs on workflow_run (base-repo context, full secret access). download_repo_name downloads that artifact and the downstream jobs (e2e_linux, e2e_mac, e2e_windows, evals) actions/checkout the artifact's repository/sha and run npm ci (executing postinstall), npm run build, and tests with GEMINI_API_KEY, GEMINI_CLI_ROBOT_GITHUB_PAT, and permissions: write-all in scope.

A fork can therefore put its own repo + SHA into the artifact and have the privileged workflow execute its code with those secrets. The existing github.repository == 'google-gemini/gemini-cli' guard is always true under workflow_run (it runs in the base context) and does not validate the artifact's origin.

This is the workflow_run artifact-poisoning pattern documented by GitHub Security Lab.

Fix

Gate artifact consumption (download_repo_name) on the triggering run's head_repository being this repository:

if: >-
  github.repository == 'google-gemini/gemini-cli' &&
  (github.event_name == 'workflow_dispatch' ||
  (github.event_name == 'workflow_run' &&
  github.event.workflow_run.head_repository.full_name == github.repository))

For a fork-triggered run this skips the artifact download, so parse_run_context falls back to github.repository/base sha and the E2E jobs no longer check out or execute fork code with secrets. Same-repo PRs and workflow_dispatch are unaffected.

Note on fork E2E

This change intentionally stops the secret-bearing E2E jobs from running fork code. If you want to keep E2E coverage for fork PRs, the safe pattern is to gate it behind a human-applied label (or a deployment environment with required reviewers) before checking out fork code with secrets — similar to how other Google repos (e.g. protocolbuffers/protobuf) require a "safe for tests" label on fork PRs. Happy to follow up with that if preferred.

Resolves #27940

The chained E2E workflow runs on `workflow_run` in the base-repo context
with GEMINI_API_KEY, GEMINI_CLI_ROBOT_GITHUB_PAT, and a write-all
GITHUB_TOKEN in scope. `download_repo_name` downloads the repo_name/head_sha
artifact produced by the `Trigger E2E` run (which runs on `pull_request`,
including forks) and the downstream jobs check out and build that
repository/sha. A fork PR can write its own repo and SHA into the artifact,
causing the privileged workflow to execute attacker-controlled code
(`npm ci`/`postinstall`, build, tests) with those secrets available.

Gate artifact consumption on the triggering run's `head_repository` being
this repository, so a fork-triggered run falls back to the base repo/sha
and does not run fork code with secrets. The existing `github.repository`
check is always true under `workflow_run` and does not validate the
artifact's origin.
@adilburaksen adilburaksen requested a review from a team as a code owner June 9, 2026 13:00
@gemini-code-assist

Copy link
Copy Markdown
Contributor

Note

Gemini is unable to generate a summary for this pull request due to the file types involved not being currently supported.

@github-actions github-actions Bot added the size/s A small PR label Jun 9, 2026
@github-actions

github-actions Bot commented Jun 9, 2026

Copy link
Copy Markdown

📊 PR Size: size/S

  • Lines changed: 16
  • Additions: +15
  • Deletions: -1
  • Files changed: 1

@sin325 sin325 left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

``

@gemini-cli gemini-cli Bot added the status/need-issue Pull requests that need to have an associated issue. label Jun 9, 2026
@adilburaksen

Copy link
Copy Markdown
Author

Thanks for taking a look @sin325. The status/need-issue label is the blocker here — this PR is a security hardening that came out of an active Google OSS-VRP report (workflow_run artifact-origin validation), so I'd rather not spell out the exploit in a public issue before it's merged.

A couple of ways to unblock, whichever your team prefers:

  • I open a minimal tracking issue ("harden chained_e2e workflow_run artifact origin check") with no exploit detail and link it, or
  • I share the specifics with you privately.

On the change itself: the diff gates download_repo_name on github.event.workflow_run.head_repository.full_name == github.repository, so a fork-triggered run falls back to the base repo/sha and the secret-bearing E2E jobs no longer check out or execute fork code. If you'd rather keep fork E2E working, I can switch to the label-gate approach instead. Happy to make whatever change you'd like — just let me know.

@adilburaksen

Copy link
Copy Markdown
Author

@sin325 opened #27940 to satisfy status/need-issue and linked it here. I kept the exploit detail out of the public issue, but I'm happy to walk you or the team through the full thing privately if that's preferable. The change itself is just the workflow_run origin check described above — whatever's easiest to unblock the merge works for me.

@gemini-cli gemini-cli Bot added area/security Issues related to security priority/p1 Important and should be addressed in the near term. and removed status/need-issue Pull requests that need to have an associated issue. labels Jun 15, 2026
@gemini-cli

gemini-cli Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Hi there! Thank you for your interest in contributing to Gemini CLI.

To ensure we maintain high code quality and focus on our prioritized roadmap, we only guarantee review and consideration of pull requests for issues that are explicitly labeled as 'help wanted'.

This PR will be closed in 7 days if it remains without that designation. We encourage you to find and contribute to existing 'help wanted' issues in our backlog! Thank you for your understanding.

@adilburaksen

Copy link
Copy Markdown
Author

This one isn't a roadmap/feature contribution — it's a security fix the team has already triaged and labeled priority/p1 + area/security, both here and on the linked issue #27940 (currently status/manual-triage). It also came out of an active Google OSS-VRP report on chained_e2e workflow_run artifact-origin validation, where the assessment is tied to this fix landing.

Given that, could it be exempted from the auto-close and routed to the security/CI maintainer for review instead of the help wanted path? The change is minimal (a single workflow_run origin guard, +15/-1) and #27940 already satisfies status/need-issue. Happy to walk through the full details privately if that's easier for the merge.

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

Labels

area/security Issues related to security priority/p1 Important and should be addressed in the near term. size/s A small PR status/pr-nudge-sent

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Validate workflow_run origin in chained_e2e before using the repo_name artifact

2 participants