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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug Fixes

- flag Paddle code patterns as warnings instead of failing benign scans
- route corrupt CatBoost scans to fail closed outcomes
- mark incomplete MXNet scans inconclusive instead of clean
- harden manifest parse boundaries around malformed metadata
Expand Down
4 changes: 2 additions & 2 deletions modelaudit/scanners/paddle_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,7 @@ def _check_chunk(
name="Binary Pattern Detection",
passed=False,
message=f"Suspicious binary pattern found: {pattern.decode('ascii', 'ignore')}",
severity=IssueSeverity.INFO,
severity=IssueSeverity.WARNING,
location=f"{path} (offset: {offset + pos})",
details={"pattern": pattern.decode("ascii", "ignore"), "offset": offset + pos},
rule_code="S902",
Expand All @@ -137,7 +137,7 @@ def _check_chunk(
name="String Pattern Detection",
passed=False,
message=f"Suspicious string pattern found: {regex}",
severity=IssueSeverity.INFO,
severity=IssueSeverity.WARNING,
location=path,
details={"pattern": regex},
rule_code="S902",
Expand Down
37 changes: 36 additions & 1 deletion tests/scanners/test_paddle_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from pathlib import Path
from unittest.mock import patch

from modelaudit.core import determine_exit_code, scan_model_directory_or_file
from modelaudit.scanners.base import IssueSeverity
from modelaudit.scanners.paddle_scanner import PaddleScanner
from modelaudit.utils.file.detection import validate_file_type
Expand All @@ -28,7 +29,25 @@ def test_paddle_scanner_detects_suspicious_pattern(tmp_path: Path) -> None:
with patch("modelaudit.scanners.paddle_scanner.HAS_PADDLE", True):
scanner = PaddleScanner()
result = scanner.scan(str(path))
assert any("suspicious" in i.message.lower() for i in result.issues)
suspicious_issues = [i for i in result.issues if "suspicious" in i.message.lower()]

assert suspicious_issues
assert all(issue.severity == IssueSeverity.WARNING for issue in suspicious_issues)


def test_paddle_suspicious_pdmodel_aggregate_exit_code_is_security_finding(tmp_path: Path) -> None:
"""Suspicious .pdmodel patterns should be warning-level security findings."""
path = tmp_path / "model.pdmodel"
path.write_bytes(b"os.system('ls')")

with patch("modelaudit.scanners.paddle_scanner.HAS_PADDLE", True):
result = scan_model_directory_or_file(str(path), cache_scan_results=False)

warning_issues = [issue for issue in result.issues if issue.severity == IssueSeverity.WARNING]
assert result.success is True
assert result.has_errors is False
assert determine_exit_code(result) == 1
assert warning_issues


def test_paddle_scanner_missing_dependency(tmp_path: Path) -> None:
Expand Down Expand Up @@ -104,6 +123,22 @@ def test_pdiparams_real_threats_still_detected(tmp_path: Path) -> None:
assert any("import os" in p for p in patterns_found), "import os should be detected"
assert any("eval(" in p for p in patterns_found), "eval( should be detected"
assert any("os.system" in p for p in patterns_found), "os.system should be detected"
assert all(i.severity == IssueSeverity.WARNING for i in result.issues)


def test_paddle_suspicious_pdiparams_aggregate_exit_code_is_security_finding(tmp_path: Path) -> None:
"""Suspicious .pdiparams patterns should be warning-level security findings."""
path = tmp_path / "bad_weights.pdiparams"
path.write_bytes(b"padding " + b"import os" + b" eval(payload) " + b"os.system('rm -rf /')")

with patch("modelaudit.scanners.paddle_scanner.HAS_PADDLE", True):
result = scan_model_directory_or_file(str(path), cache_scan_results=False)

warning_issues = [issue for issue in result.issues if issue.severity == IssueSeverity.WARNING]
assert result.success is True
assert result.has_errors is False
assert determine_exit_code(result) == 1
assert warning_issues


def test_pdmodel_hex_escape_still_flagged(tmp_path: Path) -> None:
Expand Down
Loading