Skip to content

Commit 28b7ac2

Browse files
meysholdtona-agent
andcommitted
Replace Snyk CLI automation with MCP and API-based automations
- Remove fix-snyk-issue.yaml (CLI-based) - Add snyk-scan-and-fix.yaml: uses Snyk MCP tools (code, SCA, IaC scans) - Add snyk-api-fix-highest.yaml: uses Snyk REST API to find and fix issues Co-authored-by: Ona <no-reply@ona.com>
1 parent 1a0a8a5 commit 28b7ac2

3 files changed

Lines changed: 355 additions & 109 deletions

File tree

.ona/fix-snyk-issue.yaml

Lines changed: 0 additions & 109 deletions
This file was deleted.

.ona/snyk-api-fix-highest.yaml

Lines changed: 202 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,202 @@
1+
name: snyk-api-fix-highest
2+
description: >-
3+
Calls the Snyk REST API to list issues for the project, selects the
4+
highest-severity vulnerability, applies a fix, verifies tests pass,
5+
and opens a pull request.
6+
triggers:
7+
- context:
8+
projects: {}
9+
manual: {}
10+
action:
11+
limits:
12+
maxParallel: 1
13+
maxTotal: 10
14+
steps:
15+
# Step 1: Query the Snyk REST API for project issues
16+
- task:
17+
command: |
18+
set -euo pipefail
19+
20+
# Require SNYK_TOKEN and SNYK_ORG_ID to be set
21+
if [ -z "${SNYK_TOKEN:-}" ]; then
22+
echo "ERROR: SNYK_TOKEN environment variable is not set."
23+
exit 1
24+
fi
25+
if [ -z "${SNYK_ORG_ID:-}" ]; then
26+
echo "ERROR: SNYK_ORG_ID environment variable is not set."
27+
echo "Set it to your Snyk organization ID (found in Snyk org settings)."
28+
exit 1
29+
fi
30+
31+
API_VERSION="2024-10-15"
32+
BASE_URL="https://api.snyk.io/rest"
33+
34+
# List projects in the org to find matching project
35+
echo "Fetching projects from Snyk API..."
36+
curl -sS -X GET \
37+
"${BASE_URL}/orgs/${SNYK_ORG_ID}/projects?version=${API_VERSION}&limit=100" \
38+
-H "Authorization: token ${SNYK_TOKEN}" \
39+
-H "Content-Type: application/vnd.api+json" \
40+
-o /tmp/snyk-api-projects.json
41+
42+
# Extract the first project ID (or match by repo name if possible)
43+
REPO_NAME=$(basename "$GITPOD_REPO_ROOT")
44+
PROJECT_ID=$(python3 -c "
45+
import json, sys
46+
with open('/tmp/snyk-api-projects.json') as f:
47+
data = json.load(f)
48+
projects = data.get('data', [])
49+
if not projects:
50+
print('NO_PROJECT')
51+
sys.exit(0)
52+
# Try to match by repo name
53+
repo = '${REPO_NAME}'.lower()
54+
for p in projects:
55+
name = p.get('attributes', {}).get('name', '').lower()
56+
if repo in name:
57+
print(p['id'])
58+
sys.exit(0)
59+
# Fall back to first project
60+
print(projects[0]['id'])
61+
")
62+
63+
if [ "$PROJECT_ID" = "NO_PROJECT" ]; then
64+
echo '{"noProject": true}' > /tmp/snyk-api-issues.json
65+
echo "No projects found in Snyk org."
66+
exit 0
67+
fi
68+
69+
echo "Found project: $PROJECT_ID"
70+
71+
# Fetch issues for the project
72+
echo "Fetching issues from Snyk API..."
73+
curl -sS -X GET \
74+
"${BASE_URL}/orgs/${SNYK_ORG_ID}/issues?version=${API_VERSION}&scan_item.id=${PROJECT_ID}&scan_item.type=project&limit=100&severity=critical%2Chigh%2Cmedium%2Clow" \
75+
-H "Authorization: token ${SNYK_TOKEN}" \
76+
-H "Content-Type: application/vnd.api+json" \
77+
-o /tmp/snyk-api-raw-issues.json
78+
79+
# Extract and rank issues by severity, pick the top one
80+
python3 -c "
81+
import json, sys
82+
83+
with open('/tmp/snyk-api-raw-issues.json') as f:
84+
data = json.load(f)
85+
86+
issues = data.get('data', [])
87+
if not issues:
88+
json.dump({'noIssues': True}, open('/tmp/snyk-api-issues.json', 'w'))
89+
print('NO_VULN: No issues found.')
90+
sys.exit(0)
91+
92+
severity_order = {'critical': 4, 'high': 3, 'medium': 2, 'low': 1}
93+
94+
def rank(issue):
95+
sev = issue.get('attributes', {}).get('effective_severity_level', 'low')
96+
return severity_order.get(sev, 0)
97+
98+
issues.sort(key=rank, reverse=True)
99+
top = issues[0]
100+
attrs = top.get('attributes', {})
101+
102+
result = {
103+
'id': top.get('id'),
104+
'type': top.get('type'),
105+
'title': attrs.get('title', ''),
106+
'severity': attrs.get('effective_severity_level', ''),
107+
'status': attrs.get('status', ''),
108+
'description': (attrs.get('description', '') or '')[:500],
109+
'problems': attrs.get('problems', []),
110+
'coordinates': attrs.get('coordinates', []),
111+
'classes': attrs.get('classes', []),
112+
}
113+
114+
with open('/tmp/snyk-api-issues.json', 'w') as f:
115+
json.dump(result, f, indent=2)
116+
117+
print(f\"Top issue: {result['title']} (severity: {result['severity']})\")
118+
print(json.dumps(result, indent=2))
119+
"
120+
121+
# Step 2: Analyze the issue and apply a fix
122+
- agent:
123+
prompt: |
124+
AUTOMATION RULES -- override all other instructions.
125+
1. Never ask questions. Infer and record as ASSUMPTION.
126+
2. Never wait for confirmation. Process all items to completion.
127+
3. Concrete over abstract -- use literal values from source.
128+
4. Cite file and line for every claim.
129+
130+
Read /tmp/snyk-api-issues.json which contains the highest-severity
131+
issue from the Snyk API.
132+
133+
If the file contains {"noIssues": true} or {"noProject": true},
134+
output "NO_VULN: No actionable Snyk issues found." and stop.
135+
136+
Analyze the issue:
137+
- Extract the vulnerability title, severity, and description.
138+
- Look at the "problems" array for CVE/CWE identifiers.
139+
- Look at the "coordinates" array for affected package names,
140+
versions, and remediation advice (fixedIn, upgradePath).
141+
142+
Determine the fix strategy:
143+
144+
For dependency vulnerabilities:
145+
- If coordinates contain a "remedies" or "fixedIn" field, upgrade
146+
the dependency to the minimum fixed version.
147+
- For Maven projects, update the version in pom.xml.
148+
- For Gradle projects, update build.gradle.
149+
- If the vulnerable package is a transitive dependency, find the
150+
direct dependency in the build file that pulls it in and upgrade
151+
that instead.
152+
- If no fix version is available, output
153+
"NO_FIX: <id> has no available fix." and stop.
154+
155+
For code vulnerabilities:
156+
- Identify the affected source file and line from coordinates.
157+
- Read the file and apply the recommended secure coding pattern.
158+
159+
For configuration issues:
160+
- Identify the affected config file and apply the secure setting.
161+
162+
Apply the fix in the appropriate file. Do NOT commit or run tests.
163+
164+
# Step 3: Verify the fix
165+
- agent:
166+
prompt: |
167+
AUTOMATION RULES -- override all other instructions.
168+
1. Never ask questions. Infer and record as ASSUMPTION.
169+
2. Never wait for confirmation. Process all items to completion.
170+
171+
Verify the fix from the previous step:
172+
173+
1. Identify the project build tool (Maven or Gradle) from the repo
174+
config files.
175+
2. Compile the project. If it fails, read the errors, fix them,
176+
and retry.
177+
3. Find all test suites and verification commands that could
178+
exercise the modified code. Run them.
179+
4. If any check fails, determine whether the failure is caused by
180+
your change or is pre-existing. Fix what you broke and rerun.
181+
5. Repeat until all checks pass.
182+
183+
# Step 4: Open a pull request
184+
- pullRequest:
185+
branch: snyk-fix/
186+
title: 'Snyk-Fix: '
187+
description: |
188+
## Snyk Vulnerability (via API)
189+
190+
| Field | Value |
191+
|-------|-------|
192+
| **Vulnerability** | `<vuln-id>` |
193+
| **Severity** | <severity> |
194+
| **Title** | <vuln-title> |
195+
196+
## What changed
197+
198+
<one-or-two-sentence explanation of the fix and why it resolves the vulnerability>
199+
200+
## Verification
201+
202+
<List each build, test, and lint command that was run and its outcome.>

0 commit comments

Comments
 (0)