Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion modelaudit/scanners/zip_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,6 +419,8 @@ def _scan_zip_file(self, path: str, depth: int = 0) -> ScanResult:
mar_python_result = self._scan_mar_python_entry(path, name, tmp_path, total_size)
if mar_python_result is not None:
result.merge(mar_python_result)
if not mar_python_result.success:
scan_complete = False

nested_config = dict(self.config)
nested_config["_archive_depth"] = depth + 1
Expand Down Expand Up @@ -525,7 +527,7 @@ def _scan_mar_python_entry(
message=f"Unable to parse Python entry for static analysis: {parse_error}",
severity=IssueSeverity.WARNING,
location=f"{archive_path}:{entry_name}",
details={"entry": entry_name},
details={"entry": entry_name, "analysis_kind": "syntax", "parse_error": parse_error},
)
else:
result.add_check(
Expand Down
24 changes: 24 additions & 0 deletions tests/scanners/test_zip_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -334,6 +334,30 @@ def test_scan_manifestless_mar_skips_oversized_python_handler_analysis(self, tmp
assert handler_failures[0].details.get("size_limit") == 16
assert handler_failures[0].location == f"{mar_path}:handler.py"

def test_scan_manifestless_mar_reports_malformed_python_handler(self, tmp_path: Path) -> None:
"""Manifest-less .mar handlers with invalid syntax should emit parse-error analysis checks."""
mar_path = tmp_path / "malformed_handler.mar"
with zipfile.ZipFile(mar_path, "w") as archive:
archive.writestr("handler.py", "def handle(data, context)\n return data\n")

result = self.scanner.scan(str(mar_path))
assert result.success is False
assert result.has_warnings is True
assert result.has_errors is False

handler_failures = [
check
for check in result.checks
if check.name == "TorchServe Handler Static Analysis" and check.status == CheckStatus.FAILED
]
assert len(handler_failures) == 1
assert handler_failures[0].severity == IssueSeverity.WARNING
assert "unable to parse python entry for static analysis" in handler_failures[0].message.lower()
assert handler_failures[0].details.get("entry") == "handler.py"
assert handler_failures[0].details.get("analysis_kind") == "syntax"
assert "expected ':'" in str(handler_failures[0].details.get("parse_error")).lower()
assert handler_failures[0].location == f"{mar_path}:handler.py"

def test_scan_extensionless_nested_zip_recurses(self, tmp_path: Path) -> None:
"""Extensionless ZIP members should be recursively scanned by content."""
inner_zip = io.BytesIO()
Expand Down
Loading