Finding
馃敶 GATE: Security Gate (Fork PRs) (.github/workflows/security-gate-pr-target.yml) has never started on any PR in the most recent page of its runs. All 30 of 30 runs on actions/workflows/security-gate-pr-target.yml/runs?per_page=30, from 2026-09-30T14:07Z to 23:00Z, ended in startup_failure with zero jobs. 30 is the page size, so the real total is at least 30.
The startup text on run 36788265205 (PR #1088 head 6192c92d) says:
Event 'pull_request_target' is not allowed to trigger Actions workflows. Workflow file: '.github/workflows/security-gate-pr-target.yml'.
This is not a parse defect. #997 (2026-09-22) fixed an empty-expression parse error in this file, and the file on main@3238461b parses. The Actions policy refuses the trigger itself. GET repos/hyperpolymath/standards/actions/permissions returns only enabled, allowed_actions, selected_actions_url and sha_pinning_required, with no event field. That call does not show which setting does the refusing, and this issue does not guess.
Not a merge blocker: the gate's contexts are not among the 21 required status checks on main. Filed under the 2026-09-15 stopping rule (a new finding is an issue, not a blocker).
Scope of this observation
- A local scan of
hyper-repos/ and meta-repos/ (601 .github/workflows directories) found pull_request_target in 9 files across 7 checkouts. Only standards is estate-authored. The other six are third-party forks under _OPM, a bounty fork, an esoteric-set repo, or a duplicate standards checkout.
- Bounded: local clones can be stale, so this does not prove that no other estate repo carries the trigger on GitHub.
Recommended cure (elegance arm, OWASP D223)
Move the gate from pull_request_target to pull_request:
pull_request_target is the "pwn request" class: fork-controlled content handled with a base-context token. The file's own comments already spend about 20 lines defending against it. The policy that refuses the event matches the estate's OWASP-safe ruling, so the gate should change, not the policy.
- On
pull_request, a fork PR gets a read-only token and no secrets. The gate only statically scans the diff, so it needs nothing more.
- Replace the
actions/github-script PR comment, which needs pull-requests: write, with ::error:: annotations and a job summary, and fail the job on findings.
- The PR's own copy of the workflow will then run, not the base copy. That is acceptable for a non-required advisory scan. If it is ever made required, record that trade-off in the same PR.
The alternative arm, re-enabling pull_request_target wherever the policy lives, widens the attack surface and conflicts with D223. It needs an owner ruling.
Acceptance criteria
security-gate-pr-target.yml (renamed if appropriate) runs with jobs > 0 on at least three consecutive PRs, measured on actions/workflows/<file>/runs, not inferred from the check rollup, which cannot see startup_failure.
- Planted positive control: a test PR (or a fixture exercised by the self-test suite) containing content the malicious-content scan must flag turns the gate red. A gate that cannot go red is not a gate.
- No
pull_request_target remains in standards/.github/workflows/, or, if the owner rules for re-enabling it, the PR records where the setting lives with a before/after read of it.
- The job's
permissions: block grants no write scope.
- The rsr template and any estate copy of this gate are propagated in the same campaign ("settle in standards, then propagate").
馃 Generated with Claude Code
https://claude.ai/code/session_01QYY8Gp4v4x2J7iSNn1vZ57
Finding
馃敶 GATE: Security Gate (Fork PRs)(.github/workflows/security-gate-pr-target.yml) has never started on any PR in the most recent page of its runs. All 30 of 30 runs onactions/workflows/security-gate-pr-target.yml/runs?per_page=30, from 2026-09-30T14:07Z to 23:00Z, ended instartup_failurewith zero jobs. 30 is the page size, so the real total is at least 30.The startup text on run 36788265205 (PR #1088 head
6192c92d) says:This is not a parse defect. #997 (2026-09-22) fixed an empty-expression parse error in this file, and the file on
main@3238461bparses. The Actions policy refuses the trigger itself.GET repos/hyperpolymath/standards/actions/permissionsreturns onlyenabled,allowed_actions,selected_actions_urlandsha_pinning_required, with no event field. That call does not show which setting does the refusing, and this issue does not guess.Not a merge blocker: the gate's contexts are not among the 21 required status checks on
main. Filed under the 2026-09-15 stopping rule (a new finding is an issue, not a blocker).Scope of this observation
hyper-repos/andmeta-repos/(601.github/workflowsdirectories) foundpull_request_targetin 9 files across 7 checkouts. Only standards is estate-authored. The other six are third-party forks under_OPM, a bounty fork, an esoteric-set repo, or a duplicate standards checkout.Recommended cure (elegance arm, OWASP D223)
Move the gate from
pull_request_targettopull_request:pull_request_targetis the "pwn request" class: fork-controlled content handled with a base-context token. The file's own comments already spend about 20 lines defending against it. The policy that refuses the event matches the estate's OWASP-safe ruling, so the gate should change, not the policy.pull_request, a fork PR gets a read-only token and no secrets. The gate only statically scans the diff, so it needs nothing more.actions/github-scriptPR comment, which needspull-requests: write, with::error::annotations and a job summary, and fail the job on findings.The alternative arm, re-enabling
pull_request_targetwherever the policy lives, widens the attack surface and conflicts with D223. It needs an owner ruling.Acceptance criteria
security-gate-pr-target.yml(renamed if appropriate) runs with jobs > 0 on at least three consecutive PRs, measured onactions/workflows/<file>/runs, not inferred from the check rollup, which cannot seestartup_failure.pull_request_targetremains instandards/.github/workflows/, or, if the owner rules for re-enabling it, the PR records where the setting lives with a before/after read of it.permissions:block grants no write scope.馃 Generated with Claude Code
https://claude.ai/code/session_01QYY8Gp4v4x2J7iSNn1vZ57