Skip to content

feat(untrusted_checkout_exec): account for actions/checkout allow-unsafe-pr-checkout#442

Draft
fproulx-boostsecurity wants to merge 1 commit into
mainfrom
untrusted-checkout-allow-unsafe-pr-checkout
Draft

feat(untrusted_checkout_exec): account for actions/checkout allow-unsafe-pr-checkout#442
fproulx-boostsecurity wants to merge 1 commit into
mainfrom
untrusted-checkout-allow-unsafe-pr-checkout

Conversation

@fproulx-boostsecurity

Copy link
Copy Markdown
Contributor

Context

GitHub changed actions/checkout defaults: from v7.0.0 (9c091bb…), and backported to v4/v5/v6 on 2026-07-16, checkout refuses to fetch untrusted fork PR code unless allow-unsafe-pr-checkout: true is set. When that protection is in effect, the untrusted code never lands, so untrusted_checkout_exec would become a false positive.

This makes the rule aware of the change — suppressing the finding only where the guard actually applies, while keeping coverage everywhere it doesn't.

What it does

Suppresses the finding for an actions/checkout@<ref> step only when all hold:

  • the ref enforces the safe default — v7+ / main / a SHA not in the frozen pre-fix set / v4v6 once the backport date passes;
  • allow-unsafe-pr-checkout is not enabled (a ${{ … }} expression counts as possibly-true → not suppressed);
  • the trigger is guard-covered — pull_request_target, or workflow_run whose parent is pull_request/pull_request_target.

Still fires for: old/SHA-pinned vulnerable versions, allow-unsafe-pr-checkout: true, uncovered events (issues/issue_comment/workflow_call), and untrusted checkout via gh pr checkout or raw git in run: blocks (explicitly out of scope of GitHub's change — and newly detected here).

Approach

  • models: capture with: allow-unsafe-pr-checkout (string, preserves absent/true/false/expr).
  • opa/rego/external/checkout_unsafe.rego: frozen, embedded set of pre-fix checkout commit SHAs (default-allow bad-set) + backport_floor_date. Fully offline (analyze_local).
  • utils: ref resolver (SHA set-membership; tags via major/date gate) + guard helpers; new raw-git untrusted-checkout detection branch (never suppressed).
  • rule: event-aware suppression; same-step scan for gh/git run-block checkouts (also closes a pre-existing same-step gap for gh pr checkout).
  • scanner: inject scan_time into the findings eval (POUTINE_SCAN_TIME overridable for reproducible / pinned scans).
  • regen tool: build-tag-gated (checkout_unsafe_shas) + make update-checkout-shas, with anti-gap assertions; idempotent against the committed data.

Tests

  • TestUntrustedCheckoutGuard — full fire/suppress matrix incl. git vectors and before/after the backport date.
  • TestCheckoutGuardResolution — 19-case resolver matrix.
  • Model parsing test; deterministic scan clock pinned for the package + snapshot harness.
  • Live snapshot regenerated against messypoutine: 0 deltas attributable to this change (no findings suppressed, no new git false positives).

Rollout note ⚠️

The bad-set is complete only after the v4/v5/v6 backports land. Before merging post-2026-07-16: add the v4/v5/v6 backport fix-commit SHAs to fixCommits in the regen tool and run make update-checkout-shas. Until then the date-gate correctly keeps v4/v5/v6 firing. Draft pending that data freeze.

🤖 Generated with Claude Code

…afe-pr-checkout

GitHub's actions/checkout now refuses to fetch untrusted fork PR code by
default (v7.0.0, backported to v4/v5/v6 on 2026-07-16) unless
allow-unsafe-pr-checkout: true is set. Suppress the finding when the checkout
is on a fixed version with the safe default in effect, scoped to the events
the guard actually covers (pull_request_target and PR-triggered workflow_run).

- models: capture `with: allow-unsafe-pr-checkout` (string; absent/true/false/expr)
- opa/rego/external/checkout_unsafe.rego: frozen set of pre-fix checkout SHAs
  (default-allow bad-set) + backport_floor_date; offline, works with analyze_local
- utils: resolver (SHA set membership; tags via major/date gate) + guard helpers;
  new raw-`git` untrusted-checkout detection branch (git fetch pull/N/head,
  git checkout of a head ref) — never suppressed, like gh pr checkout
- rule: event-aware suppression; same-step scan for gh/git run-block checkouts
- scanner: inject scan_time into findings eval (POUTINE_SCAN_TIME overridable)
- regen tool (build-tag checkout_unsafe_shas) + `make update-checkout-shas`
- tests: fire/suppress matrix incl. git vectors and before/after backport;
  resolver eval matrix; deterministic scan clock pinned for tests/snapshots

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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