
Model Supply Chain Security
- 68 installs
- 44 repo stars
- Updated May 22, 2026
- bagelhole/devops-security-agent-skills
model-supply-chain-security is a Claude Code skill that secures the AI model supply chain with artifact signing, provenance attestation, SBOM workflows, dependency controls, and trusted model promotion.
About
This skill secures the AI model supply chain against tampering, dependency compromise, and untrusted artifact promotion. It covers cosign signing of models and containers, SLSA provenance, SBOM generation with syft, model cards, and registry scanning. A developer uses it when pulling pretrained models from public registries or building model-serving containers for production. It matters because poisoned weights, compromised pipelines, and pickle deserialization attacks threaten ML systems.
- Secures the AI model supply chain with cosign artifact signing, provenance attestation, and SBOM workflows
- Maps SLSA levels 1-3 to ML build requirements and generates SLSA provenance in GitHub Actions
- Defends against poisoned weights, pickle deserialization attacks, and typosquatting on model registries
Model Supply Chain Security by the numbers
- 68 all-time installs (skills.sh)
- Ranked #1,188 of 2,203 Security skills by installs in the Skillselion catalog
- Data as of Jul 28, 2026 (Skillselion catalog sync)
model-supply-chain-security capabilities & compatibility
- Capabilities
- artifact signing · provenance attestation · sbom generation · security audit
- Works with
- github · aws · gcp · docker
- Use cases
- security audit · ci cd · devops
- Pricing
- Free
What model-supply-chain-security says it does
Protect models and inference components from tampering, dependency compromise, and untrusted artifact promotion.
Deserialization attacks via pickle or custom loaders
npx skills add https://github.com/bagelhole/devops-security-agent-skills --skill model-supply-chain-securityAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 68 |
|---|---|
| repo stars | ★ 44 |
| Last updated | May 22, 2026 |
| Repository | bagelhole/devops-security-agent-skills ↗ |
What it does
Sign, attest, and verify the provenance of ML models and serving images to prevent supply-chain tampering.
Who is it for?
Teams pulling pretrained models from public registries or building production model-serving containers under SLSA or SOC 2 requirements.
Skip if: Purely local experimentation with no artifact promotion or trust-policy needs.
When should I use this skill?
Pulling pretrained models from public registries or establishing trust policies for ML artifact promotion.
What you get
End-to-end verified model artifacts with signed provenance, SBOMs, CVE scanning, and signed-only execution.
By the numbers
- maps SLSA levels 1-3 for model builds
- 6 listed supply-chain threats
Files
Model Supply Chain Security
Protect models and inference components from tampering, dependency compromise, and untrusted artifact promotion.
When to Use This Skill
Use this skill when:
- Pulling pretrained models from public registries (Hugging Face, TensorFlow Hub)
- Building model-serving containers for production deployment
- Establishing trust policies for ML artifact promotion across environments
- Responding to supply chain incidents affecting ML dependencies
- Meeting SLSA or SOC2 compliance requirements for AI systems
Prerequisites
cosignv2+ installed for signing and verificationsyftfor SBOM generation of model-serving imagescraneorskopeofor OCI image inspection- Container registry with signature support (GHCR, ECR, ACR, Artifact Registry)
- CI/CD pipeline with provenance generation capability
Threats
- Poisoned pretrained weights or adapters
- Malicious model conversion tools or loaders
- Compromised build pipelines and registries
- Insecure runtime images with critical CVEs
- Typosquatting on model registries
- Deserialization attacks via pickle or custom loaders
Control Objectives
- Verify artifact integrity end-to-end
- Prove provenance for every promoted model
- Detect vulnerable dependencies before deploy
- Restrict execution to trusted signed artifacts
Model Signing with Cosign
Sign a Model Artifact
# Generate a keypair (store private key securely)
cosign generate-key-pair
# Sign an OCI-packaged model image
cosign sign --key cosign.key ghcr.io/acme/ml-models/sentiment:v2.1.0
# Keyless signing with Sigstore (uses OIDC identity)
cosign sign ghcr.io/acme/ml-models/sentiment:v2.1.0
# Verify the signature
cosign verify --key cosign.pub ghcr.io/acme/ml-models/sentiment:v2.1.0
# Keyless verification (requires certificate identity)
cosign verify \
--certificate-identity=ci-bot@acme.iam.gserviceaccount.com \
--certificate-oidc-issuer=https://accounts.google.com \
ghcr.io/acme/ml-models/sentiment:v2.1.0Sign Model Weight Files Directly
# For model files stored as blobs (not OCI images)
# Compute digest and sign
sha256sum model-weights.safetensors > model-weights.sha256
cosign sign-blob --key cosign.key model-weights.safetensors \
--output-signature model-weights.sig \
--output-certificate model-weights.crt
# Verify blob signature
cosign verify-blob --key cosign.pub \
--signature model-weights.sig \
model-weights.safetensorsSLSA for ML Pipelines
SLSA Level Requirements for Model Builds
# slsa-requirements.yaml
slsa_levels:
level_1:
- Build process is scripted (not manual)
- Provenance document generated automatically
level_2:
- Build runs on hosted CI service
- Provenance is authenticated (signed)
- Source is version controlled
level_3:
- Build environment is ephemeral and isolated
- Provenance is non-falsifiable (hardened builder)
- Source integrity verified (two-person review)Generate SLSA Provenance for Model Training
# .github/workflows/model-build-slsa.yml
name: Model Build with SLSA Provenance
on:
push:
tags: ['model-v*']
jobs:
train-and-package:
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Train model
run: python train.py --config configs/production.yaml
- name: Package model as OCI artifact
run: |
oras push ghcr.io/acme/ml-models/sentiment:${{ github.ref_name }} \
model-weights.safetensors:application/vnd.acme.model.safetensors \
model-config.json:application/json
- name: Generate SBOM for training environment
run: |
syft dir:. -o cyclonedx-json > training-sbom.json
- name: Sign and attest
run: |
cosign sign ghcr.io/acme/ml-models/sentiment:${{ github.ref_name }}
cosign attest --predicate training-sbom.json \
--type cyclonedx \
ghcr.io/acme/ml-models/sentiment:${{ github.ref_name }}
- name: Generate provenance
uses: slsa-framework/slsa-github-generator/.github/workflows/generator_container_slsa3.yml@v2.0.0
with:
image: ghcr.io/acme/ml-models/sentiment
digest: ${{ steps.push.outputs.digest }}Model Cards for Provenance
# model-card.yaml
model_details:
name: "sentiment-classifier-v2.1.0"
version: "2.1.0"
type: "text-classification"
framework: "pytorch"
license: "Apache-2.0"
provenance:
training_data:
source: "s3://acme-datasets/sentiment-v3/"
hash: "sha256:abc123..."
data_card_ref: "https://internal.acme.com/data-cards/sentiment-v3"
training_config:
source: "git://github.com/acme/ml-models@abc123"
hyperparameters:
learning_rate: 0.00005
epochs: 10
batch_size: 32
build_environment:
builder: "github-actions"
runner: "ubuntu-22.04"
python: "3.11.7"
torch: "2.1.2"
cuda: "12.1"
build_id: "gh-actions-12345"
commit_sha: "abc123def456"
build_timestamp: "2025-01-15T10:30:00Z"
signed_by: "ci-bot@acme.iam.gserviceaccount.com"
performance:
accuracy: 0.94
f1_score: 0.93
evaluation_dataset: "s3://acme-datasets/sentiment-eval-v3/"
evaluation_hash: "sha256:def456..."
security:
vulnerability_scan: "clean"
sbom_ref: "ghcr.io/acme/ml-models/sentiment:v2.1.0.sbom"
last_security_review: "2025-01-10"
known_limitations:
- "May produce biased outputs for underrepresented languages"
- "Not evaluated for adversarial robustness"Registry Scanning
# Scan model-serving image for CVEs
trivy image ghcr.io/acme/ml-models/sentiment-serving:v2.1.0
# Generate SBOM for the serving container
syft ghcr.io/acme/ml-models/sentiment-serving:v2.1.0 -o spdx-json > serving-sbom.json
# Scan SBOM for vulnerabilities
grype sbom:serving-sbom.json --fail-on critical
# Check for known-malicious model files (pickle scanning)
pip install fickling
fickling --check model.pklAutomated Registry Scan Pipeline
# .github/workflows/registry-scan.yml
name: Nightly Registry Scan
on:
schedule:
- cron: '0 2 * * *'
jobs:
scan:
runs-on: ubuntu-latest
strategy:
matrix:
image:
- ghcr.io/acme/ml-models/sentiment-serving:latest
- ghcr.io/acme/ml-models/embedding-serving:latest
- ghcr.io/acme/ml-models/rag-api:latest
steps:
- name: Scan image
run: |
trivy image --severity CRITICAL,HIGH \
--exit-code 1 \
--format json \
--output scan-$(echo ${{ matrix.image }} | tr '/:' '-').json \
${{ matrix.image }}
- name: Verify signatures are still valid
run: |
cosign verify \
--certificate-identity=ci-bot@acme.iam.gserviceaccount.com \
--certificate-oidc-issuer=https://accounts.google.com \
${{ matrix.image }}Promotion Policy Enforcement
#!/usr/bin/env python3
"""model_promotion_gate.py - Verify model meets all promotion criteria."""
import subprocess
import json
import sys
def check_signature(image: str) -> bool:
result = subprocess.run(
["cosign", "verify", "--certificate-identity=ci-bot@acme.iam.gserviceaccount.com",
"--certificate-oidc-issuer=https://accounts.google.com", image],
capture_output=True, text=True,
)
return result.returncode == 0
def check_vulnerabilities(image: str) -> bool:
result = subprocess.run(
["trivy", "image", "--severity", "CRITICAL", "--exit-code", "1",
"--quiet", image],
capture_output=True, text=True,
)
return result.returncode == 0
def check_sbom_exists(image: str) -> bool:
result = subprocess.run(
["cosign", "verify-attestation", "--type", "cyclonedx",
"--certificate-identity=ci-bot@acme.iam.gserviceaccount.com",
"--certificate-oidc-issuer=https://accounts.google.com", image],
capture_output=True, text=True,
)
return result.returncode == 0
def check_model_card(image: str) -> bool:
result = subprocess.run(
["cosign", "verify-attestation", "--type", "custom",
"--certificate-identity=ci-bot@acme.iam.gserviceaccount.com",
"--certificate-oidc-issuer=https://accounts.google.com", image],
capture_output=True, text=True,
)
return result.returncode == 0
def main():
image = sys.argv[1]
checks = {
"signature_valid": check_signature(image),
"no_critical_cves": check_vulnerabilities(image),
"sbom_attached": check_sbom_exists(image),
"model_card_present": check_model_card(image),
}
all_passed = all(checks.values())
for name, passed in checks.items():
status = "PASS" if passed else "FAIL"
print(f" [{status}] {name}")
if not all_passed:
print("Promotion BLOCKED: not all checks passed.")
sys.exit(1)
print("Promotion APPROVED: all checks passed.")
if __name__ == "__main__":
main()Runtime Hardening
- Run inference containers as non-root.
- Apply egress restrictions to prevent unauthorized downloads.
- Mount model volumes read-only when possible.
- Alert on unsigned artifact pull attempts.
- Use
safetensorsformat instead of pickle to prevent deserialization attacks.
# kubernetes deployment hardening
apiVersion: apps/v1
kind: Deployment
metadata:
name: model-serving
spec:
template:
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
containers:
- name: inference
image: ghcr.io/acme/ml-models/sentiment-serving:v2.1.0
securityContext:
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: ["ALL"]
volumeMounts:
- name: model-weights
mountPath: /models
readOnly: true
resources:
limits:
memory: "4Gi"
nvidia.com/gpu: "1"
volumes:
- name: model-weights
persistentVolumeClaim:
claimName: model-weights-pvc
readOnly: trueKyverno Policy for Admission Control
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-signed-model-images
spec:
validationFailureAction: Enforce
rules:
- name: verify-model-image-signature
match:
any:
- resources:
kinds: ["Pod"]
namespaces: ["ml-serving"]
verifyImages:
- imageReferences: ["ghcr.io/acme/ml-models/*"]
attestors:
- entries:
- keyless:
subject: "ci-bot@acme.iam.gserviceaccount.com"
issuer: "https://accounts.google.com"Troubleshooting
| Problem | Cause | Solution |
|---|---|---|
cosign verify fails with "no matching signatures" | Image was pushed without signing | Re-run the signing step; check CI pipeline logs |
| Provenance attestation missing | SLSA generator not configured | Add slsa-github-generator to the build workflow |
| Trivy reports CVEs in base image | Stale base image | Update FROM image in Dockerfile; rebuild and re-sign |
| Pickle deserialization warning | Model saved in unsafe format | Convert to safetensors: model.save_pretrained(".", safe_serialization=True) |
| Keyless verification fails | Wrong OIDC issuer or identity | Check --certificate-identity and --certificate-oidc-issuer flags |
| Model card not found for artifact | Attestation not attached to digest | Attach with cosign attest --predicate model-card.yaml --type custom IMAGE |
Related Skills
- sbom-supply-chain - Generate SBOM and provenance evidence
- container-hardening - Harden runtime container posture
- model-registry-governance - Controlled lifecycle and approvals
Related skills
FAQ
How do you sign model weight files that are not OCI images?
The skill uses cosign sign-blob with a key to sign the safetensors file and produce a signature and certificate, then verifies with cosign verify-blob.
What SLSA levels does it target?
It maps SLSA levels 1 through 3, from scripted builds with generated provenance (L1) up to ephemeral isolated build environments with non-falsifiable provenance and two-person review (L3).