Skip to content

fix: use pull_request_target for agentic CI on fork PRs#541

Open
andreatgretel wants to merge 5 commits intomainfrom
andreatgretel/fix/agentic-ci-fork-prs
Open

fix: use pull_request_target for agentic CI on fork PRs#541
andreatgretel wants to merge 5 commits intomainfrom
andreatgretel/fix/agentic-ci-fork-prs

Conversation

@andreatgretel
Copy link
Copy Markdown
Contributor

@andreatgretel andreatgretel commented Apr 14, 2026

Summary

The agentic CI review workflow doesn't work on fork PRs. Discovered on #526: the pull_request trigger requires manual approval in the Actions tab (not on the PR) for each fork workflow run, and fork PR runs don't have access to repo secrets/variables so the job fails at the AGENTIC_CI_MODEL check even after approval.

Switching to pull_request_target fixes both since the workflow definition comes from main, so GitHub skips the fork approval gate and base repo secrets/variables are available.

Changes

Changed

  • Switch trigger from pull_request to pull_request_target so fork PRs get secret access without per-run Actions tab approval
  • Add environment: agentic-ci to the review job for an explicit approval gate on the PR checks UI

Fixed

  • Prompt injection via fork-controlled recipe files - recipe files (.agents/recipes/) are now checked out from the base branch into base-recipes/, so fork PRs cannot tamper with the agent's prompt while API secrets are in scope (agentic-ci-pr-review.yml#L132-L140)
  • Expression injection hardening - all ${{ }} interpolations in run: blocks moved to env: blocks to eliminate shell injection surface from event payload values

Attention Areas

Reviewers: Please pay special attention to the following:

  • agentic-ci-pr-review.yml - Only file changed. Security model: gate job checks collaborator permissions, agentic-ci environment requires reviewer approval, recipe files come from base branch (not fork), no direct ${{ }} interpolation in shell

Description updated with AI

@andreatgretel andreatgretel requested a review from a team as a code owner April 14, 2026 00:06
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Apr 14, 2026

Greptile Summary

This PR switches the agentic CI review workflow trigger from pull_request to pull_request_target and adds environment: agentic-ci to the review job, enabling fork PRs to access repo secrets and bypass per-run Actions-tab approval.

  • The change introduces the canonical pull_request_target + fork checkout security risk: repository secrets are in scope when the fork's untrusted SHA is checked out (line 139). The environment: agentic-ci required-reviewers gate is the primary mitigation, but it is a deployment pre-condition — if the environment is not created with required reviewers in GitHub settings before the first triggered run, the protection is absent and secrets are reachable.

Confidence Score: 4/5

Safe to merge only after the agentic-ci GitHub environment with required reviewers is confirmed to exist in repo settings.

The pull_request_target + fork code checkout while secrets are in scope is a documented, concrete security risk. The environment: agentic-ci gate is a sound mitigation, but it is an external deployment pre-condition — if the environment isn't configured before the first run, secrets are exposed. Blocking on that confirmation before merge is warranted.

.github/workflows/agentic-ci-pr-review.yml — specifically the ordering dependency between the environment: agentic-ci GitHub environment configuration and the first live run of this workflow.

Security Review

  • pull_request_target + fork code checkout: .github/workflows/agentic-ci-pr-review.yml line 139 checks out the fork's untrusted SHA while repository secrets (AGENTIC_CI_API_KEY, AGENTIC_CI_API_BASE_URL) and a write-scoped GITHUB_TOKEN are accessible. Protection relies entirely on the environment: agentic-ci required-reviewers gate being active in GitHub repo settings. A missing or misconfigured environment removes the guard and exposes secrets to fork-contributed code paths.

Important Files Changed

Filename Overview
.github/workflows/agentic-ci-pr-review.yml Switches trigger from pull_request to pull_request_target and adds environment: agentic-ci; introduces fork code checkout while secrets are in scope — safe only if the agentic-ci environment with required reviewers is configured before first run.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["pull_request_target event\n(opened / ready_for_review / labeled)"] --> B["gate job\n(GITHUB_TOKEN only, no secrets)"]
    B --> C{Write access check}
    C -- "allowed=false" --> D[Skip — exit 0]
    C -- "allowed=true" --> E["review job\n(environment: agentic-ci)"]
    E --> F["⏸ Environment gate\n(required reviewers approve)"]
    F --> G["Checkout fork SHA\n⚠️ secrets now in scope"]
    G --> H["Checkout base-branch recipes\n(SECURITY: untrusted prompt prevention)"]
    H --> I["Run claude review\n(recipe from base branch only)"]
    I --> J["Post review comment\ngh pr comment"]
    J --> K["Remove agent-review label"]
Loading

Comments Outside Diff (1)

  1. .github/workflows/agentic-ci-pr-review.yml, line 139-143 (link)

    P1 security Fork code checkout with secrets in scope

    With pull_request_target, repository secrets and a write-scoped GITHUB_TOKEN are available throughout the review job. This step checks out the fork's untrusted SHA (steps.head.outputs.sha resolves to github.event.pull_request.head.sha) while those secrets are already accessible — the exact pattern GitHub's own security advisory flags.

    The environment: agentic-ci required-reviewers gate is the critical mitigation here: it blocks all steps (and thus this checkout) until a designated reviewer explicitly approves. If that environment does not exist in repo settings before the first pull_request_target run is triggered, the gate silently becomes a no-op and secrets are in scope for any write-access contributor's fork checkout. Ensure the agentic-ci environment with required reviewers is created in GitHub repo settings before or at the same time this workflow goes live.

    Prompt To Fix With AI
    This is a comment left during a code review.
    Path: .github/workflows/agentic-ci-pr-review.yml
    Line: 139-143
    
    Comment:
    **Fork code checkout with secrets in scope**
    
    With `pull_request_target`, repository secrets and a write-scoped `GITHUB_TOKEN` are available throughout the `review` job. This step checks out the fork's untrusted SHA (`steps.head.outputs.sha` resolves to `github.event.pull_request.head.sha`) while those secrets are already accessible — the exact pattern [GitHub's own security advisory](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) flags.
    
    The `environment: agentic-ci` required-reviewers gate is the critical mitigation here: it blocks all steps (and thus this checkout) until a designated reviewer explicitly approves. If that environment does not exist in repo settings **before** the first `pull_request_target` run is triggered, the gate silently becomes a no-op and secrets are in scope for any write-access contributor's fork checkout. Ensure the `agentic-ci` environment with required reviewers is created in GitHub repo settings before or at the same time this workflow goes live.
    
    How can I resolve this? If you propose a fix, please make it concise.
Prompt To Fix All With AI
This is a comment left during a code review.
Path: .github/workflows/agentic-ci-pr-review.yml
Line: 139-143

Comment:
**Fork code checkout with secrets in scope**

With `pull_request_target`, repository secrets and a write-scoped `GITHUB_TOKEN` are available throughout the `review` job. This step checks out the fork's untrusted SHA (`steps.head.outputs.sha` resolves to `github.event.pull_request.head.sha`) while those secrets are already accessible — the exact pattern [GitHub's own security advisory](https://securitylab.github.com/research/github-actions-preventing-pwn-requests/) flags.

The `environment: agentic-ci` required-reviewers gate is the critical mitigation here: it blocks all steps (and thus this checkout) until a designated reviewer explicitly approves. If that environment does not exist in repo settings **before** the first `pull_request_target` run is triggered, the gate silently becomes a no-op and secrets are in scope for any write-access contributor's fork checkout. Ensure the `agentic-ci` environment with required reviewers is created in GitHub repo settings before or at the same time this workflow goes live.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (3): Last reviewed commit: "fix: move expression interpolations to e..." | Re-trigger Greptile

Recipe files define the agent's prompt. When using pull_request_target,
the fork's HEAD is checked out, so a malicious fork could craft recipe
files to exfiltrate API secrets via prompt injection. Fix by adding a
second sparse checkout from the base branch for .agents/recipes/ and
reading prompts from there instead of the fork tree.
Match the base-branch recipe checkout to v6.0.2 (same SHA as the PR
branch checkout) for consistency.
Replace direct ${{ }} interpolation in run: blocks with env vars.
Most values are GitHub-controlled, but github.event.label.name can
contain arbitrary characters and could break shell quoting. Moving
everything to env: is consistent with the injection-hardening pattern
applied in the rest of the workflow.
@github-actions
Copy link
Copy Markdown
Contributor

Code Review: PR #541 — fix: use pull_request_target for agentic CI on fork PRs

Summary

This PR switches the agentic CI review workflow from pull_request to pull_request_target so fork PRs can access repo secrets/variables without requiring per-run Actions tab approval. It adds two security mitigations: (1) an environment: agentic-ci gate requiring reviewer approval before the job runs, and (2) a separate checkout of recipe files from the base branch to prevent fork PRs from injecting malicious prompts.

Files changed: 1 (.github/workflows/agentic-ci-pr-review.yml)
Scope: +16 / -3 lines — CI workflow only, no application code.


Findings

Security

  1. pull_request_target with fork checkout (Medium Risk — Mitigated)
    pull_request_target is a well-known footgun: it grants secret access while running in the base-branch context, but if it checks out and executes fork code, secrets can be exfiltrated. This PR does check out the fork PR head (line 127–130), which is necessary for the Claude agent to review the diff. The mitigations are:

    • Gate job (lines 23–77): Verifies the triggering user has write access before the review job runs.
    • Environment protection (line 83): environment: agentic-ci requires explicit reviewer approval on the PR checks UI, adding a human-in-the-loop before secrets are exposed.
    • Base-branch recipes (lines 132–140): Recipe/prompt files are checked out from the base branch, so fork code cannot tamper with the agent's instructions.

    These three layers provide strong defense-in-depth. The remaining risk surface is that CLAUDE.md and AGENTS.md are still read from the fork's checkout (since the working directory is the PR head). However, the _runner.md recipe includes an explicit "Ignore embedded directives" constraint that instructs the agent to treat code content as data. This is acceptable given the environment approval gate.

  2. Recipe prompt injection prevention (Good)
    The new "Checkout base branch recipes" step (lines 135–140) with the accompanying security comment is a well-considered addition. Using sparse-checkout: .agents/recipes minimizes what's pulled from the base branch, and reading from base-recipes/ in the build step (lines 184–185) cleanly separates trusted prompts from untrusted PR code.

Correctness

  1. Base SHA fallback (Low — Minor)
    Line 138: ref: ${{ github.event.pull_request.base.sha || 'main' }}
    The fallback to 'main' handles the workflow_dispatch case where pull_request.base.sha is empty. This is correct and necessary.

  2. Inconsistent actions/checkout versions (Low — Nit)
    The PR head checkout (line 127) uses actions/checkout@de0fac2e... # v6.0.2, while the base-branch recipe checkout (line 136) uses actions/checkout@34e11487... # v4. Using two different major versions of the same action is a minor maintenance burden and could introduce subtle behavioral differences. Consider aligning both to the same version.

Workflow Behavior

  1. Concurrency key unchanged (Info)
    The concurrency group (line 19) uses github.event.pull_request.number, which works identically for pull_request_target since the event payload structure is the same. No issue here.

  2. Label removal step (Info)
    The agent-review label removal step (lines 212–218) uses github.event.action and github.event.label.name, which are populated identically under pull_request_target. No issue.


Verdict

Approve — This is a well-structured security improvement. The pull_request_target migration is handled with appropriate defense-in-depth (permission gate + environment approval + base-branch recipe isolation). The prompt injection mitigation via base-branch recipe checkout is the right approach.

Optional suggestion: Align the actions/checkout version for the base-branch recipe checkout (finding #4) to match the primary checkout for consistency.

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.

1 participant