Skip to content
name: Security Alerts Review
on:
push:
paths:
- "**/*.sol"
- ".github/workflows/securityAlertsReview.yml"
issue_comment:
types:
- created
- edited
pull_request:
types:
- ready_for_review
workflow_dispatch:
jobs:
check-security-alerts:
runs-on: ubuntu-latest
steps:
- name: Fetch PR Number
id: fetch_pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
PR_NUMBER=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/pulls?state=open" | jq -r '.[0].number')
if [[ -z "$PR_NUMBER" || "$PR_NUMBER" == "null" ]]; then
echo "No open PR found, skipping check."
exit 0
fi
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV
echo "PR number: $PR_NUMBER"
- name: Fetch Security Alerts for PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Fetching security alerts for PR #${PR_NUMBER}..."
ALERTS=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?pr=${PR_NUMBER}")
echo "Raw API Response: $ALERTS"
UNRESOLVED_ALERTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "open")]' || echo "[]")
DISMISSED_WITH_COMMENTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "dismissed" and (.dismissed_comment != null and .dismissed_comment != ""))]' || echo "[]")
DISMISSED_WITHOUT_COMMENTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "dismissed" and (.dismissed_comment == null or .dismissed_comment == ""))]' || echo "[]")
UNRESOLVED_COUNT=$(echo "$UNRESOLVED_ALERTS" | jq -r 'length')
DISMISSED_WITH_COMMENTS_COUNT=$(echo "$DISMISSED_WITH_COMMENTS" | jq -r 'length')
DISMISSED_WITHOUT_COMMENTS_COUNT=$(echo "$DISMISSED_WITHOUT_COMMENTS" | jq -r 'length')
echo "UNRESOLVED_COUNT=$UNRESOLVED_COUNT" >> $GITHUB_ENV
echo "DISMISSED_WITH_COMMENTS_COUNT=$DISMISSED_WITH_COMMENTS_COUNT" >> $GITHUB_ENV
echo "DISMISSED_WITHOUT_COMMENTS_COUNT=$DISMISSED_WITHOUT_COMMENTS_COUNT" >> $GITHUB_ENV
# (Optional: Add a step here to set EXISTING_COMMENT_ID if updating an existing comment)
- name: Post or Update PR Comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Build the comment body using $'…' so that \n becomes an actual newline
COMMENT_BODY=$'### 🤖 GitHub Action: Security Alerts Review 🔍\n'
# Unresolved Alerts
if [[ "$UNRESOLVED_COUNT" -gt 0 ]]; then
COMMENT_BODY+=$'\n## 🚨 Unresolved Security Alerts\n'
COMMENT_BODY+="These **must be resolved** before merging:\n\n"
while IFS= read -r row; do
ALERT_URL=$(echo "$row" | jq -r '.html_url')
ALERT_FILE=$(echo "$row" | jq -r '.most_recent_instance.location.path')
ALERT_DESCRIPTION=$(echo "$row" | jq -r '.most_recent_instance.message.text')
COMMENT_BODY+="🔴 **[View Alert]($ALERT_URL)**\n"
COMMENT_BODY+="📌 **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+="💡 **Issue:** $ALERT_DESCRIPTION\n\n"
done < <(echo "$UNRESOLVED_ALERTS" | jq -c '.[]')
COMMENT_BODY+="⚠️ **Please resolve these alerts before merging.**\n\n"
fi
# Dismissed Alerts With Comments (including DISMISS_REASON)
if [[ "$DISMISSED_WITH_COMMENTS_COUNT" -gt 0 ]]; then
COMMENT_BODY+=$'\n## ✅ Dismissed Alerts with Explanations\n'
COMMENT_BODY+="The following alerts were dismissed with valid reasons:\n\n"
while IFS= read -r row; do
ALERT_URL=$(echo "$row" | jq -r '.html_url')
ALERT_FILE=$(echo "$row" | jq -r '.most_recent_instance.location.path')
ALERT_DESCRIPTION=$(echo "$row" | jq -r '.most_recent_instance.message.text')
DISMISS_REASON=$(echo "$row" | jq -r '.dismissed_reason')
DISMISS_COMMENT=$(echo "$row" | jq -r '.dismissed_comment')
# Capitalize the first letter of the dismissal reason
FORMATTED_DISMISS_REASON=$(echo "$DISMISS_REASON" | awk '{print toupper(substr($0,1,1)) substr($0,2)}')
COMMENT_BODY+="🟢 **[View Alert]($ALERT_URL)**\n"
COMMENT_BODY+="📌 **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+="💡 **Issue:** $ALERT_DESCRIPTION\n"
COMMENT_BODY+="✏️ **Dismissal Reason:** \`$FORMATTED_DISMISS_REASON\`\n"
COMMENT_BODY+="💬 **Comment:** \"$DISMISS_COMMENT\"\n\n"
done < <(echo "$DISMISSED_WITH_COMMENTS" | jq -c '.[]')
COMMENT_BODY+="✅ **These alerts were reviewed and dismissed correctly.**\n\n"
fi
# Dismissed Alerts Without Comments
if [[ "$DISMISSED_WITHOUT_COMMENTS_COUNT" -gt 0 ]]; then
COMMENT_BODY+=$'\n## ⚠️ Dismissed Alerts Without Comments\n'
COMMENT_BODY+="The following alerts were dismissed without explanations:\n\n"
while IFS= read -r row; do
ALERT_URL=$(echo "$row" | jq -r '.html_url')
ALERT_FILE=$(echo "$row" | jq -r '.most_recent_instance.location.path')
ALERT_DESCRIPTION=$(echo "$row" | jq -r '.most_recent_instance.message.text')
COMMENT_BODY+="⚠️ **[View Alert]($ALERT_URL)**\n"
COMMENT_BODY+="📌 **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+="💡 **Issue:** $ALERT_DESCRIPTION\n\n"
done < <(echo "$DISMISSED_WITHOUT_COMMENTS" | jq -c '.[]')
COMMENT_BODY+="⚠️ **Please provide a dismissal reason for these alerts.**\n\n"
fi
echo "COMMENT_BODY:"
echo "$COMMENT_BODY"
# Build JSON payload using jq so that newlines are preserved
COMMENT_BODY_JSON=$(jq -n --arg body "$COMMENT_BODY" '{body: $body}')
echo "COMMENT_BODY_JSON:"
echo "$COMMENT_BODY_JSON"
# Determine API URL (if EXISTING_COMMENT_ID is set, update that comment; otherwise, create new)
if [[ -n "$EXISTING_COMMENT_ID" ]]; then
API_URL="https://api.github.com/repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}"
else
API_URL="https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
fi
echo "Using API URL: $API_URL"
# Post or update the PR comment using the JSON payload
HTTP_RESPONSE=$(curl -s -o response.json -w "%{http_code}" -X POST \
-H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
--data "$COMMENT_BODY_JSON" \
"$API_URL")
echo "HTTP_RESPONSE: $HTTP_RESPONSE"
cat response.json
if [[ "$HTTP_RESPONSE" -ne 200 && "$HTTP_RESPONSE" -ne 201 ]]; then
echo "❌ Error: Failed to update PR comment. HTTP Status: $HTTP_RESPONSE"
exit 1
fi