Auto Copilot Autofix (High & Medium Only) #10
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Auto Copilot Autofix (High & Medium Only) | |
| on: | |
| workflow_dispatch: | |
| workflow_run: | |
| workflows: ["CodeQL Advanced"] | |
| types: [completed] | |
| jobs: | |
| auto-fix: | |
| runs-on: ubuntu-latest | |
| permissions: | |
| security-events: read | |
| contents: write | |
| pull-requests: write | |
| steps: | |
| - name: Trigger Autofix for High & Medium alerts | |
| env: | |
| GH_TOKEN: ${{ secrets.AUTOFIX_TOKEN }} | |
| OWNER: ${{ github.repository_owner }} | |
| REPO: ${{ github.event.repository.name }} | |
| run: | | |
| DEFAULT_BRANCH=$(gh api /repos/$OWNER/$REPO --jq '.default_branch') | |
| echo "Default branch: $DEFAULT_BRANCH" | |
| # 获取所有 open alert,用 jq 过滤 security_severity_level 为 high 或 medium 的 | |
| ALERTS=$(gh api \ | |
| "/repos/$OWNER/$REPO/code-scanning/alerts?state=open&per_page=100" \ | |
| --jq '[.[] | select(.rule.security_severity_level == "high" or .rule.security_severity_level == "medium") | {number: .number, level: .rule.security_severity_level}]') | |
| COUNT=$(echo $ALERTS | jq 'length') | |
| echo "Found $COUNT alerts with security_severity_level high or medium" | |
| echo "$ALERTS" | jq -r '.[] | " Alert #\(.number) [\(.level)]"' | |
| if [ "$COUNT" -eq 0 ]; then | |
| echo "No alerts to process, exiting." | |
| exit 0 | |
| fi | |
| for ROW in $(echo $ALERTS | jq -r '.[] | @base64'); do | |
| _jq() { echo "$ROW" | base64 -d | jq -r "$1"; } | |
| NUMBER=$(_jq '.number') | |
| SEC_LEVEL=$(_jq '.level') | |
| echo "--- Alert #$NUMBER [$SEC_LEVEL] ---" | |
| EXISTING=$(gh api \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ | |
| --jq '.status' 2>/dev/null || echo "none") | |
| if [ "$EXISTING" = "success" ]; then | |
| echo "✅ Fix already exists, committing directly..." | |
| else | |
| echo "⏳ Generating fix..." | |
| gh api -X POST \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix || { | |
| echo "⚠️ Failed to trigger autofix for #$NUMBER, skipping" | |
| continue | |
| } | |
| for i in 1 2 3; do | |
| sleep 30 | |
| EXISTING=$(gh api \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ | |
| --jq '.status' 2>/dev/null || echo "none") | |
| echo " Attempt $i: status = $EXISTING" | |
| [ "$EXISTING" = "success" ] && break | |
| done | |
| fi | |
| if [ "$EXISTING" = "success" ]; then | |
| BRANCH="autofix/${SEC_LEVEL}/alert-${NUMBER}" | |
| SHA=$(gh api /repos/$OWNER/$REPO/git/refs/heads/$DEFAULT_BRANCH \ | |
| --jq '.object.sha') | |
| # 创建分支 | |
| gh api -X POST /repos/$OWNER/$REPO/git/refs \ | |
| -f ref="refs/heads/$BRANCH" \ | |
| -f sha="$SHA" 2>/dev/null && \ | |
| echo "🌿 Created branch: $BRANCH" || \ | |
| echo "🌿 Branch already exists: $BRANCH" | |
| # 提交 fix | |
| gh api -X POST \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix/commits \ | |
| -f target_ref="$BRANCH" || { | |
| echo "❌ Failed to commit fix for alert #$NUMBER" | |
| continue | |
| } | |
| echo "✅ Committed fix to branch: $BRANCH" | |
| # 获取 alert 详情 | |
| ALERT_INFO=$(gh api \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER) | |
| ALERT_TITLE=$(echo $ALERT_INFO | jq -r '.rule.description') | |
| ALERT_HELP=$(echo $ALERT_INFO | jq -r '.rule.help // "暂无详细说明"' | head -c 800) | |
| ALERT_TAGS=$(echo $ALERT_INFO | jq -r '.rule.tags // [] | join(", ")') | |
| ALERT_FILE=$(echo $ALERT_INFO | jq -r '.most_recent_instance.location.path // "未知文件"') | |
| ALERT_LINE=$(echo $ALERT_INFO | jq -r '.most_recent_instance.location.start_line // "未知行"') | |
| ALERT_URL=$(echo $ALERT_INFO | jq -r '.html_url') | |
| CWE_TAGS=$(echo $ALERT_INFO | jq -r '[.rule.tags[] | select(startswith("external/cwe/"))] | join(", ")') | |
| # 获取 Autofix AI 修复说明 | |
| AUTOFIX_DESC=$(gh api \ | |
| /repos/$OWNER/$REPO/code-scanning/alerts/$NUMBER/autofix \ | |
| --jq '.description // "暂无 AI 修复说明"') | |
| # 创建 Draft PR | |
| gh pr create \ | |
| --repo "$OWNER/$REPO" \ | |
| --base "$DEFAULT_BRANCH" \ | |
| --head "$BRANCH" \ | |
| --draft \ | |
| --title "[Autofix][$SEC_LEVEL] Alert #$NUMBER: $ALERT_TITLE" \ | |
| --body "## 🤖 Copilot Autofix 自动修复报告 | |
| --- | |
| ### 📋 基本信息 | |
| | 字段 | 内容 | | |
| |------|------| | |
| | **Alert ID** | [#$NUMBER]($ALERT_URL) | | |
| | **安全级别** | $SEC_LEVEL | | |
| | **规则名称** | $ALERT_TITLE | | |
| | **问题文件** | \`$ALERT_FILE\` 第 $ALERT_LINE 行 | | |
| | **CWE 分类** | $CWE_TAGS | | |
| | **规则标签** | $ALERT_TAGS | | |
| --- | |
| ### 🔍 问题说明 | |
| $ALERT_HELP | |
| --- | |
| ### 🤖 AI 修复思路 | |
| $AUTOFIX_DESC | |
| --- | |
| ### ✅ Review 检查清单 | |
| - [ ] 理解了漏洞的成因和影响范围 | |
| - [ ] 确认 AI 修复逻辑正确,没有遗漏边界情况 | |
| - [ ] 确认修复没有改变原有业务逻辑 | |
| - [ ] 确认没有引入新的安全问题 | |
| - [ ] CI / 单元测试全部通过 | |
| - [ ] 如有必要,已补充对应的测试用例 | |
| --- | |
| > 此 PR 由 GitHub Copilot Autofix 自动生成,请仔细审核后再 merge。" && \ | |
| echo "🎉 PR created for alert #$NUMBER" || \ | |
| echo "⚠️ PR already exists for alert #$NUMBER" | |
| else | |
| echo "⚠️ Autofix not available for alert #$NUMBER (status: $EXISTING), skipping" | |
| fi | |
| done |