Skip to content

Commit 6827554

Browse files
authored
Add Snyk Security Scan workflow
This workflow automates Snyk security scans on code pushes and pull requests, installs dependencies, and sends findings for remediation.
1 parent a2db395 commit 6827554

File tree

1 file changed

+218
-0
lines changed

1 file changed

+218
-0
lines changed
Lines changed: 218 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,218 @@
1+
name: Snyk Security Scan and Automated Resolution
2+
3+
on:
4+
push:
5+
branches: [main, develop]
6+
pull_request:
7+
branches: [main, develop]
8+
schedule:
9+
# Run daily at 2 AM UTC
10+
- cron: '0 2 * * *'
11+
workflow_dispatch:
12+
13+
permissions:
14+
contents: read
15+
security-events: write
16+
pull-requests: write
17+
issues: write
18+
19+
jobs:
20+
snyk-scan:
21+
runs-on: ubuntu-latest
22+
23+
steps:
24+
- name: Checkout code
25+
uses: actions/checkout@v4
26+
with:
27+
fetch-depth: 0
28+
29+
- name: Setup Node.js
30+
uses: actions/setup-node@v4
31+
with:
32+
node-version: '18'
33+
34+
- name: Setup Python
35+
uses: actions/setup-python@v4
36+
with:
37+
python-version: '3.9'
38+
39+
- name: Install dependencies
40+
run: |
41+
# Install dependencies for multi-project structure
42+
echo "Installing dependencies for all projects..."
43+
44+
# Install root dependencies if they exist and are not empty
45+
if [ -f package.json ] && [ "$(jq '.dependencies // {} | length' package.json)" -gt 0 ]; then
46+
echo "Installing root Node.js dependencies..."
47+
npm ci
48+
fi
49+
50+
# Install frontend dependencies
51+
if [ -f contact-form-app/frontend/package.json ]; then
52+
echo "Installing frontend dependencies..."
53+
cd contact-form-app/frontend
54+
npm ci
55+
cd ../..
56+
fi
57+
58+
# Install backend dependencies
59+
if [ -f contact-form-app/backend/requirements.txt ]; then
60+
echo "Installing backend Python dependencies..."
61+
pip install -r contact-form-app/backend/requirements.txt
62+
fi
63+
64+
# Install other project dependencies if they exist
65+
find . -name "package.json" -not -path "./node_modules/*" -not -path "./contact-form-app/frontend/node_modules/*" | while read package_file; do
66+
dir=$(dirname "$package_file")
67+
if [ "$dir" != "." ] && [ "$dir" != "./contact-form-app/frontend" ]; then
68+
echo "Installing dependencies in $dir..."
69+
cd "$dir"
70+
if [ -f package-lock.json ] || [ -f yarn.lock ]; then
71+
npm ci 2>/dev/null || npm install
72+
fi
73+
cd - > /dev/null
74+
fi
75+
done
76+
77+
- name: Install Snyk CLI
78+
run: |
79+
npm install -g snyk
80+
snyk --version
81+
82+
- name: Authenticate Snyk
83+
env:
84+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
85+
run: |
86+
# Verify token is available
87+
if [ -z "$SNYK_TOKEN" ]; then
88+
echo "Error: SNYK_TOKEN secret is not set"
89+
echo "Please add your Snyk API token to GitHub repository secrets"
90+
exit 1
91+
fi
92+
93+
# Test authentication by running a simple command
94+
echo "Testing Snyk authentication..."
95+
snyk auth --help > /dev/null 2>&1 || echo "Snyk CLI ready"
96+
97+
- name: Run Snyk Security Scan
98+
id: snyk-scan
99+
env:
100+
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
101+
SNYK_ORG_ID: ef0bb1e6-0d53-4889-b1b3-252dac87a0ef
102+
run: |
103+
# Create output directory
104+
mkdir -p snyk-results
105+
106+
# Run Snyk Code SAST scan and send directly to Devin
107+
echo "Running Snyk Code SAST scan..."
108+
109+
# Run single scan on entire repository
110+
snyk code test --org=$SNYK_ORG_ID --json --severity-threshold=medium > snyk-results/all-findings.json || echo "Scan completed with findings"
111+
112+
# Check if we have findings to send to Devin
113+
FINDINGS_COUNT=$(cat snyk-results/all-findings.json | jq '.runs[]?.results[]? | select(.level == "warning" or .level == "error")' | jq -s length)
114+
115+
if [ "$FINDINGS_COUNT" -gt 0 ]; then
116+
echo "✅ Found $FINDINGS_COUNT security findings - sending to Devin"
117+
echo "has-findings=true" >> $GITHUB_OUTPUT
118+
else
119+
echo "✅ No security findings detected"
120+
echo "has-findings=false" >> $GITHUB_OUTPUT
121+
fi
122+
123+
- name: Send Findings to Devin for Remediation
124+
id: devin-security
125+
if: steps.snyk-scan.outputs.has-findings == 'true'
126+
env:
127+
DEVIN_API_KEY: ${{ secrets.DEVIN_API_KEY }}
128+
run: |
129+
# Extract only the essential findings (no metadata/rules)
130+
FILTERED_RESULTS=$(cat snyk-results/all-findings.json | jq '{
131+
"findings": [
132+
.runs[]?.results[]? | {
133+
"ruleId": .ruleId,
134+
"level": .level,
135+
"message": .message.text,
136+
"file": .locations[0].physicalLocation.artifactLocation.uri,
137+
"line": .locations[0].physicalLocation.region.startLine,
138+
"description": "Debug mode enabled in production code"
139+
}
140+
]
141+
}')
142+
143+
# Create the prompt with filtered findings
144+
cat > prompt.txt << 'EOF'
145+
You are Security Resolver Devin. Fix all security issues found by Snyk Code scan.
146+
147+
Repository: ${{ github.repository }}
148+
Branch: ${{ github.ref_name }}
149+
150+
Tasks:
151+
1. Clone the repository locally
152+
2. Review the Snyk Code findings below
153+
3. Fix all security issues found
154+
4. Create a pull request with the fixes
155+
156+
Security Findings:
157+
EOF
158+
159+
# Append filtered results to the prompt
160+
echo "$FILTERED_RESULTS" >> prompt.txt
161+
162+
# Convert to JSON-safe format
163+
ESCAPED_PROMPT=$(cat prompt.txt | jq -Rs .)
164+
165+
echo "Creating Devin security resolution session..."
166+
echo "Prompt preview (first 500 chars):"
167+
head -c 500 prompt.txt
168+
echo ""
169+
echo "..."
170+
RESPONSE=$(curl -s -X POST \
171+
-H "Authorization: Bearer $DEVIN_API_KEY" \
172+
-H "Content-Type: application/json" \
173+
-d "{\"prompt\": $ESCAPED_PROMPT}" \
174+
"https://api.devin.ai/v1/sessions")
175+
176+
# Check for errors in the response
177+
ERROR_MSG=$(echo "$RESPONSE" | jq -r '.detail // empty')
178+
if [ ! -z "$ERROR_MSG" ] && [ "$ERROR_MSG" != "null" ]; then
179+
echo "Error creating Devin session: $ERROR_MSG"
180+
echo "Full response: $RESPONSE"
181+
exit 1
182+
fi
183+
184+
SESSION_ID=$(echo "$RESPONSE" | jq -r '.session_id // empty')
185+
SESSION_URL=$(echo "$RESPONSE" | jq -r '.url // empty')
186+
187+
if [ -z "$SESSION_ID" ] || [ -z "$SESSION_URL" ]; then
188+
echo "Error: Devin session details are missing from the response."
189+
echo "Full response: $RESPONSE"
190+
exit 1
191+
fi
192+
193+
echo "session-id=$SESSION_ID" >> $GITHUB_OUTPUT
194+
echo "session-url=$SESSION_URL" >> $GITHUB_OUTPUT
195+
echo "✅ Devin security resolution session created successfully"
196+
echo "📊 Session ID: $SESSION_ID"
197+
echo "🔗 Session URL: $SESSION_URL"
198+
199+
- name: Upload Scan Results
200+
uses: actions/upload-artifact@v4
201+
if: always()
202+
with:
203+
name: snyk-scan-results-${{ github.run_number }}
204+
path: snyk-results/
205+
retention-days: 30
206+
207+
- name: Summary
208+
if: always()
209+
run: |
210+
echo "🔍 Snyk Code SAST Scan Complete"
211+
echo "================================"
212+
213+
if [ "${{ steps.snyk-scan.outputs.has-findings }}" == "true" ]; then
214+
echo "🚨 Security findings detected - Devin AI session created"
215+
echo "🔗 Devin Session: ${{ steps.devin-security.outputs.session-url }}"
216+
else
217+
echo "✅ No security issues found"
218+
fi

0 commit comments

Comments
 (0)