Skip to content

Commit c561c45

Browse files
Merge branch 'fix/snyk-medium-vulnerabilities' of https://github.com/auth0/auth0-ui-components into fix/snyk-medium-vulnerabilities
2 parents b604cf2 + 2edcf13 commit c561c45

1 file changed

Lines changed: 237 additions & 0 deletions

File tree

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
name: Scheduled Snyk Security Scan
2+
3+
on:
4+
schedule:
5+
# Runs every Monday at 9:00 AM UTC
6+
- cron: '0 9 * * 1'
7+
workflow_dispatch: # Allow manual trigger
8+
9+
permissions:
10+
contents: read
11+
12+
jobs:
13+
snyk-scan:
14+
name: Snyk Vulnerability Scan
15+
runs-on:
16+
labels: ubuntu-latest
17+
steps:
18+
- name: Checkout repository
19+
uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
20+
21+
- name: Setup pnpm
22+
uses: pnpm/action-setup@36de12bed180fa130ed56a35e7344f2fa7a820ab # v4
23+
24+
- name: Setup Node.js
25+
uses: actions/setup-node@0a44ba7841725637a19e28fa30b79a866c81b0a6 # v4.0.4
26+
with:
27+
node-version: 18
28+
cache: pnpm
29+
30+
- name: Install dependencies
31+
run: pnpm install --frozen-lockfile
32+
33+
- name: Run Snyk Open Source scan
34+
id: snyk_oss
35+
continue-on-error: true
36+
env:
37+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
38+
run: |
39+
npx snyk test --all-projects --severity-threshold=low --json > /tmp/snyk_oss_report.json 2>&1 || true
40+
41+
- name: Run Snyk Code scan (SAST)
42+
id: snyk_code
43+
continue-on-error: true
44+
env:
45+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
46+
run: |
47+
npx snyk code test --severity-threshold=low --json > /tmp/snyk_code_report.json 2>&1 || true
48+
49+
- name: Parse results and notify Slack
50+
if: always()
51+
env:
52+
SLACK_WEBHOOK: ${{ secrets.SLACK_CI_WORKFLOW_WEBHOOK }}
53+
GITHUB_REPOSITORY: ${{ github.repository }}
54+
GITHUB_SERVER_URL: ${{ github.server_url }}
55+
GITHUB_RUN_ID: ${{ github.run_id }}
56+
run: |
57+
ACTION_URL="${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID}"
58+
59+
# ── Parse OSS (SCA) results ──
60+
OSS_SUMMARY="No issues found"
61+
OSS_VULNS=""
62+
if [ -f /tmp/snyk_oss_report.json ] && jq -e . /tmp/snyk_oss_report.json > /dev/null 2>&1; then
63+
# Handle both single object and array responses
64+
OSS_TOTAL=$(jq '[if type == "array" then .[] else . end | .vulnerabilities // [] | length] | add // 0' /tmp/snyk_oss_report.json)
65+
OSS_CRITICAL=$(jq '[if type == "array" then .[] else . end | .vulnerabilities // [] | .[] | select(.severity == "critical")] | length' /tmp/snyk_oss_report.json)
66+
OSS_HIGH=$(jq '[if type == "array" then .[] else . end | .vulnerabilities // [] | .[] | select(.severity == "high")] | length' /tmp/snyk_oss_report.json)
67+
OSS_MEDIUM=$(jq '[if type == "array" then .[] else . end | .vulnerabilities // [] | .[] | select(.severity == "medium")] | length' /tmp/snyk_oss_report.json)
68+
OSS_LOW=$(jq '[if type == "array" then .[] else . end | .vulnerabilities // [] | .[] | select(.severity == "low")] | length' /tmp/snyk_oss_report.json)
69+
70+
if [ "$OSS_TOTAL" -gt 0 ]; then
71+
OSS_SUMMARY="${OSS_TOTAL} vulnerabilities found"
72+
OSS_VULNS="🔴 Critical: ${OSS_CRITICAL} | 🟠 High: ${OSS_HIGH} | 🟡 Medium: ${OSS_MEDIUM} | 🔵 Low: ${OSS_LOW}"
73+
74+
# Extract top 10 unique vulnerability details
75+
OSS_DETAILS=$(jq -r '
76+
[if type == "array" then .[] else . end | .vulnerabilities // [] | .[]]
77+
| sort_by(if .severity == "critical" then 0 elif .severity == "high" then 1 elif .severity == "medium" then 2 else 3 end)
78+
| unique_by(.id)
79+
| .[:10]
80+
| .[]
81+
| "• *\(.severity | ascii_upcase)*: \(.title) (\(.packageName)@\(.version // "n/a")) — \(.id)"
82+
' /tmp/snyk_oss_report.json 2>/dev/null || echo "")
83+
fi
84+
fi
85+
86+
# ── Parse Code (SAST) results ──
87+
CODE_SUMMARY="No issues found"
88+
CODE_VULNS=""
89+
if [ -f /tmp/snyk_code_report.json ] && jq -e . /tmp/snyk_code_report.json > /dev/null 2>&1; then
90+
CODE_TOTAL=$(jq '[.runs // [] | .[].results // [] | .[]] | length' /tmp/snyk_code_report.json 2>/dev/null || echo "0")
91+
CODE_HIGH=$(jq '[.runs // [] | .[].results // [] | .[] | select(.level == "error")] | length' /tmp/snyk_code_report.json 2>/dev/null || echo "0")
92+
CODE_MEDIUM=$(jq '[.runs // [] | .[].results // [] | .[] | select(.level == "warning")] | length' /tmp/snyk_code_report.json 2>/dev/null || echo "0")
93+
CODE_LOW=$(jq '[.runs // [] | .[].results // [] | .[] | select(.level == "note")] | length' /tmp/snyk_code_report.json 2>/dev/null || echo "0")
94+
95+
if [ "$CODE_TOTAL" -gt 0 ]; then
96+
CODE_SUMMARY="${CODE_TOTAL} issues found"
97+
CODE_VULNS="🟠 High: ${CODE_HIGH} | 🟡 Medium: ${CODE_MEDIUM} | 🔵 Low: ${CODE_LOW}"
98+
99+
# Extract top 10 code issues
100+
CODE_DETAILS=$(jq -r '
101+
[.runs // [] | .[].results // [] | .[]]
102+
| sort_by(if .level == "error" then 0 elif .level == "warning" then 1 else 2 end)
103+
| .[:10]
104+
| .[]
105+
| "• *\(.level | ascii_upcase)*: \(.message.text // .ruleId) — \(.locations[0].physicalLocation.artifactLocation.uri // "unknown"):L\(.locations[0].physicalLocation.region.startLine // "?")"
106+
' /tmp/snyk_code_report.json 2>/dev/null || echo "")
107+
fi
108+
fi
109+
110+
# ── Determine status ──
111+
HAS_ISSUES=false
112+
if [ "${OSS_TOTAL:-0}" -gt 0 ] || [ "${CODE_TOTAL:-0}" -gt 0 ]; then
113+
HAS_ISSUES=true
114+
fi
115+
116+
if [ "$HAS_ISSUES" = true ]; then
117+
STATUS="🚨 Snyk Security Scan: Vulnerabilities detected"
118+
COLOR="#e01e5a"
119+
else
120+
STATUS="✅ Snyk Security Scan: No vulnerabilities found"
121+
COLOR="#36a64f"
122+
fi
123+
124+
echo "=== SNYK SCAN SUMMARY ==="
125+
echo "OSS: $OSS_SUMMARY"
126+
echo "Code: $CODE_SUMMARY"
127+
128+
# ── Build Slack blocks ──
129+
# Truncate details to fit Slack's 3000-char block limit
130+
truncate_text() {
131+
local text="$1"
132+
local max_len=2800
133+
if [ ${#text} -gt $max_len ]; then
134+
echo "${text:0:$max_len}..."
135+
else
136+
echo "$text"
137+
fi
138+
}
139+
140+
BLOCKS="[
141+
{
142+
\"type\": \"header\",
143+
\"text\": { \"type\": \"plain_text\", \"text\": \"${STATUS}\" }
144+
},
145+
{
146+
\"type\": \"section\",
147+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"*Repository:* ${GITHUB_REPOSITORY}\" }
148+
},
149+
{
150+
\"type\": \"divider\"
151+
},
152+
{
153+
\"type\": \"section\",
154+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"*📦 Open Source (SCA) — ${OSS_SUMMARY}*\" }
155+
}"
156+
157+
if [ -n "$OSS_VULNS" ]; then
158+
BLOCKS="${BLOCKS},
159+
{
160+
\"type\": \"section\",
161+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"${OSS_VULNS}\" }
162+
}"
163+
fi
164+
165+
if [ -n "$OSS_DETAILS" ]; then
166+
ESCAPED_OSS=$(echo "$OSS_DETAILS" | sed 's/"/\\"/g' | tr '\n' '\\' | sed 's/\\/\\n/g' | sed 's/\\n$//')
167+
ESCAPED_OSS=$(truncate_text "$ESCAPED_OSS")
168+
BLOCKS="${BLOCKS},
169+
{
170+
\"type\": \"section\",
171+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"${ESCAPED_OSS}\" }
172+
}"
173+
fi
174+
175+
BLOCKS="${BLOCKS},
176+
{
177+
\"type\": \"divider\"
178+
},
179+
{
180+
\"type\": \"section\",
181+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"*🔍 Code (SAST) — ${CODE_SUMMARY}*\" }
182+
}"
183+
184+
if [ -n "$CODE_VULNS" ]; then
185+
BLOCKS="${BLOCKS},
186+
{
187+
\"type\": \"section\",
188+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"${CODE_VULNS}\" }
189+
}"
190+
fi
191+
192+
if [ -n "$CODE_DETAILS" ]; then
193+
ESCAPED_CODE=$(echo "$CODE_DETAILS" | sed 's/"/\\"/g' | tr '\n' '\\' | sed 's/\\/\\n/g' | sed 's/\\n$//')
194+
ESCAPED_CODE=$(truncate_text "$ESCAPED_CODE")
195+
BLOCKS="${BLOCKS},
196+
{
197+
\"type\": \"section\",
198+
\"text\": { \"type\": \"mrkdwn\", \"text\": \"${ESCAPED_CODE}\" }
199+
}"
200+
fi
201+
202+
BLOCKS="${BLOCKS},
203+
{
204+
\"type\": \"divider\"
205+
},
206+
{
207+
\"type\": \"actions\",
208+
\"elements\": [{
209+
\"type\": \"button\",
210+
\"text\": { \"type\": \"plain_text\", \"text\": \"View Full Results\" },
211+
\"url\": \"${ACTION_URL}\"
212+
}]
213+
}
214+
]"
215+
216+
# ── Send Slack notification ──
217+
if [ -n "$SLACK_WEBHOOK" ]; then
218+
echo "Sending Slack notification..."
219+
PAYLOAD=$(jq -n --argjson blocks "$BLOCKS" --arg color "$COLOR" '{
220+
attachments: [{ color: $color, blocks: $blocks }]
221+
}')
222+
223+
HTTP_CODE=$(curl -s -X POST "$SLACK_WEBHOOK" \
224+
-H "Content-Type: application/json" \
225+
-w "%{http_code}" \
226+
-o /tmp/slack_response.txt \
227+
-d "$PAYLOAD")
228+
229+
if [ "$HTTP_CODE" -ge 200 ] && [ "$HTTP_CODE" -lt 300 ]; then
230+
echo "Slack notification sent successfully (HTTP $HTTP_CODE)"
231+
else
232+
echo "Slack notification failed (HTTP $HTTP_CODE)"
233+
cat /tmp/slack_response.txt 2>/dev/null || true
234+
fi
235+
else
236+
echo "SLACK_WEBHOOK not configured, skipping notification"
237+
fi

0 commit comments

Comments
 (0)