Skip to content

Commit e6e3266

Browse files
authored
chore: fix a bug for uncaught SourceCodeError exception (#1185)
This PR addresses a bug related to the uncaught SourceCodeError exception in the PyPI type stub file analyzer. An additional integration test has been added to ensure correct handling for Python packages distributed only as wheels. Signed-off-by: behnazh-w <[email protected]>
1 parent 6655afb commit e6e3266

File tree

6 files changed

+46
-14
lines changed

6 files changed

+46
-14
lines changed

src/macaron/malware_analyzer/pypi_heuristics/base_analyzer.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# Copyright (c) 2024 - 2024, Oracle and/or its affiliates. All rights reserved.
1+
# Copyright (c) 2024 - 2025, Oracle and/or its affiliates. All rights reserved.
22
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
33

44
"""Define and initialize the base analyzer."""
@@ -40,4 +40,9 @@ def analyze(self, pypi_package_json: PyPIPackageJsonAsset) -> tuple[HeuristicRes
4040
-------
4141
tuple[HeuristicResult, dict[str, JsonType]]:
4242
The result and related information collected during the analysis.
43+
44+
Raises
45+
------
46+
HeuristicAnalyzerValueError
47+
If a heuristic analysis fails due to malformed package information.
4348
"""

src/macaron/malware_analyzer/pypi_heuristics/metadata/type_stub_file.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import logging
77
import os
88

9-
from macaron.errors import SourceCodeError
109
from macaron.json_tools import JsonType
1110
from macaron.malware_analyzer.pypi_heuristics.base_analyzer import BaseHeuristicAnalyzer
1211
from macaron.malware_analyzer.pypi_heuristics.heuristics import HeuristicResult, Heuristics
@@ -40,11 +39,12 @@ def analyze(self, pypi_package_json: PyPIPackageJsonAsset) -> tuple[HeuristicRes
4039
tuple[HeuristicResult, dict[str, JsonType]]:
4140
The result and related information collected during the analysis.
4241
"""
42+
# TODO: .pyi stub files may be present in both source distributions (sdist) and wheels.
43+
# Currently, we only check the sdist, which can lead to false positives in this heuristic.
44+
# To improve accuracy, we should also check for stub files in the wheel distribution.
4345
result = pypi_package_json.download_sourcecode()
4446
if not result:
45-
error_msg = "No source code files have been downloaded"
46-
logger.debug(error_msg)
47-
raise SourceCodeError(error_msg)
47+
return HeuristicResult.SKIP, {"message": "No source code files have been downloaded.", "pyi_files": 0}
4848

4949
file_count = sum(
5050
sum(1 for f in files if f.endswith(".pyi"))

src/macaron/slsa_analyzer/checks/detect_malicious_metadata_check.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ def analyze_source(
154154
return {analyzer.heuristic: result}, detail_info
155155

156156
except SourceCodeError as error:
157-
error_msg = f"Unable to perform analysis, source code not available: {error}"
157+
error_msg = f"Unable to perform source code analysis: {error}"
158158
logger.debug(error_msg)
159159
raise HeuristicAnalyzerValueError(error_msg) from error
160160

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
/* Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved. */
2+
/* Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/. */
3+
4+
#include "prelude.dl"
5+
6+
Policy("check-malicious-package", component_id, "Check the malicious package.") :-
7+
check_passed(component_id, "mcn_detect_malicious_metadata_1").
8+
9+
apply_policy_to("check-malicious-package", component_id) :-
10+
is_component(component_id, "pkg:pypi/[email protected]").
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Copyright (c) 2025 - 2025, Oracle and/or its affiliates. All rights reserved.
2+
# Licensed under the Universal Permissive License v 1.0 as shown at https://oss.oracle.com/licenses/upl/.
3+
4+
description: |
5+
Analyzing a Python package that is distributed as a wheel, with no source (sdist) available.
6+
7+
tags:
8+
- macaron-python-package
9+
10+
steps:
11+
- name: Run macaron analyze
12+
kind: analyze
13+
options:
14+
command_args:
15+
- -purl
16+
- pkg:pypi/[email protected]
17+
- name: Run macaron verify-policy to verify that the malicious metadata check passes.
18+
kind: verify
19+
options:
20+
policy: policy.dl

tests/malware_analyzer/pypi/test_type_stub_file.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77

88
import pytest
99

10-
from macaron.errors import SourceCodeError
1110
from macaron.malware_analyzer.pypi_heuristics.heuristics import HeuristicResult
1211
from macaron.malware_analyzer.pypi_heuristics.metadata.type_stub_file import TypeStubFileAnalyzer
1312

@@ -64,14 +63,12 @@ def test_analyze_no_files_fail(analyzer: TypeStubFileAnalyzer, pypi_package_json
6463

6564

6665
def test_analyze_download_failed_raises_error(analyzer: TypeStubFileAnalyzer, pypi_package_json: MagicMock) -> None:
67-
"""Test the analyzer raises SourceCodeError when source code download fails."""
66+
"""Test the analyzer when source code download fails."""
6867
pypi_package_json.download_sourcecode.return_value = False
69-
70-
with pytest.raises(SourceCodeError) as exc_info:
71-
analyzer.analyze(pypi_package_json)
72-
73-
assert "No source code files have been downloaded" in str(exc_info.value)
74-
pypi_package_json.download_sourcecode.assert_called_once()
68+
assert (
69+
HeuristicResult.SKIP,
70+
{"message": "No source code files have been downloaded.", "pyi_files": 0},
71+
) == analyzer.analyze(pypi_package_json)
7572

7673

7774
@pytest.mark.parametrize(

0 commit comments

Comments
 (0)