|
29 | 29 | from macaron.slsa_analyzer.build_tool.poetry import Poetry |
30 | 30 | from macaron.slsa_analyzer.checks.base_check import BaseCheck |
31 | 31 | from macaron.slsa_analyzer.checks.check_result import CheckResultData, CheckResultType, Confidence, JustificationType |
| 32 | +from macaron.slsa_analyzer.package_registry.deps_dev import APIAccessError, DepsDevService |
32 | 33 | from macaron.slsa_analyzer.package_registry.pypi_registry import PyPIPackageJsonAsset, PyPIRegistry |
33 | 34 | from macaron.slsa_analyzer.registry import registry |
34 | 35 | from macaron.slsa_analyzer.specs.package_registry_spec import PackageRegistryInfo |
@@ -182,7 +183,7 @@ def __init__(self) -> None: |
182 | 183 | """Initialize a check instance.""" |
183 | 184 | check_id = "mcn_detect_malicious_metadata_1" |
184 | 185 | description = """This check analyzes the metadata of a package based on reports malicious behavior. |
185 | | - Supported ecosystem: PyPI. |
| 186 | + Supported ecosystem for unknown malware: PyPI. |
186 | 187 | """ |
187 | 188 | super().__init__(check_id=check_id, description=description, eval_reqs=[]) |
188 | 189 |
|
@@ -288,37 +289,46 @@ def run_check(self, ctx: AnalyzeContext) -> CheckResultData: |
288 | 289 | The result of the check. |
289 | 290 | """ |
290 | 291 | result_tables: list[CheckFacts] = [] |
291 | | - # First check if this package is a known malware |
| 292 | + package_registry_info_entries = ctx.dynamic_data["package_registries"] |
292 | 293 |
|
| 294 | + # First check if this package is a known malware |
293 | 295 | data = {"package": {"purl": ctx.component.purl}} |
294 | | - response = send_post_http_raw(self.osv_query_url, json_data=data, headers=None) |
295 | | - res_obj = None |
296 | | - if response: |
297 | | - try: |
298 | | - res_obj = response.json() |
299 | | - except requests.exceptions.JSONDecodeError as error: |
300 | | - logger.debug("Unable to get a valid response from %s: %s", self.osv_query_url, error) |
301 | | - if res_obj: |
302 | | - for vuln in res_obj.get("vulns", {}): |
303 | | - v_id = json_extract(vuln, ["id"], str) |
304 | | - if v_id and v_id.startswith("MAL-"): |
305 | | - result_tables.append( |
306 | | - MaliciousMetadataFacts( |
307 | | - known_malware=f"https://osv.dev/vulnerability/{v_id}", |
308 | | - result={}, |
309 | | - detail_information=vuln, |
310 | | - confidence=Confidence.HIGH, |
| 296 | + |
| 297 | + try: |
| 298 | + package_exists = bool(DepsDevService.get_package_info(ctx.component.purl)) |
| 299 | + except APIAccessError as error: |
| 300 | + logger.debug(error) |
| 301 | + |
| 302 | + # Known malicious packages must have been removed. |
| 303 | + if not package_exists: |
| 304 | + response = send_post_http_raw(self.osv_query_url, json_data=data, headers=None) |
| 305 | + res_obj = None |
| 306 | + if response: |
| 307 | + try: |
| 308 | + res_obj = response.json() |
| 309 | + except requests.exceptions.JSONDecodeError as error: |
| 310 | + logger.debug("Unable to get a valid response from %s: %s", self.osv_query_url, error) |
| 311 | + if res_obj: |
| 312 | + for vuln in res_obj.get("vulns", {}): |
| 313 | + if v_id := json_extract(vuln, ["id"], str): |
| 314 | + result_tables.append( |
| 315 | + MaliciousMetadataFacts( |
| 316 | + known_malware=f"https://osv.dev/vulnerability/{v_id}", |
| 317 | + result={}, |
| 318 | + detail_information=vuln, |
| 319 | + confidence=Confidence.HIGH, |
| 320 | + ) |
311 | 321 | ) |
| 322 | + if result_tables: |
| 323 | + return CheckResultData( |
| 324 | + result_tables=result_tables, |
| 325 | + result_type=CheckResultType.FAILED, |
312 | 326 | ) |
313 | | - if result_tables: |
314 | | - return CheckResultData( |
315 | | - result_tables=result_tables, |
316 | | - result_type=CheckResultType.FAILED, |
317 | | - ) |
318 | 327 |
|
319 | | - package_registry_info_entries = ctx.dynamic_data["package_registries"] |
| 328 | + # If the package is not a known malware, run malware analysis heuristics. |
320 | 329 | for package_registry_info_entry in package_registry_info_entries: |
321 | 330 | match package_registry_info_entry: |
| 331 | + # Currently, only PyPI packages are supported. |
322 | 332 | case PackageRegistryInfo( |
323 | 333 | build_tool=Pip() | Poetry(), |
324 | 334 | package_registry=PyPIRegistry() as pypi_registry, |
|
0 commit comments