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", " | 
| 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()
         | 
