Model Trust Gateway — unified secure loader with signature verification, trust policy enforcement, and sandbox isolation
Project description
secure-torch
Model Trust Gateway — Unified, defense-in-depth Python library for safely loading AI model files.
What is secure-torch?
Loading third-party AI models is a supply chain risk. torch.load, ONNX Runtime, and safetensors have all been vectors for RCE, data exfiltration, and silent model tampering.
secure-torch is a Model Trust Enforcement Layer — not just a safe loader wrapper.
It adds what the ML ecosystem is missing:
| Feature | What it does |
|---|---|
| Signature verification | Verify model provenance before loading (online + offline) |
| Trust policy enforcement | Block models from untrusted publishers |
| Pickle opcode validation | Inspect opcodes without executing — blocks RCE payloads |
| Sandbox isolation | Load in restricted subprocess (seccomp on Linux) |
| Explainable threat scoring | Named breakdown, not a magic number |
| SBOM parsing | SPDX AI Profile support (experimental) |
How it complements safetensors
safetensors solves unsafe deserialization and memory safety. secure-torch adds the trust layer on top — signature verification, publisher policy, and provenance validation — for safetensors, PyTorch pickle, and ONNX alike.
Quick Start
pip install secure-torch
Drop-in usage
import secure_torch as torch # drop-in replacement
model = torch.load("model.pt") # safe by default
Require a valid signature
model = torch.load(
"model.pt",
require_signature=True,
bundle_path="model.pt.sigstore", # online Rekor verification
)
Offline verification (enterprise / air-gapped)
model = torch.load(
"model.pt",
require_signature=True,
pubkey_path="trusted.pub", # Ed25519 public key
bundle_path="model.pt.sig", # raw signature bytes
)
Trusted publishers allowlist
model = torch.load(
"model.pt",
trusted_publishers=["huggingface.co/meta", "openai.com"],
)
Audit first, block later (gradual adoption)
model, report = torch.load("model.pt", audit_only=True)
print(report.threat_level) # ThreatLevel.LOW
print(report.score_breakdown) # {'unsigned_model': 40}
print(report.warnings) # ['No signature bundle found']
Sandbox isolation
model = torch.load("model.pt", sandbox=True)
# Model loaded in restricted subprocess (strict exec/network blocking via seccomp on Linux)
Other compatibility surfaces
torch.jit.load("model.pt") # secure pipeline for local artifacts
torch.hub.load("pytorch/vision", "resnet50") # remote convenience passthrough
torch.from_pretrained("bert-base-uncased") # remote convenience passthrough
torch.save(model, "model.pt")
torch.hub.load and direct torch.from_pretrained calls do not currently enforce secure-torch security checks for remote fetches.
Passing security arguments (require_signature, trusted_publishers, audit_only, max_threat_score, sandbox, sbom_*, bundle_path, pubkey_path) raises SecurityError.
For enforced security controls, download artifacts first and call torch.load(local_path, ...), or use secure_torch.patch_huggingface(...) for Hugging Face download interception.
Hugging Face integration (automatic download scanning)
import secure_torch
from transformers import AutoModel
# Global monkey-patch: intercept hf_hub_download and validate model files.
secure_torch.patch_huggingface(require_signature=False, max_threat_score=20)
model = AutoModel.from_pretrained("gpt2")
# Optional cleanup when done.
secure_torch.unpatch_huggingface()
patch_huggingface(...) hooks huggingface_hub.file_download.hf_hub_download.
For model-like files (.pt, .pth, .bin, .safetensors, .onnx),
secure-torch runs format detection, validators, signature/publisher checks, and threat policy
evaluation before returning the downloaded file path. Unsafe artifacts are blocked with
UnsafeModelError.
Interactive CLI audit
# Styled report (rich-enabled when available)
secure-torch audit model.pt
# Machine-readable output
secure-torch audit model.pt --json
Pipeline
Every secure_load() call runs this fixed pipeline — steps cannot be skipped:
secure_load(file)
1. format detect ← .safetensors / .pt / .onnx / magic bytes
2. signature verify ← fail fast if require_signature=True
3. threat score ← explainable named dict
4. policy enforce ← trusted_publishers check
5. sandbox load ← subprocess (+ seccomp on Linux)
6. return tensors
Threat Scoring
Scores are named and explainable — not a magic number:
model, report = torch.load("model.pt", audit_only=True)
print(report.score_breakdown)
# {
# 'unsigned_model': 40,
# 'custom_ops_detected': 30,
# 'unknown_publisher': 20,
# }
# total: 90 → ThreatLevel.CRITICAL
| Score | Threat Level |
|---|---|
| 0 | SAFE |
| 1–19 | LOW |
| 20–49 | MEDIUM |
| 50–79 | HIGH |
| 80+ | CRITICAL |
Default max_threat_score is 20 (MEDIUM). Adjust with max_threat_score=N.
SBOM Policy (Experimental)
Note: SPDX AI Profile is not yet widely adopted (early 2026). This feature is forward-looking — secure-torch is helping establish the standard.
from secure_torch.sbom.spdx_parser import parse_sbom
from secure_torch.sbom.opa_runner import OPAPolicyRunner
sbom = parse_sbom("model.spdx.json")
runner = OPAPolicyRunner("policy/production.rego")
denials = runner.evaluate(sbom, context={"environment": "production"})
if denials:
raise RuntimeError(f"Policy violations: {denials}")
Example policy (policy/production.rego):
package secure_torch.policy
deny[msg] {
input.sensitivePersonalInformation == "yes"
msg := "Model contains sensitive personal information"
}
deny[msg] {
ds := input.aiProfile.trainingDatasets[_]
startswith(ds.license, "GPL")
input.environment == "production"
msg := sprintf("GPL dataset '%v' blocked in production", [ds.name])
}
Supported Formats
| Format | Validator | Notes |
|---|---|---|
.safetensors |
Header + dtype allowlist | Complements safetensors library |
.pt / .pth / .bin |
Pickle opcode validator | Never executes pickle |
.onnx |
Protobuf inspector | Custom op domain detection |
Signature Verification
Mode 1 — Online (Sigstore / Rekor)
# Sign your model
cosign sign-blob model.pt --bundle model.pt.sigstore
# Verify on load
torch.load("model.pt", require_signature=True, bundle_path="model.pt.sigstore")
Mode 2 — Offline (Ed25519 pubkey)
# Sign
openssl genpkey -algorithm ed25519 -out private.pem
openssl pkey -in private.pem -pubout -out public.pem
openssl pkeyutl -sign -inkey private.pem -in model.pt -out model.pt.sig
# Verify on load
torch.load("model.pt", require_signature=True, pubkey_path="public.pem", bundle_path="model.pt.sig")
CVE Coverage
| CVE | Attack | Status |
|---|---|---|
| CVE-2023-44271 | PyTorch pickle RCE (Salesforce) | ✅ Blocked |
| GHSA-v9fq-2296 | HuggingFace metadata injection | ✅ Scored |
| CVE-2024-5980 | NVIDIA Triton ONNX custom op RCE | ✅ Scored |
Installation
pip install secure-torch
# With ONNX support
pip install secure-torch[onnx]
# With Sigstore online verification
pip install secure-torch[sigstore]
# With offline public-key verification
pip install secure-torch[crypto]
# Everything
pip install secure-torch[all]
Development
git clone https://github.com/Avanishk05/secure-torch
cd secure-torch
pip install -e .
pip install pytest pytest-cov mypy ruff bandit
pytest tests/
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file secure_torch-0.2.0.tar.gz.
File metadata
- Download URL: secure_torch-0.2.0.tar.gz
- Upload date:
- Size: 33.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96c47115e7a2259b47f4dba352a81a6da635f4f0f21c09343375e678405fb380
|
|
| MD5 |
a272dd7a9b7776d72bf25e338ee3830e
|
|
| BLAKE2b-256 |
66da26b44083953aa2f03a117870a8180f7147b6792b874691c14f1a65735a9e
|
Provenance
The following attestation bundles were made for secure_torch-0.2.0.tar.gz:
Publisher:
ci.yml on Avanishk05/secure-torch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
secure_torch-0.2.0.tar.gz -
Subject digest:
96c47115e7a2259b47f4dba352a81a6da635f4f0f21c09343375e678405fb380 - Sigstore transparency entry: 996354145
- Sigstore integration time:
-
Permalink:
Avanishk05/secure-torch@3cdd7025d87771c201137f66f31eccc4765403fe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Avanishk05
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3cdd7025d87771c201137f66f31eccc4765403fe -
Trigger Event:
push
-
Statement type:
File details
Details for the file secure_torch-0.2.0-py3-none-any.whl.
File metadata
- Download URL: secure_torch-0.2.0-py3-none-any.whl
- Upload date:
- Size: 40.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.7
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e4e69fbf3b1f54750d68e281bf009fba3a0543feaf83eabddb86bd948e478a6e
|
|
| MD5 |
d820992a3b3a6c111cea87c4384472d3
|
|
| BLAKE2b-256 |
81606c55e54a9afb5588f53f1af5b091a178fd433d06a1ce7250ed08a9f58538
|
Provenance
The following attestation bundles were made for secure_torch-0.2.0-py3-none-any.whl:
Publisher:
ci.yml on Avanishk05/secure-torch
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
secure_torch-0.2.0-py3-none-any.whl -
Subject digest:
e4e69fbf3b1f54750d68e281bf009fba3a0543feaf83eabddb86bd948e478a6e - Sigstore transparency entry: 996354175
- Sigstore integration time:
-
Permalink:
Avanishk05/secure-torch@3cdd7025d87771c201137f66f31eccc4765403fe -
Branch / Tag:
refs/tags/v0.2.0 - Owner: https://github.com/Avanishk05
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
ci.yml@3cdd7025d87771c201137f66f31eccc4765403fe -
Trigger Event:
push
-
Statement type: