FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix: only skip CVE remediation for open PRs (#1606) · sourcebot-dev/sourcebot@6d1e610 · GitHub

Commit 6d1e610

Browse files
fix: only skip CVE remediation for open PRs (#1606)
1 parent 3ce4393 commit 6d1e610

5 files changed

Lines changed: 139 additions & 17 deletions

File tree

‎.github/scripts/filter-unlinked-cve-issues.jq‎

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
1-
def has_linked_github_pr:
1+
def has_open_linked_github_pr:
22
any(
3-
.attachments.nodes[]?.url?;
4-
type == "string"
5-
and test("^https://github\\.com/[^/]+/[^/]+/pull/[0-9]+(?:[/?#].*)?$")
3+
.attachments.nodes[]?;
4+
(.url? | type == "string"
5+
and test("^https://github\\.com/[^/]+/[^/]+/pull/[0-9]+(?:[/?#].*)?$"))
6+
and .githubPrState? == "open"
67
);
78

89
[
910
.[]
1011
| select(any(.labels.nodes[]?; .name == "CVE"))
11-
| select(has_linked_github_pr | not)
12+
| select(has_open_linked_github_pr | not)
1213
| {
1314
id,
1415
identifier,

‎.github/scripts/find-unlinked-cve-issues.sh‎

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,4 +103,56 @@ while true; do
103103
fi
104104
done
105105

106+
github_pr_states='[]'
107+
while IFS= read -r pr_url; do
108+
if [[ ! "$pr_url" =~ ^https://github\.com/([^/]+)/([^/]+)/pull/([0-9]+)([/?#].*)?$ ]]; then
109+
echo "Could not parse linked GitHub pull request URL: $pr_url" >&2
110+
exit 1
111+
fi
112+
113+
owner="${BASH_REMATCH[1]}"
114+
repository="${BASH_REMATCH[2]}"
115+
pull_number="${BASH_REMATCH[3]}"
116+
if ! pr_state=$(gh api "repos/$owner/$repository/pulls/$pull_number" --jq '.state'); then
117+
echo "Could not fetch linked GitHub pull request: $pr_url" >&2
118+
exit 1
119+
fi
120+
121+
if [[ "$pr_state" != "open" && "$pr_state" != "closed" ]]; then
122+
echo "GitHub returned an unexpected state for $pr_url: $pr_state" >&2
123+
exit 1
124+
fi
125+
126+
github_pr_states=$(jq -cn \
127+
--argjson states "$github_pr_states" \
128+
--arg url "$pr_url" \
129+
--arg state "$pr_state" \
130+
'$states + [{url: $url, state: $state}]')
131+
done < <(jq -r '
132+
[
133+
.[]
134+
| select(any(.labels.nodes[]?; .name == "CVE"))
135+
| .attachments.nodes[]?.url?
136+
| strings
137+
| select(test("^https://github\\.com/[^/]+/[^/]+/pull/[0-9]+(?:[/?#].*)?$"))
138+
]
139+
| unique[]
140+
' <<<"$all_issues")
141+
142+
all_issues=$(jq -cn \
143+
--argjson issues "$all_issues" \
144+
--argjson states "$github_pr_states" '
145+
($states | map({key: .url, value: .state}) | from_entries) as $states_by_url
146+
| $issues
147+
| map(
148+
.attachments.nodes |= map(
149+
if $states_by_url[.url] != null then
150+
. + {githubPrState: $states_by_url[.url]}
151+
else
152+
.
153+
end
154+
)
155+
)
156+
')
157+
106158
jq -c -f "$FILTER" <<<"$all_issues"

‎.github/scripts/test-cve-remediation.sh‎

Lines changed: 74 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,22 +60,22 @@ ISSUES='[
6060
{
6161
"id": "issue-2",
6262
"identifier": "SOU-2",
63-
"title": "[sourcebot-dev/example] CVE-2: linked in this repository",
63+
"title": "[sourcebot-dev/example] CVE-2: open PR linked in this repository",
6464
"url": "https://linear.app/sourcebot/issue/SOU-2/test",
6565
"priority": 2,
6666
"state": {"name": "In Progress", "type": "started"},
6767
"labels": {"nodes": [{"name": "CVE"}]},
68-
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/example/pull/42"}]}
68+
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/example/pull/42", "githubPrState": "open"}]}
6969
},
7070
{
7171
"id": "issue-3",
7272
"identifier": "SOU-3",
73-
"title": "[sourcebot-dev/example] CVE-3: linked in a companion repository",
73+
"title": "[sourcebot-dev/example] CVE-3: closed PR linked in a companion repository",
7474
"url": "https://linear.app/sourcebot/issue/SOU-3/test",
7575
"priority": 1,
7676
"state": {"name": "Todo", "type": "unstarted"},
7777
"labels": {"nodes": [{"name": "CVE"}]},
78-
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/companion/pull/9/files"}]}
78+
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/companion/pull/9/files", "githubPrState": "closed"}]}
7979
},
8080
{
8181
"id": "issue-4",
@@ -100,6 +100,15 @@ ISSUES='[
100100
]'
101101

102102
EXPECTED='[
103+
{
104+
"id": "issue-3",
105+
"identifier": "SOU-3",
106+
"title": "[sourcebot-dev/example] CVE-3: closed PR linked in a companion repository",
107+
"url": "https://linear.app/sourcebot/issue/SOU-3/test",
108+
"priority": 1,
109+
"status": "Todo",
110+
"statusType": "unstarted"
111+
},
103112
{
104113
"id": "issue-5",
105114
"identifier": "SOU-5",
@@ -121,14 +130,15 @@ EXPECTED='[
121130
]'
122131

123132
assert_json \
124-
"keeps only CVEs without a linked GitHub pull request and sorts by priority" \
133+
"keeps CVEs without an open linked GitHub pull request and sorts by priority" \
125134
"$(jq -c -f "$FILTER" <<<"$ISSUES")" \
126135
"$EXPECTED"
127136

128137
FAKE_CURL_DIR=$(mktemp -d)
129138
FAKE_CURL_COUNT=$(mktemp)
130139
FAKE_CURL_PAYLOAD_DIR=$(mktemp -d)
131-
trap 'rm -rf "$FAKE_CURL_DIR" "$FAKE_CURL_PAYLOAD_DIR"; rm -f "$FAKE_CURL_COUNT"' EXIT
140+
FAKE_GH_LOG=$(mktemp)
141+
trap 'rm -rf "$FAKE_CURL_DIR" "$FAKE_CURL_PAYLOAD_DIR"; rm -f "$FAKE_CURL_COUNT" "$FAKE_GH_LOG"' EXIT
132142
printf '0\n' > "$FAKE_CURL_COUNT"
133143

134144
cat > "$FAKE_CURL_DIR/curl" <<'EOF'
@@ -181,12 +191,22 @@ elif ((count == 2)); then
181191
{
182192
"id": "page-1-linked",
183193
"identifier": "SOU-21",
184-
"title": "[sourcebot-dev/example] CVE-21: linked",
194+
"title": "[sourcebot-dev/example] CVE-21: open PR linked",
185195
"url": "https://linear.app/sourcebot/issue/SOU-21/test",
186196
"priority": 1,
187197
"state": {"name": "Backlog", "type": "backlog"},
188198
"labels": {"nodes": [{"name": "CVE"}]},
189199
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/example/pull/21"}]}
200+
},
201+
{
202+
"id": "page-1-closed-pr",
203+
"identifier": "SOU-23",
204+
"title": "[sourcebot-dev/example] CVE-23: closed PR linked",
205+
"url": "https://linear.app/sourcebot/issue/SOU-23/test",
206+
"priority": 2,
207+
"state": {"name": "Backlog", "type": "backlog"},
208+
"labels": {"nodes": [{"name": "CVE"}]},
209+
"attachments": {"nodes": [{"url": "https://github.com/sourcebot-dev/companion/pull/23/files"}]}
190210
}
191211
],
192212
"pageInfo": {"hasNextPage": true, "endCursor": "next-page"}
@@ -220,10 +240,38 @@ printf '200'
220240
EOF
221241
chmod +x "$FAKE_CURL_DIR/curl"
222242

243+
cat > "$FAKE_CURL_DIR/gh" <<'EOF'
244+
#!/usr/bin/env bash
245+
set -euo pipefail
246+
247+
if [[ "$1" != "api" ]]; then
248+
echo "Unexpected gh command: $*" >&2
249+
exit 1
250+
fi
251+
252+
endpoint="$2"
253+
printf '%s\n' "$endpoint" >> "$FAKE_GH_LOG"
254+
case "$endpoint" in
255+
repos/sourcebot-dev/example/pulls/21)
256+
printf 'open\n'
257+
;;
258+
repos/sourcebot-dev/companion/pulls/23)
259+
printf 'closed\n'
260+
;;
261+
*)
262+
echo "Unexpected GitHub API endpoint: $endpoint" >&2
263+
exit 1
264+
;;
265+
esac
266+
EOF
267+
chmod +x "$FAKE_CURL_DIR/gh"
268+
223269
DISCOVERED=$(
224270
PATH="$FAKE_CURL_DIR:$PATH" \
225271
FAKE_CURL_COUNT="$FAKE_CURL_COUNT" \
226272
FAKE_CURL_PAYLOAD_DIR="$FAKE_CURL_PAYLOAD_DIR" \
273+
FAKE_GH_LOG="$FAKE_GH_LOG" \
274+
GH_TOKEN="test-token" \
227275
LINEAR_API_KEY="test-key" \
228276
LINEAR_TEAM_ID="team-key" \
229277
LINEAR_GRAPHQL_ATTEMPTS=1 \
@@ -240,6 +288,15 @@ EXPECTED_DISCOVERED='[
240288
"status": "Todo",
241289
"statusType": "unstarted"
242290
},
291+
{
292+
"id": "page-1-closed-pr",
293+
"identifier": "SOU-23",
294+
"title": "[sourcebot-dev/example] CVE-23: closed PR linked",
295+
"url": "https://linear.app/sourcebot/issue/SOU-23/test",
296+
"priority": 2,
297+
"status": "Backlog",
298+
"statusType": "backlog"
299+
},
243300
{
244301
"id": "page-1-unlinked",
245302
"identifier": "SOU-20",
@@ -251,6 +308,10 @@ EXPECTED_DISCOVERED='[
251308
}
252309
]'
253310
assert_json "paginates Linear results and filters before invoking Claude" "$DISCOVERED" "$EXPECTED_DISCOVERED"
311+
assert_json \
312+
"checks every unique linked GitHub pull request state" \
313+
"$(jq -Rsc 'split("\n") | map(select(length > 0))' "$FAKE_GH_LOG")" \
314+
'["repos/sourcebot-dev/companion/pulls/23","repos/sourcebot-dev/example/pulls/21"]'
254315
assert_json \
255316
"resolves the configured Linear team identifier" \
256317
"$(jq -c '.variables.teamId' "$FAKE_CURL_PAYLOAD_DIR/1.json")" \
@@ -275,6 +336,12 @@ assert_workflow_contains \
275336
assert_workflow_contains \
276337
"only invokes Claude when discovery found work" \
277338
"if: needs.discover.outputs.has_issues == 'true'"
339+
assert_workflow_contains \
340+
"grants discovery read access to pull request state" \
341+
'pull-requests: read'
342+
assert_workflow_contains \
343+
"authenticates GitHub API requests with the workflow token" \
344+
'GH_TOKEN: ${{ github.token }}'
278345
assert_workflow_contains \
279346
"passes only Linear UUIDs between jobs to avoid secret redaction" \
280347
"issue_ids=\$(jq -c 'map(.id)'"

‎.github/workflows/_cve-remediation.yml‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ on:
1414
type: string
1515
default: ''
1616
max_issues:
17-
description: Maximum number of unlinked CVEs to pass to Claude in one run.
17+
description: Maximum number of CVEs without an open linked PR to pass to Claude in one run.
1818
required: false
1919
type: number
2020
default: 50
@@ -28,10 +28,11 @@ on:
2828

2929
jobs:
3030
discover:
31-
name: Find unlinked CVEs
31+
name: Find CVEs without an open PR
3232
runs-on: ubuntu-latest
3333
permissions:
3434
contents: read
35+
pull-requests: read
3536
outputs:
3637
has_issues: ${{ steps.discover.outputs.has_issues }}
3738
issue_ids: ${{ steps.discover.outputs.issue_ids }}
@@ -49,9 +50,10 @@ jobs:
4950
path: .cve-remediation-workflow
5051
persist-credentials: false
5152

52-
- name: Find open CVEs without a linked PR
53+
- name: Find open CVEs without an open linked PR
5354
id: discover
5455
env:
56+
GH_TOKEN: ${{ github.token }}
5557
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
5658
LINEAR_TEAM_ID: ${{ secrets.LINEAR_TEAM_ID }}
5759
REPOSITORY: ${{ github.repository }}
@@ -82,7 +84,7 @@ jobs:
8284
{
8385
echo "## CVE remediation discovery"
8486
echo
85-
echo "Found **$total_issue_count** open CVE(s) for \`$REPOSITORY\` without a linked GitHub PR."
87+
echo "Found **$total_issue_count** open CVE(s) for \`$REPOSITORY\` without an open linked GitHub PR."
8688
if ((total_issue_count > issue_count)); then
8789
echo "This run will process the first **$issue_count** by Linear priority."
8890
fi

‎.github/workflows/cve-remediation.yml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ on:
77
workflow_dispatch:
88
inputs:
99
max_issues:
10-
description: Maximum number of unlinked CVEs to process.
10+
description: Maximum number of CVEs without an open linked PR to process.
1111
required: false
1212
type: number
1313
default: 50

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL