brieuc.crosson commited on
Commit
48c592d
·
1 Parent(s): 272e2d2

feat: fix python vuln finder

Browse files
agentpimentbleu/services/dependency_service.py CHANGED
@@ -310,7 +310,7 @@ class DependencyService:
310
 
311
  try:
312
  result = subprocess.run(
313
- ["pip-audit", "--json", "-r", manifest_path],
314
  capture_output=True,
315
  text=True,
316
  check=False # Don't raise an exception on non-zero exit code
@@ -372,7 +372,7 @@ class DependencyService:
372
  primary_advisory_id = None
373
 
374
  # Use the advisory_id as primary if it's a GHSA or OSV ID
375
- if advisory_id and (advisory_id.startswith('GHSA-') or advisory_id.startswith('OSV-')):
376
  primary_advisory_id = advisory_id
377
 
378
  if 'aliases' in vuln_info:
@@ -383,15 +383,39 @@ class DependencyService:
383
  elif not primary_advisory_id and (alias.startswith('GHSA-') or alias.startswith('OSV-')):
384
  primary_advisory_id = alias
385
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
386
  vulnerability = {
387
  'package_name': package_name,
388
  'vulnerable_version': package_version,
 
389
  'cve_ids': cve_ids,
390
  'primary_advisory_id': primary_advisory_id,
391
  'advisory_link': advisory_link,
392
  'advisory_title': advisory_id,
393
  'severity': severity,
394
- 'fix_suggestion_from_tool': fix_suggestion
 
395
  }
396
 
397
  vulnerabilities.append(vulnerability)
 
310
 
311
  try:
312
  result = subprocess.run(
313
+ ["pip-audit", "-f", "json", "-r", manifest_path],
314
  capture_output=True,
315
  text=True,
316
  check=False # Don't raise an exception on non-zero exit code
 
372
  primary_advisory_id = None
373
 
374
  # Use the advisory_id as primary if it's a GHSA or OSV ID
375
+ if advisory_id and (advisory_id.startswith('GHSA-') or advisory_id.startswith('OSV-') or advisory_id.startswith('PYSEC-')):
376
  primary_advisory_id = advisory_id
377
 
378
  if 'aliases' in vuln_info:
 
383
  elif not primary_advisory_id and (alias.startswith('GHSA-') or alias.startswith('OSV-')):
384
  primary_advisory_id = alias
385
 
386
+ # Parse affected version ranges from the 'affected' array
387
+ affected_ranges = []
388
+ if 'affected' in vuln_info and isinstance(vuln_info['affected'], list):
389
+ for aff_item in vuln_info['affected']:
390
+ if 'ranges' in aff_item and isinstance(aff_item['ranges'], list):
391
+ for r_item in aff_item['ranges']:
392
+ if r_item.get('type') == 'SEMVER' and isinstance(r_item.get('events'), list):
393
+ # Construct a human-readable range, e.g., "<0.11.0" or ">=1.0.0, <1.2.3"
394
+ events = r_item['events']
395
+ range_parts = []
396
+ for e in events:
397
+ if "introduced" in e and e["introduced"] != "0": # often 0 is not useful
398
+ range_parts.append(f">={e['introduced']}")
399
+ if "fixed" in e:
400
+ range_parts.append(f"<{e['fixed']}")
401
+ if "last_affected" in e:
402
+ range_parts.append(f"<={e['last_affected']}")
403
+ if range_parts:
404
+ affected_ranges.append(", ".join(sorted(list(set(range_parts))))) # sort for consistency
405
+
406
+ advisory_vulnerable_range = "; ".join(affected_ranges) if affected_ranges else None
407
+
408
  vulnerability = {
409
  'package_name': package_name,
410
  'vulnerable_version': package_version,
411
+ 'installed_version': package_version, # For 'analyzed_project_version'
412
  'cve_ids': cve_ids,
413
  'primary_advisory_id': primary_advisory_id,
414
  'advisory_link': advisory_link,
415
  'advisory_title': advisory_id,
416
  'severity': severity,
417
+ 'fix_suggestion_from_tool': fix_suggestion,
418
+ 'advisory_vulnerable_range': advisory_vulnerable_range # Add the parsed vulnerable range
419
  }
420
 
421
  vulnerabilities.append(vulnerability)
tests/services/test_dependency_service.py CHANGED
@@ -137,7 +137,24 @@ class TestDependencyService(unittest.TestCase):
137
  "id": "PYSEC-2019-123",
138
  "link": "https://osv.dev/vulnerability/PYSEC-2019-123",
139
  "aliases": ["CVE-2016-10149"],
140
- "description": "The debugger in Werkzeug before 0.11.0 allows remote code execution."
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
141
  },
142
  "fix": {
143
  "versions": ["0.11.0"]
@@ -154,7 +171,13 @@ class TestDependencyService(unittest.TestCase):
154
  self.assertEqual(len(result), 1)
155
  self.assertEqual(result[0]['package_name'], 'werkzeug')
156
  self.assertEqual(result[0]['vulnerable_version'], '0.10.0')
 
157
  self.assertEqual(result[0]['cve_ids'], ['CVE-2016-10149'])
 
 
 
 
 
158
 
159
  # Assert the mock was called
160
  mock_run.assert_called_once()
 
137
  "id": "PYSEC-2019-123",
138
  "link": "https://osv.dev/vulnerability/PYSEC-2019-123",
139
  "aliases": ["CVE-2016-10149"],
140
+ "description": "The debugger in Werkzeug before 0.11.0 allows remote code execution.",
141
+ "affected": [
142
+ {
143
+ "package": {
144
+ "name": "werkzeug",
145
+ "ecosystem": "PyPI"
146
+ },
147
+ "ranges": [
148
+ {
149
+ "type": "SEMVER",
150
+ "events": [
151
+ {"introduced": "0"},
152
+ {"fixed": "0.11.0"}
153
+ ]
154
+ }
155
+ ]
156
+ }
157
+ ]
158
  },
159
  "fix": {
160
  "versions": ["0.11.0"]
 
171
  self.assertEqual(len(result), 1)
172
  self.assertEqual(result[0]['package_name'], 'werkzeug')
173
  self.assertEqual(result[0]['vulnerable_version'], '0.10.0')
174
+ self.assertEqual(result[0]['installed_version'], '0.10.0')
175
  self.assertEqual(result[0]['cve_ids'], ['CVE-2016-10149'])
176
+ self.assertEqual(result[0]['primary_advisory_id'], 'PYSEC-2019-123')
177
+ self.assertEqual(result[0]['advisory_link'], 'https://osv.dev/vulnerability/PYSEC-2019-123')
178
+ self.assertEqual(result[0]['advisory_title'], 'PYSEC-2019-123')
179
+ self.assertEqual(result[0]['advisory_vulnerable_range'], '<0.11.0')
180
+ self.assertEqual(result[0]['fix_suggestion_from_tool'], 'Update to one of these versions: 0.11.0')
181
 
182
  # Assert the mock was called
183
  mock_run.assert_called_once()