forked from lifinance/contracts
-
Notifications
You must be signed in to change notification settings - Fork 0
227 lines (188 loc) Β· 10.7 KB
/
securityAlertsReview.yml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
name: Security Alerts Review
# - ensures that all security alerts from olympix static analysis are properly handled before merging.
# - enforces a strict review policy where every alert must be either resolved or dismissed with a justification.
# - prevents merging a PR if any alerts are unresolved or dismissed without a comment.
# - helps maintain a transparent and collaborative security review process by generating a pr comment summarizing the status of all security alerts.
on:
push:
paths:
- 'src/**.sol'
pull_request:
types:
- synchronize
- reopened
- ready_for_review
pull_request_review:
types:
- submitted
workflow_dispatch:
jobs:
check-security-alerts:
runs-on: ubuntu-latest
steps:
# Ensure that the Olympix Static Analysis workflow has run successfully at least once before proceeding.
# This check is necessary because the Security Alerts Review workflow should not proceed unless
# a valid Olympix Static Analysis report is available for the current branch.
- name: Check if Olympix Static Analysis has run at least once and was successful
id: check-analysis
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
BRANCH_NAME: ${{ github.head_ref }}
run: |
echo "Checking latest Olympix Static Analysis run for branch: $BRANCH_NAME"
# Fetch the latest completed runs of the Olympix Static Analysis workflow
LATEST_RUN=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/actions/workflows/olympixStaticAnalysis.yml/runs?status=completed&per_page=10")
echo "LATEST_RUN:"
echo "$LATEST_RUN"
# Filter to find the first run with head_branch matching our branch name
WORKFLOW_STATUS=$(echo "$LATEST_RUN" | jq -r --arg branch "$BRANCH_NAME" '.workflow_runs[] | select(.head_branch == $branch) | .conclusion' | head -n1)
echo "WORKFLOW_STATUS:"
echo "$WORKFLOW_STATUS"
if [[ "$WORKFLOW_STATUS" != "success" ]]; then
echo "The Olympix Static Analysis workflow has not been successfully completed for branch: $BRANCH_NAME."
echo "The Security Alerts Review workflow cannot continue because a valid Olympix Static Analysis report is required."
exit 1
fi
- 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}..."
# Fetch security alerts via GitHub API
ALERTS=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?pr=${PR_NUMBER}")
# Log raw API response for debugging
echo "Raw API Response:"
echo "$ALERTS"
# Extract unresolved alerts (open alerts)
UNRESOLVED_ALERTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "open") ]' || echo "[]")
# Extract dismissed alerts without comments (empty dismissed_comment)
DISMISSED_ALERTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "dismissed" and (.dismissed_comment == null or .dismissed_comment == ""))]' || echo "[]")
# Extract dismissed alerts with comments (successful dismissals)
COMMENTED_ALERTS=$(echo "$ALERTS" | jq -c '[.[] | select(.state == "dismissed" and (.dismissed_comment != null and .dismissed_comment != ""))]' || echo "[]")
UNRESOLVED_COUNT=$(echo "$UNRESOLVED_ALERTS" | jq -r 'length')
DISMISSED_COUNT=$(echo "$DISMISSED_ALERTS" | jq -r 'length')
COMMENTED_COUNT=$(echo "$COMMENTED_ALERTS" | jq -r 'length')
# Output for debugging
echo "UNRESOLVED_ALERTS: $UNRESOLVED_ALERTS"
echo "DISMISSED_ALERTS (without comments): $DISMISSED_ALERTS"
echo "COMMENTED_ALERTS (with comments): $COMMENTED_ALERTS"
echo "UNRESOLVED_COUNT: $UNRESOLVED_COUNT"
echo "DISMISSED_COUNT: $DISMISSED_COUNT"
echo "COMMENTED_COUNT: $COMMENTED_COUNT"
# Save values in the environment as single-line JSON
echo "UNRESOLVED_ALERTS=$UNRESOLVED_ALERTS" >> $GITHUB_ENV
echo "DISMISSED_ALERTS=$DISMISSED_ALERTS" >> $GITHUB_ENV
echo "COMMENTED_ALERTS=$COMMENTED_ALERTS" >> $GITHUB_ENV
echo "UNRESOLVED_COUNT=$UNRESOLVED_COUNT" >> $GITHUB_ENV
echo "DISMISSED_COUNT=$DISMISSED_COUNT" >> $GITHUB_ENV
echo "COMMENTED_COUNT=$COMMENTED_COUNT" >> $GITHUB_ENV
- name: Find Existing PR Comment
id: find_comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Searching for existing PR comment..."
COMMENT_ID=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments" | jq -r \
'.[] | select(.body | startswith("### π€ GitHub Action: Security Alerts Review")) | .id')
if [[ -n "$COMMENT_ID" && "$COMMENT_ID" != "null" ]]; then
echo "EXISTING_COMMENT_ID=$COMMENT_ID" >> $GITHUB_ENV
fi
echo "Found comment ID: $COMMENT_ID"
- name: Post or Update PR Comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
COMMENT_BODY="### π€ GitHub Action: Security Alerts Review π\n\n"
# Add Unresolved Alerts
if [[ "$UNRESOLVED_COUNT" -gt 0 ]]; then
COMMENT_BODY+="π¨ **Unresolved Security Alerts Found!** π¨\n"
COMMENT_BODY+="The following security alerts 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) - **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+=" πΉ $ALERT_DESCRIPTION\n\n"
done < <(echo "$UNRESOLVED_ALERTS" | jq -c '.[]')
fi
# Add Dismissed Alerts Without Comments
if [[ "$DISMISSED_COUNT" -gt 0 ]]; then
COMMENT_BODY+="The following alerts were dismissed but require a dismissal comment:\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) - **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+=" πΉ $ALERT_DESCRIPTION\n\n"
done < <(echo "$DISMISSED_ALERTS" | jq -c '.[]')
fi
if [[ "$UNRESOLVED_COUNT" -gt 0 || "$DISMISSED_COUNT" -gt 0 ]]; then
COMMENT_BODY+="β οΈ **Please resolve the above issues before merging.**\n\n"
fi
# Add Dismissed Alerts With Comments (Successful dismissals)
if [[ "$COMMENTED_COUNT" -gt 0 ]]; then
COMMENT_BODY+="π’ **Dismissed Security Alerts with Comments**\n"
COMMENT_BODY+="The following alerts were dismissed with proper comments:\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')
CAPITALIZED_REASON=$(echo "$DISMISS_REASON" | sed 's/^\(.\)/\U\1/')
COMMENT_BODY+="π’ [View Alert]($ALERT_URL) - **File:** \`$ALERT_FILE\`\n"
COMMENT_BODY+=" πΉ $ALERT_DESCRIPTION\n"
COMMENT_BODY+=" πΉ Dismiss Reason: **$CAPITALIZED_REASON**\n"
COMMENT_BODY+=" πΉ Dismiss Comment: $DISMISS_COMMENT\n\n"
done < <(echo "$COMMENTED_ALERTS" | jq -c '.[]')
fi
# If no unresolved alerts and no dismissed alerts missing comments, add overall success message
if [[ "$UNRESOLVED_COUNT" -eq 0 && "$DISMISSED_COUNT" -eq 0 ]]; then
COMMENT_BODY+="β
**No unresolved security alerts!** π\n\n"
fi
# Update existing comment if found; otherwise, post a new one.
if [[ -n "$EXISTING_COMMENT_ID" ]]; then
echo "Updating existing comment ID: $EXISTING_COMMENT_ID"
curl -s -X PATCH -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
-d "{\"body\": \"$COMMENT_BODY\"}" \
"https://api.github.com/repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}"
else
echo "Posting new comment to PR..."
curl -s -X POST -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
-d "{\"body\": \"$COMMENT_BODY\"}" \
"https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments"
fi
- name: Check if Action Should Fail
run: |
echo "π Checking if the workflow should fail based on security alerts..."
echo "UNRESOLVED_COUNT: $UNRESOLVED_COUNT"
echo "DISMISSED_COUNT: $DISMISSED_COUNT"
# Fail the workflow if there are unresolved alerts.
if [[ "$UNRESOLVED_COUNT" -gt 0 ]]; then
echo "β ERROR: $UNRESOLVED_COUNT unresolved security alerts found!"
echo "β οΈ These alerts must be resolved before merging."
exit 1
fi
# Fail the workflow if there are dismissed alerts without comments.
if [[ "$DISMISSED_COUNT" -gt 0 ]]; then
echo "β ERROR: $DISMISSED_COUNT security alerts were dismissed without comments!"
echo "β οΈ Please provide a dismissal reason for these alerts."
exit 1
fi
echo "β
No blocking security issues found. The workflow will pass successfully."