Skip to main content

modelaudit-picklescan

Rust-backed, bounded, static pickle security scanner. Inspects Python pickle streams and PyTorch ZIP checkpoints without unpickling them, and returns a typed report you can feed into CI, SARIF exporters, or custom policy engines.

PyPI version Python versions License: MIT Wheel platforms

Why this package

Pickle deserialization is the most common supply-chain attack vector in ML checkpoints, and existing Python-only scanners either unpickle the payload (unsafe), scan string literals only (imprecise), or fail open on large/malformed inputs (dangerous in CI). This package is a direct response:

  • Rust scanner engine. Opcode walker, string analyzer, and nested-payload decoder are all native code.
  • Fail-closed semantics. Every scan returns both a status (complete / inconclusive / error) and a verdict (clean / suspicious / malicious / unknown). Truncation, timeouts, budget exhaustion, and parser errors downgrade the verdict instead of silently returning clean.
  • Bounded by construction. Opcode count, wall-clock timeout, string-literal bytes, nested-payload bytes, and recursion depth are all configurable caps with safe defaults. A malicious producer cannot force unbounded memory or CPU.
  • Zero Python runtime dependencies. The wheel is self-contained — pip install modelaudit-picklescan and nothing else.
  • Attested provenance. Release wheels are published to PyPI with sigstore attestations via GitHub Actions trusted publishing.
  • Typed, immutable reports. PickleReport, Finding, Notice, and ScanError are frozen dataclasses with to_dict() for serialization. The package ships py.typed for mypy / pyright.

Install

pip install modelaudit-picklescan

Pre-built abi3 wheels ship for Python 3.10–3.13 on five targets: Linux x86_64, Linux aarch64, macOS arm64, macOS x86_64, and Windows x64. Other platforms install from the sdist and require a Rust toolchain (see Building from source).

Quickstart

from modelaudit_picklescan import scan_file

report = scan_file("suspicious_model.pt")  # raw pickle or PyTorch ZIP checkpoint

print(f"status={report.status.value} verdict={report.verdict.value}")
for finding in report.findings:
    print(f"  [{finding.severity.value}] {finding.rule_code}: {finding.message}")
    if finding.location:
        print(f"    at {finding.location}")

Example output on a PyTorch ZIP whose inner pickle reduces on os.system:

status=complete verdict=malicious
  [critical] DANGEROUS_CALL: Found REDUCE opcode invoking os.system
    at suspicious_model.pt:archive/data.pkl (pos 42)

Example output on a truncated or oversized pickle where analysis is incomplete:

status=inconclusive verdict=unknown
  (no findings — scan was truncated, inspect report.notices and report.coverage)

The finding.location string follows the format {source} (pos {byte_offset}). The source on PyTorch ZIP members is {archive_path}:{member_name}.

What it detects

Each finding carries a rule_code so downstream tooling can allowlist, suppress, or route alerts:

Rule code What it flags
DANGEROUS_CALL REDUCE/NEWOBJ/NEWOBJ_EX opcodes invoking a callable known to execute code
DANGEROUS_GLOBAL Imports of modules or classes that enable code execution when the pickle is loaded
EXTENSION_REF copyreg.extension / EXT1/EXT2/EXT4 opcodes that resolve through process state
MALFORMED_STACK_GLOBAL STACK_GLOBAL operands crafted to bypass naive string-matching scanners
NON_ALLOWLISTED_GLOBAL Non-allowlisted global references that require manual review before loading
PERSISTENT_ID PERSID / BINPERSID references that delegate object construction to the loader
PICKLE_EXPANSION Oversized or amplified pickle structures consistent with zip-bomb-style payloads
POST_BUDGET_GLOBAL Dangerous globals observed after the opcode budget, surfaced conservatively
STRUCTURAL_TAMPER Opcode sequences that do not correspond to any legitimate pickle producer
SUSPICIOUS_STRING High-signal string literals (shell metacharacters, import payloads, URLs)
S203 Non-allowlisted __main__ global reference (requires manual review before loading)
S213 Raw (unencoded) nested pickle payload inside a byte field
S601 Base64-encoded nested pickle payload inside a string literal
S602 Hex-encoded nested pickle payload inside a string literal

The scanner covers pickle protocols 0 through 5, recognizes short and extended opcodes, and reconstructs module.class targets for STACK_GLOBAL without executing them.

When to use this vs. modelaudit

Use modelaudit-picklescan if you want a single-purpose library to embed in another tool: a linter, a model registry gate, a custom CI step, or a server-side scanner. It does pickle analysis and nothing else.

Use modelaudit if you want the full static scanner CLI: 40+ model/archive format scanners, SARIF and JSON output, remote-source scanning (Hugging Face, S3, GCS, JFrog, MLflow, DVC), license and secret detection, caching, progress reporting, and CI recipes. modelaudit uses this package internally for its pickle scanner.

API overview

from modelaudit_picklescan import (
    PickleScanner, ScanOptions,
    scan_file, scan_bytes, scan_stream,
    shared_source_sensitive_caches,
    PickleReport, Finding, Notice, ScanError,
    Severity, ScanStatus, SafetyVerdict, CoverageSummary,
)

Three convenience entry points, each returning a PickleReport:

  • scan_file(path, *, options=None) — scan a .pkl / .pickle or a PyTorch ZIP checkpoint (detects the container, enumerates pickle members, combines reports).
  • scan_bytes(data, *, source="<bytes>", options=None) — scan an in-memory payload.
  • scan_stream(stream, *, source="<stream>", size=None, options=None) — scan a binary file-like object; falls back to bounded spooling when size is unknown.

For long-running services, construct PickleScanner(options=...) once and reuse it across calls.

When scanning a related batch of files, use shared_source_sensitive_caches() to reuse bounded call-graph source analysis. Analyzed source content is validated before each later report; changes observed at final validation fail closed as inconclusive, and later scan batches remain isolated:

from modelaudit_picklescan import scan_file, shared_source_sensitive_caches

with shared_source_sensitive_caches():
    reports = [scan_file(path) for path in model_paths]

Resource controls — ScanOptions

All fields have safe defaults; override only what you need.

Field Default Meaning
timeout_s 3600.0 Per-scan wall clock, capped at 86_400 seconds
max_opcodes 1_000_000 Opcode budget before the scanner downgrades to partial
post_budget_scan_bytes 100 MiB Bytes to keep scanning for globals after the budget
max_known_stream_read_bytes 100 MiB Cap on streams with a known size
max_unbounded_stream_read_bytes 8 MiB Cap on streams without a known size
max_string_literal_scan_chars 8 MiB Cap on bytes inspected for SUSPICIOUS_STRING
max_nested_pickle_bytes 2 MiB Cap on each decoded nested-payload inspection
max_nested_depth 2 Recursion depth for base64/hex-encoded pickles

Construction validates every field; pass invalid values and you'll get a ValueError immediately instead of a misleading scan result.

Report contract — PickleReport

  • status: ScanStatus — complete, inconclusive, or error.
  • verdict: SafetyVerdict — clean, suspicious, malicious, or unknown. clean requires status=complete with no findings.
  • findings: tuple[Finding, ...] — WARNING or CRITICAL security results.
  • notices: tuple[Notice, ...] — DEBUG/INFO explainability and coverage notes (budget hits, truncation, unsupported members).
  • errors: tuple[ScanError, ...] — operational failures (short reads, malformed containers, engine errors).
  • coverage: CoverageSummary — bytes_scanned, bytes_total, opcode_count, and per-phase completion flags.
  • metadata: Mapping[str, Any] — container info (e.g. container_type="pytorch_zip", archive size, pickle members).
  • duration_s: float — scan wall clock.

Convenience accessors: report.has_security_findings, report.is_clean, report.to_dict().

Reports and all nested models are frozen — call to_dict() if you need a mutable payload for serialization. For aggregation, treat findings at warning/critical as security alerts; group notices by code rather than showing every INFO row as actionable.

PyTorch ZIP checkpoints

scan_file auto-detects PyTorch ZIP containers from PyTorch metadata plus pickle members, including hidden members, and combines per-member reports into a single container-level report with metadata.container_type="pytorch_zip". Archive member count is capped at 10,000 entries; per-member pickles are capped at 512 MiB. Both limits are enforced by structured notices, not silent skips.

Building from source

Wheels cover five targets; any other platform or a custom Python ABI requires building from source:

# Requires Rust 1.83+ and a working C toolchain
pip install modelaudit-picklescan --no-binary modelaudit-picklescan

From a checkout:

pip install packages/modelaudit-picklescan
# or, for development with hot-reload of the Rust extension:
maturin develop --release -m packages/modelaudit-picklescan/Cargo.toml

Stability and versioning

modelaudit-picklescan follows semantic versioning. 0.x should be read as pre-1.0 — expect small adjustments as the API settles. The working intent, reflected in the current code, is:

  • Resource-control defaults (ScanOptions) are tuned conservatively; changes that relax a default will be called out in the changelog.
  • Public report models (PickleReport, Finding, Notice, ScanError) and their field names are the supported surface for serialization and downstream tooling.
  • Rule codes are intended to be additive — new codes rather than renames — so that downstream allowlists and suppressions remain stable.
  • Verdict semantics — SafetyVerdict.CLEAN is only returned when ScanStatus.COMPLETE holds and there are no findings; truncation, timeouts, and engine errors never produce CLEAN. This is enforced in _combine_verdict / _with_*_notice in api.py.

Any change to the items above will be announced in CHANGELOG.md and the GitHub release notes.

Security and reporting

Please do not open public GitHub issues for suspected vulnerabilities. See the project security policy for coordinated disclosure.

License

MIT. See LICENSE.

Metadata

Release files for modelaudit-picklescan 0.1.12

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for modelaudit-picklescan 0.1.12
File Size Uploaded
modelaudit_picklescan-0.1.12.tar.gz 475.6 kB Details

Built distributions (wheels)

Table of built distributions (wheels) for modelaudit-picklescan 0.1.12
File
modelaudit_picklescan-0.1.12-cp310-abi3-win_amd64.whl CPython 3.10 abi3 Windows x86-64 Details
modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_x86_64.whl CPython 3.10 abi3 Linux glibc 2.28+ x86-64 Details
modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_aarch64.whl CPython 3.10 abi3 Linux glibc 2.28+ ARM64 Details
modelaudit_picklescan-0.1.12-cp310-abi3-macosx_11_0_arm64.whl CPython 3.10 abi3 macOS 11.0+ ARM64 Details
modelaudit_picklescan-0.1.12-cp310-abi3-macosx_10_12_x86_64.whl CPython 3.10 abi3 macOS 10.12+ x86-64 Details

Total release size: 5.4 MB

Release files / modelaudit_picklescan-0.1.12.tar.gz

Download URL modelaudit_picklescan-0.1.12.tar.gz
Size 475.6 kB
Tags Source
SHA-256 checksum
How to use checksums
6ddd37609b72b8a14686d20344ed9e7d555e08d9df33ea914b8e5895587fb689
BLAKE2b-256 checksum
How to use checksums
7d02b90d5bc97579eabef9f3847790548fa6e46e4605c3bfe77010b71a2d8972
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / modelaudit_picklescan-0.1.12-cp310-abi3-win_amd64.whl

Download URL modelaudit_picklescan-0.1.12-cp310-abi3-win_amd64.whl
Size 886.4 kB
Tags CPython 3.10 Windows x86-64 abi3
SHA-256 checksum
How to use checksums
78fa5ada426e78cf1d0085a5aea88946a9e16d9e474b1dfd1124fe0b77eb77a4
BLAKE2b-256 checksum
How to use checksums
9da9597ecde3795b54efb53a1d75831f06206af1aea415ee536bb18a244bb7cf
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_x86_64.whl

Download URL modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_x86_64.whl
Size 1.0 MB
Tags CPython 3.10 Linux glibc 2.28+ x86-64 abi3
SHA-256 checksum
How to use checksums
87995bb19f344ece03314eb91277eff076d5316adc9943e0c5fe9f2534c5f9e1
BLAKE2b-256 checksum
How to use checksums
91512009e9b3d95135e969ddd0dae023b6bb25fdbb5e4f592fc139746c2bde41
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_aarch64.whl

Download URL modelaudit_picklescan-0.1.12-cp310-abi3-manylinux_2_28_aarch64.whl
Size 1.0 MB
Tags CPython 3.10 Linux glibc 2.28+ ARM64 abi3
SHA-256 checksum
How to use checksums
767816f4248be42964657cadfb0c30aa20b762076c7f4e15a85bc8ad3f426a12
BLAKE2b-256 checksum
How to use checksums
c1636f69a7b207b836797c58f7928ddc477be9fb4d96c9d1954a978256b4352b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / modelaudit_picklescan-0.1.12-cp310-abi3-macosx_11_0_arm64.whl

Download URL modelaudit_picklescan-0.1.12-cp310-abi3-macosx_11_0_arm64.whl
Size 972.7 kB
Tags CPython 3.10 abi3 macOS 11.0+ ARM64
SHA-256 checksum
How to use checksums
1b39fb1df9761bfe93c95c0d38e27495de8b5a5f755eeefc0af7f019c550217a
BLAKE2b-256 checksum
How to use checksums
66655daeadef27eb6131cf32114f7bf932a759e18ec965507820657fb37e6847
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release files / modelaudit_picklescan-0.1.12-cp310-abi3-macosx_10_12_x86_64.whl

Download URL modelaudit_picklescan-0.1.12-cp310-abi3-macosx_10_12_x86_64.whl
Size 977.7 kB
Tags CPython 3.10 abi3 macOS 10.12+ x86-64
SHA-256 checksum
How to use checksums
35f2579c9fd53b6e74b9bfd5403da0d32c56ea25484164339e42320df4f6b35a
BLAKE2b-256 checksum
How to use checksums
99fb7acce372cf5399e23bc5341c0394a3b038951711307ba96cca338bbc546e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.13

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 8, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.12 This release

6 release files

0.1.10

6 release files

0.1.9

6 release files

0.1.8

6 release files

0.1.5

6 release files

0.1.4

6 release files

0.1.3

6 release files

0.1.2

6 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page