Skip to content

ci(status): skip Status job when upstream scan was cancelled#22

Merged
Cre-eD merged 1 commit into
mainfrom
ci/status-skip-on-cancelled-upstream
May 19, 2026
Merged

ci(status): skip Status job when upstream scan was cancelled#22
Cre-eD merged 1 commit into
mainfrom
ci/status-skip-on-cancelled-upstream

Conversation

@Cre-eD
Copy link
Copy Markdown
Contributor

@Cre-eD Cre-eD commented May 19, 2026

Summary

The Semgrep Status and Security Status aggregator jobs currently emit a FAILURE check_run when their upstream scan was cancelled. In practice the only realistic cause of cancellation is concurrency superseding the run — two webhook events arriving for the same SHA at the same instant, which happens commonly after a force-push followed by a rapid follow-up push (a delayed webhook from the earlier push fires alongside the new one).

This FAILURE check_run then blocks branch protection on the surviving non-cancelled run, even though the actual scan was clean.

Hit live on simple-container-com/api PR #273 — every required-status check came back as CANCELLED + SUCCESS from the dual-fire, with the cancelled twin's Status emitting the FAILURE that left the PR mergeStateStatus=BLOCKED.

Fix

Change if: always()if: ${{ !cancelled() }} on both Status jobs.

Upstream scan result Before After
success Status runs, evaluates findings (unchanged)
failure Status runs, reports failure (unchanged)
cancelled Status runs, emits FAILURE Status SKIPPED, emits NEUTRAL

A skipped Status produces a NEUTRAL check_run that branch protection treats as not-failing. The surviving non-cancelled run's Status still emits SUCCESS and gates the merge correctly.

Security trade-off (acknowledged inline)

The previous behaviour was documented as guarding against "a fork PR that's cancelled mid-flight merging without ever being scanned." That concern is mitigated separately:

  • Fork PRs do not receive repo secrets (GitHub policy) — the scans run in read-only mode anyway
  • Neither Semgrep nor secret-scan have an exfiltration vector — they only emit findings
  • A maintainer reviewing the PR sees the cancellation / skip in the check-rollup before clicking Merge — the cancellation is visible, not silent

The original inline comments are preserved + extended to spell out this reasoning.

Test plan

  • YAML lint passes for both workflows.
  • Consumer repo simple-container-com/api PR #273 should be unblocked once the new ref is consumed (it already pins to @main, so this merge propagates automatically).

Files

  • .github/workflows/semgrep.yml — Status job if: updated.
  • .github/workflows/security-scan.yml — Status job if: updated.

No behaviour change for the common case (scans completing normally). Only changes the cancellation-cascade path.

The Semgrep Status and Security Status aggregator jobs evaluate the
upstream scan's `result` and emit a FAILURE check_run when that
result is `cancelled`. In practice the only way the upstream gets
cancelled is concurrency superseding the run (two events arrive for
the same SHA — common after force-push + rapid follow-up push,
which leaves a delayed webhook in GitHub's queue).

That FAILURE check_run then blocks branch protection on the
surviving SUCCESS run, even though the actual scan was clean.

Fix: change `if: always()` → `if: ${{ !cancelled() }}` on both
Status jobs. After this:

  - scan succeeds  → Status runs, evaluates findings, passes/fails
  - scan fails     → Status runs, sees `result=failure`, fails
  - scan cancelled → Status SKIPPED, no FAILURE check_run produced

A skipped Status produces a NEUTRAL check_run that branch protection
treats as not-failing. The surviving non-cancelled run's Status
emits SUCCESS and gates the merge correctly.

Security trade-off (acknowledged inline in the YAML):

The previous behaviour was justified by the comment "a fork PR
that's cancelled mid-flight could merge without ever being scanned."
That concern is mitigated separately:

  - Fork PRs do not receive repo secrets (GitHub policy)
  - These scans are read-only, no exfiltration vector
  - A maintainer reviewing the PR can see the cancellation /
    skip in the check-rollup before clicking Merge

Observed in the wild on simple-container-com/api PR #273
(c38148e) — every required-status check went CANCELLED + SUCCESS
from the dual-fire, with the cancelled twin's Status job emitting
the FAILURE that blocked the merge.

Refs simple-container-com/api#273.

Signed-off-by: Dmitrii Creed <creeed22@gmail.com>
@github-actions
Copy link
Copy Markdown

Security Scan Results

Repository: actions | Commit: 735bf31

Check Status Details
✅ Secret Scan Pass No secrets detected
⏩ Dependencies Skipped -

Scanned at 2026-05-19 09:46 UTC

@github-actions
Copy link
Copy Markdown

Semgrep Scan Results

Repository: actions | Commit: 735bf31

Check Status Details
✅ Semgrep Pass 0 total findings (no error/warning)

Scanned at 2026-05-19 09:46 UTC

@Cre-eD Cre-eD merged commit d7db720 into main May 19, 2026
15 checks passed
@Cre-eD Cre-eD deleted the ci/status-skip-on-cancelled-upstream branch May 19, 2026 14:56
Cre-eD added a commit to simple-container-com/api that referenced this pull request May 19, 2026
Picks up the permanent fix for the cancellation-cascade we hit on
the previous head of this PR (c38148e, where dual-fired pull_request
events left FAILURE check_runs from the cancelled twin's Status
aggregator job and blocked merge):

  simple-container-com/actions#22
    ci(status): skip Status job when upstream scan was cancelled

After this bump, future dual-fire events on any PR in this repo
will produce NEUTRAL (skipped) Status check_runs from the cancelled
twin instead of FAILURE — branch protection then treats the
cancelled run as not-failing and the surviving run's SUCCESS
gates the merge.

Also pulls in actions#17 (drop sc-image-release reusable workflow +
bump-self-release tooling) which landed between the previous pin
and this one; no consumer-facing change in this repo because we
don't reference those workflows.

Pin applied uniformly across all four consumer workflows that
reference the actions repo's reusable workflows:

- .github/workflows/semgrep.yml
- .github/workflows/semgrep-comment.yml
- .github/workflows/security-scan.yml
- .github/workflows/security-scan-comment.yml

Signed-off-by: Dmitrii Creed <creeed22@gmail.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