From e23d6db242630c9e046124f845db027c0a22ff46 Mon Sep 17 00:00:00 2001 From: Michael D'Angelo Date: Fri, 10 Apr 2026 14:23:18 -0700 Subject: [PATCH 1/2] fix: flag paddle code patterns as warnings --- CHANGELOG.md | 1 + modelaudit/scanners/paddle_scanner.py | 4 ++-- tests/scanners/test_paddle_scanner.py | 31 ++++++++++++++++++++++++++- 3 files changed, 33 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fc1c12a9c..f71f97be6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -35,6 +35,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Bug Fixes +- make Paddle suspicious code patterns produce security exit codes - avoid CoreML nested parse failures on bounded-read truncation - flag TensorFlow `LoadLibrary` and `LoadLibraryV2` graph ops as dangerous native-library loading - detect split CNTK native-user-function and native-library references diff --git a/modelaudit/scanners/paddle_scanner.py b/modelaudit/scanners/paddle_scanner.py index ed3d8f62c..6a19749ac 100644 --- a/modelaudit/scanners/paddle_scanner.py +++ b/modelaudit/scanners/paddle_scanner.py @@ -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", @@ -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", diff --git a/tests/scanners/test_paddle_scanner.py b/tests/scanners/test_paddle_scanner.py index 13ba7ce1d..5833bec8f 100644 --- a/tests/scanners/test_paddle_scanner.py +++ b/tests/scanners/test_paddle_scanner.py @@ -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 @@ -28,7 +29,22 @@ 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: + 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) + + assert result.success is True + assert determine_exit_code(result) == 1 + assert any(issue.severity == IssueSeverity.WARNING for issue in result.issues) def test_paddle_scanner_missing_dependency(tmp_path: Path) -> None: @@ -104,6 +120,19 @@ 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: + 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) + + assert result.success is True + assert determine_exit_code(result) == 1 + assert any(issue.severity == IssueSeverity.WARNING for issue in result.issues) def test_pdmodel_hex_escape_still_flagged(tmp_path: Path) -> None: From 65169094332068dbe36cb85c8d83e83b74839aa0 Mon Sep 17 00:00:00 2001 From: Michael D'Angelo Date: Fri, 10 Apr 2026 15:34:00 -0700 Subject: [PATCH 2/2] test: assert paddle warning aggregate semantics --- tests/scanners/test_paddle_scanner.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/tests/scanners/test_paddle_scanner.py b/tests/scanners/test_paddle_scanner.py index 5833bec8f..7f7ede7a4 100644 --- a/tests/scanners/test_paddle_scanner.py +++ b/tests/scanners/test_paddle_scanner.py @@ -36,15 +36,18 @@ def test_paddle_scanner_detects_suspicious_pattern(tmp_path: Path) -> None: 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 any(issue.severity == IssueSeverity.WARNING for issue in result.issues) + assert warning_issues def test_paddle_scanner_missing_dependency(tmp_path: Path) -> None: @@ -124,15 +127,18 @@ def test_pdiparams_real_threats_still_detected(tmp_path: Path) -> None: 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 any(issue.severity == IssueSeverity.WARNING for issue in result.issues) + assert warning_issues def test_pdmodel_hex_escape_still_flagged(tmp_path: Path) -> None: