Skip to content

Commit 03f9893

Browse files
fix(ci): match existing Linear issues so triage stops creating duplicates
The Linear search bound the team UUID as a `String!` GraphQL variable into `team.id.eq`, which expects `ID`. That variable-type mismatch made every search return null data, so no finding ever matched an existing issue and the job re-filed every CVE on each run. Drop the team filter (repo-prefix title scoping is the authoritative filter anyway) and warn when a search errors. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent a685d01 commit 03f9893

1 file changed

Lines changed: 13 additions & 4 deletions

File tree

.github/workflows/vulnerability-triage.yml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -568,22 +568,31 @@ jobs:
568568
} >> "$GITHUB_OUTPUT"
569569
570570
# For each finding, search Linear for an existing issue whose title contains the
571-
# finding id AND is scoped to this repo (title starts with "[<repository>]").
572-
# Prefer an open issue over a closed one.
573-
SEARCH_QUERY='query($teamId: String!, $text: String!) { issues(filter: { team: { id: { eq: $teamId } }, title: { contains: $text } }) { nodes { id identifier url title state { type } } } }'
571+
# finding id, then scope to this repo via the "[<repository>]" title prefix and
572+
# prefer an open issue over a closed one. The team is intentionally NOT part of
573+
# the GraphQL filter: its `id.eq` expects an `ID`, and binding our `String`
574+
# team-UUID variable there is a type error that makes every search return null.
575+
# Repo-prefix scoping below is the authoritative filter anyway.
576+
SEARCH_QUERY='query($text: String!) { issues(filter: { title: { contains: $text } }) { nodes { id identifier url title state { type } } } }'
574577
575578
echo '[]' > /tmp/matched.json
576579
jq -c '.cves[]' findings-base.json > /tmp/findings.jsonl
577580
578581
while IFS= read -r finding; do
579582
CVE_ID=$(echo "$finding" | jq -r '.cveId')
580-
VARS=$(jq -n --arg teamId "$TEAM_UUID" --arg text "$CVE_ID" '{teamId: $teamId, text: $text}')
583+
VARS=$(jq -n --arg text "$CVE_ID" '{text: $text}')
581584
PAYLOAD=$(jq -n --arg query "$SEARCH_QUERY" --argjson vars "$VARS" '{query: $query, variables: $vars}')
582585
RESPONSE=$(curl -s -X POST https://api.linear.app/graphql \
583586
-H "Content-Type: application/json" \
584587
-H "Authorization: $LINEAR_API_KEY" \
585588
-d "$PAYLOAD")
586589
590+
# Surface query failures instead of silently treating them as "no match"
591+
# (which would create a duplicate issue).
592+
if [ "$(echo "$RESPONSE" | jq 'has("errors") or (.data.issues == null)')" = "true" ]; then
593+
echo "::warning::Linear search for $CVE_ID failed: $(echo "$RESPONSE" | jq -c '.errors // .')"
594+
fi
595+
587596
SELECTED=$(echo "$RESPONSE" | jq --arg prefix "[$REPOSITORY]" '
588597
[.data.issues.nodes[]? | select(.title | startswith($prefix))] as $matches |
589598
($matches | map(select(.state.type != "completed" and .state.type != "canceled")) | .[0]) as $open |

0 commit comments

Comments
 (0)