Skip to content

security: bind reusable SAST and security scans to the exact pull-request head #1222

Description

@seonghobae

Problem

Organization-required reusable security workflows currently produce synthetic-merge evidence for pull requests instead of scanning the exact submitted head.

Fresh reproduction on ContextualWisdomLab/g7#1 on 2026-08-22:

  • exact PR head: 4a1773f604b3971478f486557dac137d4202086c
  • GitHub synthetic merge: 8fb41406de757dd2c8edf7a7006c8bd4c68bb29f
  • Security Scan run 32558956134 checked out the synthetic merge ref;
  • SAST Semgrep run 32558956167 checked out the same synthetic merge ref;
  • the current central sast-semgrep.yml checkout has no explicit PR-head ref;
  • the current central security-scan.yml Trivy/dependency-review checkout paths likewise do not bind the scan to the submitted head.

This violates the organization merge-evidence contract: synthetic merge, predecessor-head, queued, skipped-required, or otherwise non-exact evidence must never be promoted to exact-current-head proof.

The g7 canary also shows why the distinction matters operationally: the reusable scans currently fail on broad repository/base findings (Trivy dependency findings and 234 Semgrep warning/error findings), so the leaf readiness PR must not absorb unrelated remediation simply to manufacture a passing gate.

Responsibility boundary

Fix this in the organization-central reusable workflow layer. Do not duplicate or weaken the gate in downstream repositories and do not change leaf application code to compensate for checkout semantics.

Required design

For pull-request runs, resolve and verify the immutable target repository plus pull_request.head.sha before scanning. Preserve the repository's normal default ref for protected-branch pushes and other explicitly supported events. Where a scanner legitimately compares base and head, materialize both immutable revisions and make the evidence labels unambiguous rather than treating a synthetic merge ref as the submitted head.

SARIF attribution, job summaries, logs, artifacts, and any machine-readable evidence should record the target repository, exact submitted head SHA, base SHA where relevant, and the actual checked-out commit. Fail closed if those identities disagree.

Acceptance criteria

  • SAST Semgrep pull-request jobs scan github.event.pull_request.head.sha from the correct target/head repository, not refs/pull/*/merge.
  • Trivy filesystem and other head-oriented Security Scan jobs scan the exact submitted PR head.
  • Base/head comparison jobs materialize and label both immutable revisions without conflating either with the synthetic merge commit.
  • A focused workflow contract test rejects a PR checkout without an exact-head ref.
  • A downstream canary proves git rev-parse HEAD == pull_request.head.sha in SAST and Security Scan evidence.
  • SARIF/check evidence remains associated with the intended pull request without weakening severity thresholds or suppressing valid findings.
  • Existing least-privilege permissions, immutable action/container pins, cancellation behavior, and fail-closed security gates remain intact.
  • No manual success status, stale evidence, or branch-protection bypass is introduced.

Downstream dependency

g7#1 must remain unmerged while its exact-current-head CI/security/review/protection contract is incomplete. Its broad pre-existing dependency/SAST baseline should be handled as separate security-owner work rather than mixed into the bounded readiness change.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: authAuthentication, authorization, identity, or tenant isolationarea: ci-cdCI, GitHub Actions, checks, release, or supply chainarea: dependenciesDependency or lockfile maintenancearea: securitySecurity boundary, hardening, or vulnerability preventionpriority: highHigh-priority or P1 workstatus: triagedOpen issue has an organization taxonomy assignmenttype: securitySecurity vulnerability or security-specific remediation

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions