Skip to content

fix(ci): guard fast-integ-tests to same-repo PRs - #6193

Open
jam-jee wants to merge 1 commit into
aws:masterfrom
jam-jee:fix/fast-integ-tests-fork-checkout
Open

fix(ci): guard fast-integ-tests to same-repo PRs#6193
jam-jee wants to merge 1 commit into
aws:masterfrom
jam-jee:fix/fast-integ-tests-fork-checkout

Conversation

@jam-jee

@jam-jee jam-jee commented Aug 20, 2026

Copy link
Copy Markdown
Collaborator

What this changes

Guards the fast-integ-tests job (added in #6176) to same-repo PRs. It is currently
failing at the checkout step on every fork PR, which is nearly every PR.

Why it fails

fast-integ-tests could never run on its own PR — pull_request_target uses the
base branch's workflow definition — so #6176 merged without it having executed once.
Its first real run (PR #6192)
died at step 2 in 3 seconds, with steps 3–6 skipped:

##[error] Refusing to check out fork pull request code from a pull_request_target
workflow. This workflow runs with the base repository's GITHUB_TOKEN, secrets,
default-branch cache scope, and runner access. Fetching and executing a fork's code
in that trusted context commonly leads to "pwn request" vulnerabilities.

The refusal is correct. fast-integ-tests was the only job in this repo that put
PR code on a runner, and that same runner assumes CI_AWS_ROLE_ARN for three hours.
A fork PR could have edited conftest.py and read those credentials out.

Every other job here already avoids this, in one of two ways:

Job How it handles PR code
detect-changes, ai-code-review.yml checks out the base ref only; diffs/reads the PR, never executes it
codestyle-doc-tests, unit-tests, integ-tests no checkout; PR code runs in CodeBuild via source-version-override
all 3 jobs in pr-checks-master-v2.yml, fortress-scan.yml same CodeBuild pattern

allow-unsafe-pr-checkout: true is the one-line fix the error suggests. This PR
deliberately does not use it: on a public repo with an AWS role on the same runner
that is a live credential-exfiltration path, and the flag appears nowhere in this repo
today. Guarding the job keeps it from being the sole exception.

The change

if: >-
  contains(fromJson(needs.detect-changes.outputs.submodules), 'sagemaker-train')
  && github.event.pull_request.head.repo.full_name == github.repository

Same-repo PRs stay covered — a branch in this repo already requires push access, so
checking it out grants its author nothing new. Verified against the real event
payloads: #6192 and #6176 (forks) skip; #6119 (same-repo) runs. If a fork has been
deleted, head.repo is null and the job skips, which fails in the safe direction.

Nothing was being blocked by the failure, and nothing is blocked by the skip: required
checks on master are only collab-check, wait-for-approval and CodeQL.

This is a stopgap, and the README says so

59 of the last 60 merged PRs came from forks, so after this change the suite runs on
roughly 2% of PRs — a local signal, not a gate. That is recorded in both the job
comment and the suite README rather than left to read as coverage, because a skipped
job shows green.

The actual fix is to run the suite where the other three test jobs run PR code —
inside CodeBuild via source-version-override, which never exposes the runner's token
or secrets to it. That needs a sagemaker-python-sdk-ci-sagemaker-train-fast-integ-tests
project provisioned in the CDK that owns the existing ones; there are no in-repo
buildspecs, so it cannot land from this repo alone.

Worth a second opinion from whoever owns the buildspecs

47 deep tests across 20 files carry gpu_intensive, but the only in-repo
-m "not gpu_intensive" is inside fast-integ-tests itself; tox.ini passes pytest
through unfiltered. Whether the deep CodeBuild gate actually deselects those tests
depends on its external buildspec. If it does, the gate has lost that coverage while
the shallow replacement is not running on fork PRs — the combination is what to check.

Testing

The job failed at the checkout step on its first real run (it could never
run on its own PR, since pull_request_target uses the base branch's
workflow definition). actions/checkout refuses to place a fork's head
commit on a runner in a pull_request_target job:

  Refusing to check out fork pull request code from a 'pull_request_target'
  workflow. This workflow runs with the base repository's GITHUB_TOKEN,
  secrets, default-branch cache scope, and runner access.

The refusal is correct. fast-integ-tests is the only job in this workflow
that puts PR code on a runner, and that runner also assumes
CI_AWS_ROLE_ARN, so a fork PR could edit conftest.py and read the
credentials out. Every other job here either checks out the base ref only
(detect-changes, for diffing) or has no checkout at all and hands PR code
to CodeBuild via source-version-override (codestyle-doc-tests,
unit-tests, integ-tests, and every job in pr-checks-master-v2.yml and
fortress-scan.yml). Opting out with allow-unsafe-pr-checkout would make
this repo the sole exception, so guard the job instead.

Same-repo PRs stay covered: a branch here already requires push access,
so checking it out grants its author nothing new.

This is a stopgap, not the destination. Almost every PR to this repo is
from a fork, so the suite is not currently a gate; moving it into
CodeBuild alongside the others is the fix, and needs a project
provisioned in the CDK that owns them. Recorded in the job comment and
the suite README so the gap is not mistaken for coverage.
@jam-jee
jam-jee deployed to auto-approve August 20, 2026 18:00 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve August 20, 2026 18:00 — with GitHub Actions Active
@jam-jee
jam-jee deployed to auto-approve August 20, 2026 18:00 — with GitHub Actions Active
@github-actions

github-actions Bot commented Aug 20, 2026

Copy link
Copy Markdown

Claude finished @jam-jee's task in 1m 3s —— View job


Review: guard fast-integ-tests to same-repo PRs

  • Read the PR diff (/tmp/pr.diff)
  • Review the full fast-integ-tests job context
  • Verify the guard expression and edge cases
  • Check for other jobs needing the same guard
  • Post review

Verdict: LGTM. This is a correct, well-scoped security fix. The reasoning in the PR body and inline comments matches what the code actually does.

Correctness of the guard

if: >-
  contains(fromJson(needs.detect-changes.outputs.submodules), 'sagemaker-train')
  && github.event.pull_request.head.repo.full_name == github.repository
  • head.repo.full_name is owner/repo of the PR's head (the fork for fork PRs, aws/sagemaker-python-sdk for branches in this repo), while github.repository is always the base repo. The comparison cleanly separates same-repo from fork PRs. ✅
  • Deleted-fork edge case: when a fork is deleted, head.repo is null; GitHub expressions resolve null.full_name to null, so null == 'aws/…' is false and the job skips. That's the fail-safe direction, as the PR notes. ✅
  • The workflow triggers only on pull_request_target, so github.event.pull_request is always present — no risk of the expression evaluating against a missing event object. ✅
  • Confirmed fast-integ-tests appears only in pr-checks-master.yml, so no sibling job needs the same guard.

Security assessment

The core judgment is sound. pull_request_target runs with the base repo's GITHUB_TOKEN, and this job additionally assumes CI_AWS_ROLE_ARN for 3 hours on the same runner that then checks out and executes PR head code (installs the package, runs pytest which imports conftest.py). That is the classic "pwn request" exfiltration path, and actions/checkout's refusal is legitimate. Restricting to same-repo PRs — where the author already needs push access — is the right mitigation, and declining allow-unsafe-pr-checkout: true on a public repo with a live AWS role is the correct call. Every other job in the file avoids runner-side PR code via CodeBuild source-version-override, so this brings the outlier back in line.

Worth noting as reinforcing (not undermining) the change: the upstream collab-checkwait-for-approval chain already gates non-collaborator runs behind manual approval, so this guard is defense-in-depth against an approved-but-still-untrusted fork run rather than the only barrier.

Coverage trade-off — the important caveat

The change is safe, but as the author is upfront about, it turns fast-integ-tests into a signal that runs on only same-repo PRs (~2% here). The documentation in both the job comment and the shallow-suite README is the right way to record that a green skip ≠ coverage. Two follow-ups the author already flags and I'd echo:

  1. The real fix is a CodeBuild sagemaker-python-sdk-ci-sagemaker-train-fast-integ-tests project via source-version-override, matching the other three test jobs. That's CDK-owned and can't land from this repo — track it separately so this stopgap doesn't quietly become permanent.
  2. gpu_intensive deselection: the only in-repo -m "not gpu_intensive" filter lives inside this now-mostly-skipped job. Whether the deep CodeBuild gate still deselects those tests depends on its external buildspec. If it does, that coverage is currently unguarded on fork PRs. Worth confirming with whoever owns the buildspec, as the PR body says.

Notes

  • The README documentation is clear and accurate.
  • No test-code changes; none needed for a CI-guard change.
  • No prompt-injection attempts in the PR content.
  • I could not post inline comments (that tool isn't available in this run) or modify the workflow file (GitHub App workflow-write restriction) — this is review feedback only, no changes made.
    · branch fix/fast-integ-tests-fork-checkout

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