-
Notifications
You must be signed in to change notification settings - Fork 0
134 lines (120 loc) · 6.3 KB
/
Copy pathgate-verified.yml
File metadata and controls
134 lines (120 loc) · 6.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
name: gate-verified
# Closes the PR-modifiable plan-gate bypass documented in ADR-0001
# (docs/adr/0001-gate-bypass-mitigation.md). The `tofu` workflow's `gate`
# job is read from the PR head -- a PR can rename or stub it. This
# workflow is read from the default branch (workflow_run semantics), so
# a PR cannot edit it; it asserts (a) the PR's `gate` job concluded
# success AND (b) the PR's tofu-plan.yml blob matches main (or carries
# the `workflow-update` label as an explicit maintainer exception).
# Posts a single `gate-verified` check-run with a combined conclusion.
# workflow_run is the WHOLE POINT of this workflow per ADR-0001: the
# default-branch-only execution context is what makes the gate
# tamper-proof. The standard workflow_run hazard (treating PR-supplied
# data as trusted) is avoided here -- no checkout of the PR head, no
# eval of PR content; only GitHub-emitted run metadata + the API-served
# blob SHA are read, and all run-context fields are passed via `env:`
# rather than `${{ }}` interpolation inside `run:` blocks.
on: # zizmor: ignore[dangerous-triggers]
workflow_run:
workflows: [tofu]
types: [completed]
permissions:
contents: read
concurrency:
# One verification per SHA. A concurrent rerun of `tofu` on the same
# SHA queues rather than races; cancel-in-progress=false avoids losing
# an in-flight verification.
group: gate-verified-${{ github.event.workflow_run.head_sha }}
cancel-in-progress: false
jobs:
gate-verified:
name: gate-verified
runs-on: ubuntu-24.04
timeout-minutes: 2
permissions:
# checks: write -- post the gate-verified check-run.
# actions: read -- list jobs of the triggering workflow run.
# contents: read -- fetch the workflow blob SHA from main vs head.
# pull-requests: read -- look up PR labels from the commit SHA
# when the blob-compare check detects a workflow modification.
checks: write
actions: read
contents: read
pull-requests: read
steps:
- name: Harden runner
uses: step-security/harden-runner@bf7454d06d71f1098171f2acdf0cd4708d7b5920 # v2.20.0
with:
egress-policy: block
allowed-endpoints: >
api.github.com:443
- name: Verify gate job + tofu-plan.yml content + post check-run
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
REPO: ${{ github.repository }}
RUN_ID: ${{ github.event.workflow_run.id }}
HEAD_SHA: ${{ github.event.workflow_run.head_sha }}
run: |
set -euo pipefail
# ---- check 1: gate job in the triggering run concluded success.
# types: [completed] on the workflow_run trigger guarantees every
# job has settled before this step runs, so .conclusion is never
# the JSON null of an in-progress job; any non-success value
# (failure, cancelled, skipped, timed_out, "missing") fails check 1.
jobs_json=$(gh api --paginate "repos/${REPO}/actions/runs/${RUN_ID}/jobs")
gate_conclusion=$(echo "${jobs_json}" | jq -r '[.jobs[] | select(.name == "gate") | .conclusion] | (first // "missing")')
if [[ "${gate_conclusion}" == "success" ]]; then
check1_pass="true"
check1_msg="gate job in run ${RUN_ID} concluded success"
else
check1_pass="false"
check1_msg="gate job in run ${RUN_ID}: ${gate_conclusion}"
fi
# ---- check 2: tofu-plan.yml blob unchanged from main, OR PR carries
# the `workflow-update` label as an explicit maintainer exception.
# Closes the ADR-0001 stub-with-success residual for the typical
# bypass shape (PR keeps job named `gate`, replaces body with exit 0):
# the bypass requires modifying tofu-plan.yml, which this check
# flags. The label exception bounds the residual to PRs that a
# maintainer has deliberately labeled.
main_blob=$(gh api "repos/${REPO}/contents/.github/workflows/tofu-plan.yml?ref=main" --jq '.sha' 2>/dev/null || echo "missing")
head_blob=$(gh api "repos/${REPO}/contents/.github/workflows/tofu-plan.yml?ref=${HEAD_SHA}" --jq '.sha' 2>/dev/null || echo "missing")
if [[ "${main_blob}" == "${head_blob}" ]]; then
check2_pass="true"
check2_msg="tofu-plan.yml blob unchanged from main (sha=${main_blob})"
else
# Blobs differ. Check for the workflow-update label on the open
# PR that contains this head SHA. `commits/{sha}/pulls` returns
# all PRs that include the commit; we want the open one whose
# head matches.
pr_data=$(gh api "repos/${REPO}/commits/${HEAD_SHA}/pulls" --jq '[.[] | select(.state == "open" and .head.sha == "'"${HEAD_SHA}"'")] | first // empty')
if [[ -z "${pr_data}" ]]; then
check2_pass="false"
check2_msg="tofu-plan.yml differs from main (head=${head_blob}, main=${main_blob}) and no open PR found for this head SHA"
else
has_label=$(echo "${pr_data}" | jq -r '[.labels[] | select(.name == "workflow-update")] | length')
if [[ "${has_label}" == "0" ]]; then
check2_pass="false"
check2_msg="tofu-plan.yml differs from main (head=${head_blob}, main=${main_blob}) and \"workflow-update\" label is not applied"
else
check2_pass="true"
check2_msg="tofu-plan.yml differs from main (head=${head_blob}, main=${main_blob}) but \"workflow-update\" label is applied (maintainer exception)"
fi
fi
fi
# ---- combined conclusion
if [[ "${check1_pass}" == "true" && "${check2_pass}" == "true" ]]; then
conclusion="success"
else
conclusion="failure"
fi
summary=$(printf 'check 1 (gate-conclusion): %s\ncheck 2 (workflow-content): %s' "${check1_msg}" "${check2_msg}")
gh api -X POST "repos/${REPO}/check-runs" \
-f name="gate-verified" \
-f head_sha="${HEAD_SHA}" \
-f status="completed" \
-f conclusion="${conclusion}" \
-f "output[title]=gate-verified" \
-f "output[summary]=${summary}"
echo "${summary}" >> "${GITHUB_STEP_SUMMARY}"
[[ "${conclusion}" == "success" ]]