Skip to content

Commit 7ad8c5a

Browse files
committed
Merge remote-tracking branch 'origin/main' into linear/sou-1551-sourcebot-devsourcebot-cve-2026-59869-js-yaml-js-yaml-5e0b
# Conflicts: # CHANGELOG.md
2 parents 34dfd2a + 906af93 commit 7ad8c5a

33 files changed

Lines changed: 2005 additions & 244 deletions

.github/workflows/vulnerability-triage.yml

Lines changed: 84 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,31 @@ on:
4343
required: false
4444
type: boolean
4545
default: false
46+
linear_assignee_id:
47+
description: 'Linear user UUID to assign. Leave empty to use assign_to_api_key_owner behavior.'
48+
required: false
49+
type: string
50+
default: ''
51+
assign_to_api_key_owner:
52+
description: 'Assign issues to the Linear API key owner when linear_assignee_id is empty.'
53+
required: false
54+
type: boolean
55+
default: true
56+
linear_state_name:
57+
description: 'Linear workflow state for newly created or reopened issues.'
58+
required: false
59+
type: string
60+
default: 'Triage'
61+
linear_cycle_id:
62+
description: 'Linear cycle UUID to assign. Takes precedence over linear_use_active_cycle.'
63+
required: false
64+
type: string
65+
default: ''
66+
linear_use_active_cycle:
67+
description: 'Assign issues to the Linear team active cycle when linear_cycle_id is empty.'
68+
required: false
69+
type: boolean
70+
default: false
4671
secrets:
4772
LINEAR_API_KEY:
4873
required: true
@@ -58,6 +83,7 @@ permissions:
5883
contents: read
5984
packages: read
6085
security-events: read # Required for CodeQL alerts API
86+
vulnerability-alerts: read # Required for Dependabot alerts API
6187
id-token: write # Required for OIDC authentication
6288

6389
jobs:
@@ -133,7 +159,7 @@ jobs:
133159
id: check
134160
env:
135161
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
136-
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT }}
162+
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT || secrets.GITHUB_TOKEN }}
137163
run: |
138164
HAS_ALERTS=false
139165
@@ -184,7 +210,7 @@ jobs:
184210
- name: Write alerts summary
185211
env:
186212
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
187-
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT }}
213+
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT || secrets.GITHUB_TOKEN }}
188214
run: |
189215
echo "## Dependabot & CodeQL Alert Check" >> "$GITHUB_STEP_SUMMARY"
190216
echo "" >> "$GITHUB_STEP_SUMMARY"
@@ -252,8 +278,6 @@ jobs:
252278
steps:
253279
- name: Checkout repository
254280
uses: actions/checkout@v4
255-
with:
256-
submodules: recursive
257281

258282
- name: Download scan results
259283
if: needs.scan.outputs.has_vulnerabilities == 'true'
@@ -279,7 +303,7 @@ jobs:
279303
280304
- name: Fetch Dependabot alerts
281305
env:
282-
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT }}
306+
DEPENDABOT_PAT: ${{ secrets.DEPENDABOT_PAT || secrets.GITHUB_TOKEN }}
283307
run: |
284308
if [ -z "$DEPENDABOT_PAT" ]; then
285309
echo "::warning::DEPENDABOT_PAT not configured. Writing empty Dependabot alerts."
@@ -541,13 +565,17 @@ jobs:
541565
LINEAR_API_KEY: ${{ secrets.LINEAR_API_KEY }}
542566
LINEAR_TEAM_ID: ${{ secrets.LINEAR_TEAM_ID }}
543567
REPOSITORY: ${{ github.repository }}
568+
LINEAR_STATE_NAME: ${{ inputs.linear_state_name || 'Triage' }}
544569
run: |
545570
set -euo pipefail
546-
# Resolve team UUID + the "CVE" label, "Triage" state, and API key owner once,
547-
# and expose them as outputs so the issue-creation step can reuse them.
548-
METADATA_QUERY='query($teamId: String!) { team(id: $teamId) { id labels(filter: { name: { eq: "CVE" } }) { nodes { id } } states(filter: { name: { eq: "Triage" } }) { nodes { id } } } viewer { id } }'
549-
METADATA_PAYLOAD=$(jq -n --arg query "$METADATA_QUERY" --arg teamId "$LINEAR_TEAM_ID" \
550-
'{query: $query, variables: {teamId: $teamId}}')
571+
# Resolve team UUID, labels, configured state, active cycle, and API key
572+
# owner once, then expose them for issue creation and reopening.
573+
METADATA_QUERY='query($teamId: String!, $stateName: String!) { team(id: $teamId) { id labels(filter: { name: { eq: "CVE" } }) { nodes { id } } states(filter: { name: { eq: $stateName } }) { nodes { id } } activeCycle { id } } viewer { id } }'
574+
METADATA_PAYLOAD=$(jq -n \
575+
--arg query "$METADATA_QUERY" \
576+
--arg teamId "$LINEAR_TEAM_ID" \
577+
--arg stateName "$LINEAR_STATE_NAME" \
578+
'{query: $query, variables: {teamId: $teamId, stateName: $stateName}}')
551579
METADATA_RESPONSE=$(curl -s -X POST https://api.linear.app/graphql \
552580
-H "Content-Type: application/json" \
553581
-H "Authorization: $LINEAR_API_KEY" \
@@ -556,6 +584,7 @@ jobs:
556584
TEAM_UUID=$(echo "$METADATA_RESPONSE" | jq -r '.data.team.id // empty')
557585
LABEL_ID=$(echo "$METADATA_RESPONSE" | jq -r '.data.team.labels.nodes[0].id // empty')
558586
STATE_ID=$(echo "$METADATA_RESPONSE" | jq -r '.data.team.states.nodes[0].id // empty')
587+
ACTIVE_CYCLE_ID=$(echo "$METADATA_RESPONSE" | jq -r '.data.team.activeCycle.id // empty')
559588
VIEWER_ID=$(echo "$METADATA_RESPONSE" | jq -r '.data.viewer.id // empty')
560589
561590
if [ -z "$TEAM_UUID" ]; then
@@ -594,6 +623,7 @@ jobs:
594623
echo "label_id=$LABEL_ID"
595624
echo "repo_label_id=$REPO_LABEL_ID"
596625
echo "state_id=$STATE_ID"
626+
echo "active_cycle_id=$ACTIVE_CYCLE_ID"
597627
echo "viewer_id=$VIEWER_ID"
598628
} >> "$GITHUB_OUTPUT"
599629
@@ -673,25 +703,42 @@ jobs:
673703
LABEL_ID: ${{ steps.match.outputs.label_id }}
674704
REPO_LABEL_ID: ${{ steps.match.outputs.repo_label_id }}
675705
STATE_ID: ${{ steps.match.outputs.state_id }}
706+
ACTIVE_CYCLE_ID: ${{ steps.match.outputs.active_cycle_id }}
676707
VIEWER_ID: ${{ steps.match.outputs.viewer_id }}
708+
CONFIGURED_ASSIGNEE_ID: ${{ inputs.linear_assignee_id }}
709+
ASSIGN_TO_API_KEY_OWNER: ${{ inputs.assign_to_api_key_owner }}
710+
LINEAR_STATE_NAME: ${{ inputs.linear_state_name || 'Triage' }}
711+
CONFIGURED_CYCLE_ID: ${{ inputs.linear_cycle_id }}
712+
USE_ACTIVE_CYCLE: ${{ inputs.linear_use_active_cycle }}
677713
run: |
678714
set -uo pipefail
679-
# Team UUID, the "CVE" + repository labels, "Triage" state, and the API key
680-
# owner's user ID were already resolved by the "Match existing Linear issues"
681-
# step and passed in as env.
715+
# Team UUID, labels, state, cycle, and assignee metadata were already
716+
# resolved by the "Match existing Linear issues" step.
682717
STRUCTURED_OUTPUT=$(cat findings.json)
683718
719+
TARGET_ASSIGNEE_ID="$CONFIGURED_ASSIGNEE_ID"
720+
if [ -z "$TARGET_ASSIGNEE_ID" ] && [ "$ASSIGN_TO_API_KEY_OWNER" = "true" ]; then
721+
TARGET_ASSIGNEE_ID="$VIEWER_ID"
722+
fi
723+
TARGET_CYCLE_ID="$CONFIGURED_CYCLE_ID"
724+
if [ -z "$TARGET_CYCLE_ID" ] && [ "$USE_ACTIVE_CYCLE" = "true" ]; then
725+
TARGET_CYCLE_ID="$ACTIVE_CYCLE_ID"
726+
fi
727+
684728
if [ -z "$LABEL_ID" ]; then
685729
echo "::warning::Could not find 'CVE' label in Linear team. Creating issues without label."
686730
fi
687731
if [ -z "$REPO_LABEL_ID" ]; then
688732
echo "::warning::Could not resolve '$REPOSITORY' repository label. Creating issues without it."
689733
fi
690734
if [ -z "$STATE_ID" ]; then
691-
echo "::warning::Could not find 'Triage' state in Linear team. Using default state."
735+
echo "::warning::Could not find '$LINEAR_STATE_NAME' state in Linear team. Using default state."
736+
fi
737+
if [ -z "$TARGET_ASSIGNEE_ID" ]; then
738+
echo "Issues will be created or reopened unassigned."
692739
fi
693-
if [ -z "$VIEWER_ID" ]; then
694-
echo "::warning::Could not resolve Linear API key owner. Issues will be created unassigned."
740+
if [ "$USE_ACTIVE_CYCLE" = "true" ] && [ -z "$TARGET_CYCLE_ID" ]; then
741+
echo "::warning::Could not resolve an active Linear cycle. Issues will be created without a cycle."
695742
fi
696743
697744
# Map severity to Linear priority
@@ -716,7 +763,7 @@ jobs:
716763
# Write CVEs to temp file so the while loop doesn't run in a pipe subshell
717764
echo "$STRUCTURED_OUTPUT" | jq -c '.cves[]' > /tmp/cves.jsonl
718765
719-
MUTATION='mutation CreateIssue($teamId: String!, $title: String!, $description: String, $priority: Int, $labelIds: [String!], $stateId: String, $assigneeId: String) { issueCreate(input: { teamId: $teamId, title: $title, description: $description, priority: $priority, labelIds: $labelIds, stateId: $stateId, assigneeId: $assigneeId }) { success issue { id identifier url } } }'
766+
MUTATION='mutation CreateIssue($teamId: String!, $title: String!, $description: String, $priority: Int, $labelIds: [String!], $stateId: String, $assigneeId: String, $cycleId: String) { issueCreate(input: { teamId: $teamId, title: $title, description: $description, priority: $priority, labelIds: $labelIds, stateId: $stateId, assigneeId: $assigneeId, cycleId: $cycleId }) { success issue { id identifier url } } }'
720767
721768
while IFS= read -r cve; do
722769
CVE_ID=$(echo "$cve" | jq -r '.cveId')
@@ -737,22 +784,27 @@ jobs:
737784
fi
738785
739786
if [ "$LINEAR_EXISTS" = "true" ] && [ "$LINEAR_CLOSED" = "true" ]; then
740-
# Reopen the closed issue by setting its state back to Triage
787+
# Reopen the closed issue using the configured repository routing.
741788
echo "Found closed Linear issue $LINEAR_IDENTIFIER for $CVE_ID ($LINEAR_URL) — will attempt to reopen"
742789
if [ -z "$STATE_ID" ]; then
743-
echo "::warning::Cannot reopen $CVE_ID ($LINEAR_IDENTIFIER) — no Triage state found. Skipping."
744-
echo "- Skipped **$CVE_ID** — found closed issue [$LINEAR_IDENTIFIER]($LINEAR_URL) but no Triage state to reopen" >> "$GITHUB_STEP_SUMMARY"
790+
echo "::warning::Cannot reopen $CVE_ID ($LINEAR_IDENTIFIER) — no '$LINEAR_STATE_NAME' state found. Skipping."
791+
echo "- Skipped **$CVE_ID** — found closed issue [$LINEAR_IDENTIFIER]($LINEAR_URL) but no '$LINEAR_STATE_NAME' state to reopen" >> "$GITHUB_STEP_SUMMARY"
745792
SKIPPED_COUNT=$((SKIPPED_COUNT + 1))
746793
continue
747794
fi
748795
749-
REOPEN_MUTATION='mutation($issueId: String!, $stateId: String!, $assigneeId: String) { issueUpdate(id: $issueId, input: { stateId: $stateId, assigneeId: $assigneeId }) { success issue { id identifier url } } }'
796+
REOPEN_MUTATION='mutation($issueId: String!, $stateId: String!, $assigneeId: String, $cycleId: String) { issueUpdate(id: $issueId, input: { stateId: $stateId, assigneeId: $assigneeId, cycleId: $cycleId }) { success issue { id identifier url } } }'
750797
REOPEN_VARIABLES=$(jq -n \
751798
--arg issueId "$LINEAR_ISSUE_ID" \
752799
--arg stateId "$STATE_ID" \
753800
'{issueId: $issueId, stateId: $stateId}')
754-
if [ -n "$VIEWER_ID" ]; then
755-
REOPEN_VARIABLES=$(echo "$REOPEN_VARIABLES" | jq --arg aid "$VIEWER_ID" '. + {assigneeId: $aid}')
801+
if [ -n "$TARGET_ASSIGNEE_ID" ]; then
802+
REOPEN_VARIABLES=$(echo "$REOPEN_VARIABLES" | jq --arg aid "$TARGET_ASSIGNEE_ID" '. + {assigneeId: $aid}')
803+
else
804+
REOPEN_VARIABLES=$(echo "$REOPEN_VARIABLES" | jq '. + {assigneeId: null}')
805+
fi
806+
if [ -n "$TARGET_CYCLE_ID" ]; then
807+
REOPEN_VARIABLES=$(echo "$REOPEN_VARIABLES" | jq --arg cid "$TARGET_CYCLE_ID" '. + {cycleId: $cid}')
756808
fi
757809
REOPEN_PAYLOAD=$(jq -n --arg query "$REOPEN_MUTATION" --argjson vars "$REOPEN_VARIABLES" '{query: $query, variables: $vars}')
758810
@@ -765,7 +817,7 @@ jobs:
765817
REOPEN_IDENTIFIER=$(echo "$REOPEN_RESPONSE" | jq -r '.data.issueUpdate.issue.identifier // empty')
766818
if [ -n "$REOPEN_URL" ]; then
767819
echo "Reopened Linear issue $REOPEN_IDENTIFIER for $CVE_ID: $REOPEN_URL"
768-
echo "- Reopened [$REOPEN_IDENTIFIER]($REOPEN_URL) for **$CVE_ID** — $TITLE (moved back to Triage)" >> "$GITHUB_STEP_SUMMARY"
820+
echo "- Reopened [$REOPEN_IDENTIFIER]($REOPEN_URL) for **$CVE_ID** — $TITLE (moved to $LINEAR_STATE_NAME)" >> "$GITHUB_STEP_SUMMARY"
769821
REOPENED_COUNT=$((REOPENED_COUNT + 1))
770822
else
771823
echo "::error::Failed to reopen Linear issue $LINEAR_IDENTIFIER for $CVE_ID"
@@ -796,8 +848,13 @@ jobs:
796848
if [ -n "$STATE_ID" ]; then
797849
VARIABLES=$(echo "$VARIABLES" | jq --arg sid "$STATE_ID" '. + {stateId: $sid}')
798850
fi
799-
if [ -n "$VIEWER_ID" ]; then
800-
VARIABLES=$(echo "$VARIABLES" | jq --arg aid "$VIEWER_ID" '. + {assigneeId: $aid}')
851+
if [ -n "$TARGET_ASSIGNEE_ID" ]; then
852+
VARIABLES=$(echo "$VARIABLES" | jq --arg aid "$TARGET_ASSIGNEE_ID" '. + {assigneeId: $aid}')
853+
else
854+
VARIABLES=$(echo "$VARIABLES" | jq '. + {assigneeId: null}')
855+
fi
856+
if [ -n "$TARGET_CYCLE_ID" ]; then
857+
VARIABLES=$(echo "$VARIABLES" | jq --arg cid "$TARGET_CYCLE_ID" '. + {cycleId: $cid}')
801858
fi
802859
803860
PAYLOAD=$(jq -n --arg query "$MUTATION" --argjson vars "$VARIABLES" '{query: $query, variables: $vars}')
@@ -827,4 +884,4 @@ jobs:
827884
if [ "$FAILED_COUNT" -gt 0 ]; then
828885
echo "::error::Failed to create $FAILED_COUNT Linear issue(s)"
829886
exit 1
830-
fi
887+
fi

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,17 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Fixed
11+
- Upgraded `tar` to `^7.5.20`. [#1474](https://github.com/sourcebot-dev/sourcebot/pull/1474)
12+
- [EE] Preserved cached repository permissions when OAuth token refresh fails because of a transient code host outage. [#1481](https://github.com/sourcebot-dev/sourcebot/pull/1481)
13+
- [EE] Classified code host permission sync failures by provider context before clearing cached repository permissions. [#1482](https://github.com/sourcebot-dev/sourcebot/pull/1482)
14+
- [EE] Added action-required warnings and guided recovery when permission syncing clears cached repository access. [#1484](https://github.com/sourcebot-dev/sourcebot/pull/1484)
15+
- Upgraded `brace-expansion` to `^1.1.16`/`^2.1.2`/`^5.0.7` to address CVE-2026-13149. [#1471](https://github.com/sourcebot-dev/sourcebot/pull/1471)
16+
- Upgraded `shell-quote` to `^1.10.0`. [#1469](https://github.com/sourcebot-dev/sourcebot/pull/1469)
1117
- Upgraded `js-yaml` to `^4.3.0`. [#1470](https://github.com/sourcebot-dev/sourcebot/pull/1470)
1218

19+
### Changed
20+
- Reduced Sentry span sampling to 10% outside development. [#1475](https://github.com/sourcebot-dev/sourcebot/pull/1475)
21+
1322
## [5.1.3] - 2026-07-20
1423

1524
### Added

0 commit comments

Comments
 (0)