1212name : security-scan
1313on :
1414 schedule :
15- - cron : ' 0 0 */2 * *'
15+ - cron : " 0 0 */2 * *"
1616 workflow_dispatch :
1717
1818jobs :
2727 - name : Setup Node.js
2828 uses : actions/setup-node@v4
2929 with :
30- node-version-file : ' openmetadata-ui/src/main/resources/ui/.nvmrc'
30+ node-version-file : " openmetadata-ui/src/main/resources/ui/.nvmrc"
3131
3232 - name : Enable yarn
3333 run : corepack enable
@@ -124,30 +124,6 @@ jobs:
124124 print()
125125 EOF
126126
127- - name : Slack on Failure
128- if : steps.retire-scan.outcome == 'failure'
129- uses : slackapi/slack-github-action@v1.23.0
130- with :
131- channel-id : ${{ secrets.SLACK_CHANNEL_IDS }}
132- payload : |
133- {
134- "text": "🚨 Vulnerability scan failed on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>. 🚨"
135- }
136- env :
137- SLACK_BOT_TOKEN : ${{ secrets.SLACK_BOT_TOKEN }}
138-
139- - name : Slack on Success
140- if : steps.retire-scan.outcome == 'success'
141- uses : slackapi/slack-github-action@v1.23.0
142- with :
143- channel-id : ${{ secrets.SLACK_CHANNEL_IDS }}
144- payload : |
145- {
146- "text": "🟢 Vulnerability scan passed for OpenMetadata Repo on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>."
147- }
148- env :
149- SLACK_BOT_TOKEN : ${{ secrets.SLACK_BOT_TOKEN }}
150-
151127 - name : Force failure on vulnerabilities found
152128 if : steps.retire-scan.outcome == 'failure'
153129 run : exit 1
@@ -163,25 +139,25 @@ jobs:
163139 - name : Free Disk Space (Ubuntu)
164140 uses : jlumbroso/free-disk-space@main
165141 with :
166- tool-cache : false
167- android : true
168- dotnet : true
169- haskell : true
170- large-packages : false
171- docker-images : true
172- swap-storage : true
142+ tool-cache : false
143+ android : true
144+ dotnet : true
145+ haskell : true
146+ large-packages : false
147+ docker-images : true
148+ swap-storage : true
173149 - uses : actions/checkout@v4
174150
175151 - name : Set up Python 3.10
176152 uses : actions/setup-python@v5
177153 with :
178- python-version : ' 3.10'
154+ python-version : " 3.10"
179155
180156 - name : Set up JDK 21
181157 uses : actions/setup-java@v4
182158 with :
183- java-version : ' 21 '
184- distribution : ' temurin'
159+ java-version : " 21 "
160+ distribution : " temurin"
185161
186162 - name : Install Ubuntu dependencies
187163 run : |
@@ -215,40 +191,111 @@ jobs:
215191 continue-on-error : true
216192 run : |
217193 source env/bin/activate
218- make snyk-report
219-
220- - name : Slack on Failure
221- if : steps.security-report.outcome != 'success'
222- uses : slackapi/slack-github-action@v1.23.0
223- with :
224- channel-id : ${{ secrets.SLACK_CHANNEL_IDS }}
225- payload : |
226- {
227- "text": "🚨 Security report failed on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>. 🚨"
228- }
229- env :
230- SLACK_BOT_TOKEN : ${{ secrets.SLACK_BOT_TOKEN }}
231-
232- - name : Slack on Success
233- if : steps.security-report.outcome == 'success'
234- uses : slackapi/slack-github-action@v1.23.0
235- with :
236- channel-id : ${{ secrets.SLACK_CHANNEL_IDS }}
237- payload : |
238- {
239- "text": "🟢 Security report generated for OpenMetadata Repo on branch `${{ github.ref_name }}`, please check it <https://github.com/open-metadata/OpenMetadata/actions/runs/${{ github.run_id }}|here>."
240- }
241- env :
242- SLACK_BOT_TOKEN : ${{ secrets.SLACK_BOT_TOKEN }}
194+ rm -rf security-report
195+ mkdir -p security-report
196+ # Run snyk subtargets directly; skip `export-snyk-pdf-report` which deletes JSONs after PDF conversion.
197+ make snyk-ingestion-report || true
198+ make snyk-ingestion-base-slim-report || true
199+ make snyk-airflow-apis-report || true
200+ make snyk-server-report || true
201+ make snyk-ui-report || true
202+
203+ - name : Publish Snyk Summary
204+ id : snyk-summary
205+ if : always() && steps.maven-build.outcome == 'success'
206+ run : |
207+ python3 scripts/snyk_summary.py security-report \
208+ --counts-file security-report/_counts.json \
209+ --slack-file security-report/_slack.txt \
210+ >> $GITHUB_STEP_SUMMARY
211+ # Expose counts as step output for downstream gating.
212+ counts=$(cat security-report/_counts.json)
213+ echo "counts=$counts" >> $GITHUB_OUTPUT
214+ high=$(jq '.high + .critical' security-report/_counts.json)
215+ echo "high_critical=$high" >> $GITHUB_OUTPUT
216+
217+ - name : Fail on high/critical Snyk findings
218+ if : always() && steps.snyk-summary.outputs.high_critical != '' && steps.snyk-summary.outputs.high_critical != '0'
219+ run : |
220+ echo "::error::Snyk found ${{ steps.snyk-summary.outputs.high_critical }} high/critical vulnerabilities (see Job Summary)"
221+ exit 1
243222
244- - name : Upload Snyk Report HTML files
245- if : steps.security-report.outcome == 'success'
223+ - name : Generate Snyk HTML/PDF
224+ if : always() && steps.maven-build.outcome == 'success'
225+ run : |
226+ # Back up JSONs because html_to_pdf.py deletes them after PDF conversion.
227+ mkdir -p /tmp/snyk-json-backup
228+ cp security-report/*.json /tmp/snyk-json-backup/ 2>/dev/null || true
229+ make export-snyk-pdf-report || true
230+ # Restore JSONs alongside generated PDFs/HTMLs.
231+ cp /tmp/snyk-json-backup/*.json security-report/ 2>/dev/null || true
232+
233+ - name : Upload Snyk Reports
234+ if : always() && steps.maven-build.outcome == 'success'
246235 uses : actions/upload-artifact@v4
247236 with :
248237 name : security-report
249238 path : security-report
239+ retention-days : 30
250240
251241 - name : Force failure
252242 if : steps.maven-build.outcome != 'success' || steps.security-report.outcome != 'success'
253243 run : |
254244 exit 1
245+
246+ notify :
247+ runs-on : ubuntu-latest
248+ environment : security-scan
249+ needs : [vulnerability-scan, security-scan]
250+ if : always()
251+ steps :
252+ - name : Download Snyk artifact
253+ if : needs.security-scan.result != 'skipped'
254+ uses : actions/download-artifact@v4
255+ with :
256+ name : security-report
257+ path : security-report
258+ continue-on-error : true
259+
260+ - name : Build Slack payload
261+ id : build
262+ run : |
263+ retire="${{ needs.vulnerability-scan.result }}"
264+ snyk="${{ needs.security-scan.result }}"
265+ status_icon() {
266+ case "$1" in
267+ success) echo "✅" ;;
268+ cancelled) echo "⚠️ (cancelled)" ;;
269+ skipped) echo "⚠️ (skipped)" ;;
270+ *) echo "❌" ;;
271+ esac
272+ }
273+ retire_icon=$(status_icon "$retire")
274+ snyk_icon=$(status_icon "$snyk")
275+ if [ "$retire" = "success" ] && [ "$snyk" = "success" ]; then
276+ icon="🟢"
277+ elif [ "$retire" = "failure" ] || [ "$snyk" = "failure" ]; then
278+ icon="🚨"
279+ else
280+ icon="⚠️"
281+ fi
282+ run_url="https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}"
283+ {
284+ echo "$icon *Security scan* — *OpenMetadata Repo* on branch \`${{ github.ref_name }}\`"
285+ echo "• Vulnerability scan (Retire.js): $retire_icon"
286+ echo "• Security scan (Snyk): $snyk_icon"
287+ echo "<$run_url|Open run details>"
288+ if [ -f security-report/_slack.txt ]; then
289+ echo
290+ cat security-report/_slack.txt
291+ fi
292+ } > slack_body.txt
293+ jq -Rs '{text: ., mrkdwn: true}' slack_body.txt > payload.json
294+
295+ - name : Send Slack Notification
296+ uses : slackapi/slack-github-action@v1.27.1
297+ with :
298+ channel-id : ${{ secrets.SLACK_CHANNEL_IDS }}
299+ payload-file-path : payload.json
300+ env :
301+ SLACK_BOT_TOKEN : ${{ secrets.SLACK_BOT_TOKEN }}
0 commit comments