-
Notifications
You must be signed in to change notification settings - Fork 0
174 lines (162 loc) · 8.34 KB
/
Copy pathcodeql.yml
File metadata and controls
174 lines (162 loc) · 8.34 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
name: CodeQL
on:
push:
branches: [main]
pull_request:
# Required for merge-queue entries: queued runs report checks via merge_group.
merge_group:
types: [checks_requested]
schedule:
- cron: "0 6 * * 1" # Weekly Monday 6am UTC
permissions:
actions: read
security-events: write
contents: read
concurrency:
# Cancel superseded first-attempt PR runs. Non-PR runs and manual reruns get
# run-unique groups so a pending or newer run is never cancelled or replaced.
# Pinned by tests/integration/ci/workflow-concurrency.test.ts; see cicd.yml and
# veryfront-studio#5544.
group: ${{ github.event_name == 'pull_request' && github.run_attempt == '1' && format('{0}-{1}', github.workflow, github.ref) || format('{0}-{1}', github.workflow, github.run_id) }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
analyze:
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
name: Analyze
runs-on: ubuntu-latest
# CodeQL takes ~10 minutes on this repo. Cap the job well under the merge
# queue's check_response_timeout (1800s) so that a hung step fails fast and
# visibly, rather than stalling until the Actions default of 360 minutes —
# which would reproduce the very ejection this workflow exists to prevent.
timeout-minutes: 25
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: github/codeql-action/init@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
with:
languages: javascript-typescript
queries: security-and-quality
# A merge-queue batch can dissolve while this job is still running. GitHub
# then deletes the ephemeral gh-readonly-queue ref, and the SARIF upload —
# which starts ~10 minutes in, after the analysis itself has already
# succeeded — fails with "ref ... not found in this repository". Nothing is
# being merged at that point, so the run is moot rather than failing, but
# the queue reads the red check as the PR's fault and ejects it.
#
# github/codeql-action offers no supported way to express "this run is
# moot" (github/codeql-action#1572, open since March 2023 with no fix), so
# the outcome is captured here and re-raised by the guard below.
#
# What the guard actually tolerates, stated plainly: ANY analyze failure on
# a merge_group run whose target ref has provably gone away, or has moved
# to a different SHA. That is broader than the upload 404 that motivated it
# — an extractor crash or a query failure landing in the same window is
# swallowed too. It is safe because check runs are bound to a SHA: if the
# queue branch is gone or has moved, nothing can be merged on the strength
# of this run, and the rebuilt entry re-runs CodeQL from scratch. Every
# other case fails the build — a non-merge_group event, a ref still live at
# our SHA, an unparseable response, or any indeterminate API status.
#
# ASSUMPTION — the merge queue's merge_method is SQUASH, which mints a
# fresh SHA for every rebuild, so a moot run's SHA can never recur on a
# later live entry. Under REBASE a no-op rebase could reproduce the same
# SHA, and this run's moot-green could then satisfy a live entry. If the
# queue's merge method ever changes, revisit this guard first.
- id: analyze
uses: github/codeql-action/analyze@b96794f015dfd88f77b49b1c93e0fa7110f94c63 # v4.38.0
continue-on-error: true
with:
category: "/language:javascript-typescript"
- name: Re-raise CodeQL failure unless the target ref is gone
if: ${{ steps.analyze.outcome == 'failure' }}
env:
GH_TOKEN: ${{ github.token }}
EVENT_NAME: ${{ github.event_name }}
TARGET_REF: ${{ github.ref }}
TARGET_SHA: ${{ github.sha }}
run: |
set -euo pipefail
# Only merge-queue refs are ephemeral. Every other event keeps its ref,
# so a CodeQL failure there is always the build's problem.
if [ "$EVENT_NAME" != "merge_group" ]; then
echo "::error::CodeQL failed on a ${EVENT_NAME} run. Failing the build."
exit 1
fi
body="$(mktemp)"
trap 'rm -f "$body"' EXIT
# This turns a red required check green, so every tolerated path leaves
# an auditable line in the job summary, not only in the raw log.
summary="${GITHUB_STEP_SUMMARY:-/dev/null}"
# curl already emits 000 through -w on a transport failure, so no
# shell-side fallback is wanted: `|| echo 000` would append a second
# sentinel and yield the literal string 000000. The explicit assignment
# below keeps the sentinel exactly three zeroes.
#
# The request is bounded and retried. An unbounded hang against
# api.github.com would hold the job open past the queue's
# check_response_timeout and eject the PR — the exact failure this
# guard exists to prevent. Worst case here is ~2 minutes.
if ! status="$(curl -sS \
--connect-timeout 10 \
--max-time 30 \
--retry 3 \
--retry-delay 2 \
--retry-all-errors \
-o "$body" -w '%{http_code}' \
-H "Authorization: Bearer ${GH_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "X-GitHub-Api-Version: 2022-11-28" \
"${GITHUB_API_URL}/repos/${GITHUB_REPOSITORY}/git/ref/${TARGET_REF#refs/}")"; then
status="000"
fi
case "$status" in
404)
echo "::notice::${TARGET_REF} no longer exists: the merge-queue batch dissolved" \
"while CodeQL was uploading. Nothing is being merged from this run, so its" \
"result cannot gate anything. Treating it as moot."
{
echo "### CodeQL failure tolerated as moot"
echo
echo "A failing CodeQL run was reported as successful."
echo
echo "- Target ref: \`${TARGET_REF}\`"
echo "- Run SHA: \`${TARGET_SHA}\`"
echo "- Reason: the ref returned HTTP 404 — the merge-queue batch dissolved while this job was running, so this run cannot gate any merge."
} >> "$summary"
;;
200)
# A 200 whose body carries no object SHA leaves us unable to tell a
# live ref from a dead one. That must fail closed: without this
# check an empty value compares unequal to TARGET_SHA and falls
# into the tolerant "batch was rebuilt" path below.
if ! current="$(jq -r '.object.sha // empty' "$body")"; then
current=""
fi
if [ -z "$current" ]; then
echo "::error::${TARGET_REF} returned HTTP 200 but the response carried no object SHA," \
"so whether this run is still live cannot be determined. Failing closed."
exit 1
fi
if [ "$current" = "$TARGET_SHA" ]; then
echo "::error::${TARGET_REF} still points at ${TARGET_SHA}, so this run is live." \
"The CodeQL failure is genuine. Failing the build."
exit 1
fi
echo "::notice::${TARGET_REF} has moved from ${TARGET_SHA} to ${current}: the batch" \
"was rebuilt without this run. Its result cannot gate the new head. Treating it as moot."
{
echo "### CodeQL failure tolerated as moot"
echo
echo "A failing CodeQL run was reported as successful."
echo
echo "- Target ref: \`${TARGET_REF}\`"
echo "- Run SHA: \`${TARGET_SHA}\`"
echo "- Ref now at: \`${current}\`"
echo "- Reason: the batch was rebuilt without this run, so this run cannot gate the new head."
} >> "$summary"
;;
*)
echo "::error::Could not determine whether ${TARGET_REF} still exists (HTTP ${status};" \
"000 means the request failed at the transport level). Failing closed."
exit 1
;;
esac