Security Vulnerability Slack Notification #2599
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: Security Vulnerability Slack Notification | |
| on: | |
| schedule: | |
| # Runs every hour at minute 0 | |
| - cron: '0 * * * *' | |
| # Allows you to test manually | |
| workflow_dispatch: | |
| jobs: | |
| check-alerts: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v3 | |
| - name: Check for New Alerts | |
| env: | |
| GH_TOKEN: ${{ secrets.DEPENDABOT_PAT }} | |
| SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK_URL }} | |
| run: | | |
| # 1. Calculate time 65 minutes ago | |
| TIME_THRESHOLD=$(date -u -d '65 minutes ago' +'%Y-%m-%dT%H:%M:%SZ') | |
| echo "Checking for alerts created after: $TIME_THRESHOLD" | |
| # 2. Fetch Open Alerts | |
| RAW_DATA=$(gh api "/repos/${{ github.repository }}/dependabot/alerts?state=open") | |
| # 3. Filter for NEW items only | |
| # Open alerts created > 65 mins ago | |
| ALERTS=$(echo "$RAW_DATA" | jq --arg TIME "$TIME_THRESHOLD" \ | |
| '[ .[] | select(.state == "open") | select(.created_at > $TIME) ]') | |
| # --- FOR TESTING ONLY --- | |
| # ALERTS=$(echo "$RAW_DATA" | jq '[ .[] | select(.state == "open") ]') | |
| # --------------------------------------------------------------- | |
| # 4. Check count | |
| LENGTH=$(echo "$ALERTS" | jq 'length') | |
| if [ "$LENGTH" -eq 0 ]; then | |
| echo "::notice:: No new alerts found in the last hour." | |
| exit 0 | |
| fi | |
| echo "Found $LENGTH new alert(s). Sending notifications..." | |
| REPO_NAME="${{ github.repository }}" | |
| ISSUE_USER="Dependabot" | |
| # 5. LOOP through each alert | |
| echo "$ALERTS" | jq -c '.[]' | while read -r alert; do | |
| # Extract details | |
| SUMMARY=$(echo "$alert" | jq -r '.security_advisory.summary // "Security Vulnerability"') | |
| PACKAGE=$(echo "$alert" | jq -r '.dependency.package.name // "Unknown Package"') | |
| SEVERITY=$(echo "$alert" | jq -r '.security_advisory.severity // "Unknown"') | |
| ISSUE_URL=$(echo "$alert" | jq -r '.html_url // .url // "https://github.com"') | |
| # Format Title | |
| ISSUE_TITLE="${SUMMARY} - ${PACKAGE} (${SEVERITY})" | |
| echo "Sending alert for: $PACKAGE" | |
| # Build Slack Message Text | |
| # FIX: We construct the string INSIDE jq using "\(...)" interpolation. | |
| # FIX: We use -r (raw output) so the variable stores actual newlines, not escaped \n | |
| MESSAGE_TEXT=$(jq -nr \ | |
| --arg repo "$REPO_NAME" \ | |
| --arg title "$ISSUE_TITLE" \ | |
| --arg user "$ISSUE_USER" \ | |
| --arg url "$ISSUE_URL" \ | |
| '"*🚨 New Dependabot Alert (\($repo)) 🚨*\n\n*Issue Title:* \($title)\n*Opened By:* \($user)\n\n*View Issue:* \($url)"') | |
| # Build JSON Payload | |
| # We pass the raw MESSAGE_TEXT into this new jq command, which handles the escaping correctly for JSON. | |
| SLACK_PAYLOAD=$(jq -n \ | |
| --arg text "$MESSAGE_TEXT" \ | |
| '{ | |
| "channel": "#docs-devdocs-notifications", | |
| "username": "Security Vulnerability Slack Notification", | |
| "icon_emoji": ":rotating_light:", | |
| "text": $text | |
| }') | |
| # Send to Slack | |
| curl -s -X POST \ | |
| -H 'Content-type: application/json' \ | |
| --data "$SLACK_PAYLOAD" \ | |
| "$SLACK_WEBHOOK_URL" | |
| sleep 1 | |
| done |