AI News HubLIVE
站内改写5 min read

Show HN: Model Due Diligence

Model Due Diligence is a Python CLI tool for static supply-chain due diligence on local AI model files and cloned model repositories before importing them into runtimes like Ollama or llama.cpp. It checks file inventory, hashes, risky serialization formats, suspicious code patterns, Git provenance, and more, producing Markdown, JSON, and SARIF reports. The tool does not load weights or execute code, and its risk scoring ranges from 0 to 100.

SourceHacker News AIAuthor: djhope99

Notifications You must be signed in to change notification settings

Fork 0

Star 0

BranchesTags

Open more actions menu

Folders and files

NameName

Last commit message

Last commit date

Latest commit

History

13 Commits

13 Commits

.github

.github

docs

docs

examples

examples

scripts

scripts

src/model_due_diligence

src/model_due_diligence

tests

tests

.DS_Store

.DS_Store

.env.example

.env.example

.gitattributes

.gitattributes

.gitignore

.gitignore

.python-version

.python-version

LICENSE

LICENSE

README.md

README.md

pyproject.toml

pyproject.toml

Repository files navigation

model-due-diligence is a Python command-line tool for performing static supply-chain due diligence on local AI model files and cloned model repositories before they are imported into runtimes such as Ollama, llama.cpp, LM Studio or Transformers.

It is designed to help answer one practical question:

“Is there obvious static evidence that this model artefact or repository should not be trusted, loaded or run without further review?”

It reduces practical risk from unsafe serialisation, suspicious repository content, weak provenance, exposed secrets, unexpected binaries, unsafe dependency files and malformed model metadata.

It does not prove that a model is safe.

A clean report means only that this tool did not identify the specific static artefact risks it is designed to detect. It must not be treated as proof that model weights, repository content, runtime behaviour or downstream use are benign.

Contents

What the tool does

What the tool does not do

Architecture

Scanner coverage

Risk scoring

Install

Quick start

CLI reference

Example workflows

Reports and outputs

Recommended operating model

Development workflow

Testing and quality gates

Repository structure

Security posture

Standards alignment

Limitations

Roadmap

Contributing

Licence

What the tool does

model-due-diligence statically inspects a local path and generates reviewable evidence.

It checks:

file inventory, SHA-256 hashes, permissions and symlinks;

high-risk serialisation formats such as pickle, .pt, .pth, .bin, .joblib and H5;

lower-risk model formats such as .gguf, .safetensors and .onnx;

GGUF magic bytes and version metadata;

safetensors header metadata;

suspicious text and binary strings;

Python AST indicators such as eval, exec, compile, pickle.loads, os.system and subprocess;

trust_remote_code=True usage in Python and text files;

risky pickle-like byte markers in high-risk serialisation formats;

high-entropy non-model files;

Git provenance, origin remote, current commit, dirty worktree and Git LFS listing where available;

external scanner output from ModelScan, Semgrep, Bandit, pip-audit and detect-secrets;

optional quality self-checks using Ruff, Pyright and mypy.

The tool produces:

a human-readable Markdown report;

a deterministic JSON report for automation;

an optional SARIF report for code-scanning workflows;

raw external scanner outputs where external tools are run.

What the tool does not do

The tool is intentionally static. During normal scanning it does not:

load model weights;

import untrusted repository code;

execute model-specific scripts;

run model inference;

send artefacts to external services;

require network access for local scanning;

decide automatically that a model is safe.

Static scanning cannot reliably detect:

malicious behaviour encoded directly into model weights;

sleeper-agent or trigger-based backdoors;

training-data poisoning;

benchmark-specific manipulation;

malicious behaviour that appears only after fine-tuning;

malicious behaviour that appears only after tools are connected;

prompt-injection obedience in downstream RAG or agent workflows;

data exfiltration behaviour that only appears at runtime;

vulnerabilities in local model runtimes;

all unsafe deserialisation evasions.

Use it as a risk-reduction gate, not as a trust oracle.

Architecture

The project uses a modular monolith architecture. This keeps installation and local execution simple while maintaining clear internal boundaries between CLI, orchestration, scanners, risk scoring and reports.

flowchart LR user[User / CI] --> cli[CLI] cli --> app[Application Orchestrator] app --> inventory[File Inventory] app --> native[Native Static Scanners] app --> external[External Scanner Adapters] app --> risk[Risk Scorer] risk --> report_model[Audit Report Model] app --> report_model report_model --> markdown[Markdown Report] report_model --> json[JSON Report] report_model --> sarif[SARIF Report]

native --> text[Text Patterns] native --> ast[Python AST] native --> binary[Binary Strings] native --> entropy[Entropy] native --> metadata[Model Metadata] native --> pickle[Pickle Heuristics] native --> git[Git Provenance]

external --> modelscan[ModelScan] external --> semgrep[Semgrep] external --> bandit[Bandit] external --> pipaudit[pip-audit] external --> secrets[detect-secrets] external --> quality[Quality Self-Checks]

Loading

Runtime flow

sequenceDiagram participant U as User / CI participant C as CLI participant A as App participant I as Inventory participant N as Native Scanners participant E as External Scanners participant R as Risk Scorer participant W as Report Writers

U->>C: mdd --out C->>C: Parse arguments and build ScanContext C->>A: Run scan A->>I: Build file inventory and hashes I-->>A: FileRecord[] + Finding[] A->>N: Run static native scanners N-->>A: Finding[] + ModelMetadata[] A->>E: Run optional external scanners E-->>A: CommandResult[] + Finding[] A->>R: Score findings and tool outcomes R-->>A: Risk score + risk level A-->>C: AuditReport C->>W: Write Markdown / JSON / SARIF C-->>U: Print risk score, risk level and report paths

Loading

Internal dependency direction

Dependencies should flow in one direction:

cli -> app -> domain app -> inventory app -> scanners app -> external app -> reporting scanners -> domain/config/utils external -> domain/config/command_runner reporting -> domain/config

Rules:

scanners must not import app;

reporters must not run scanners;

external adapters must not write final project reports directly;

domain models must not depend on filesystem, subprocess or reporting modules;

native scanners must not execute model artefacts or repository code.

Scanner coverage

Coverage area Native support External support Status

File inventory, hashes and permissions Yes No Covered

Symlink detection Yes No Covered

Executable/script detection Yes Semgrep / Bandit Covered

High-risk serialisation detection Yes ModelScan Covered

Pickle heuristic indicators Yes ModelScan Covered

GGUF header inspection Yes No Basic coverage

Safetensors header inspection Yes No Basic coverage

Suspicious text/code patterns Yes Semgrep / Bandit Covered

Python AST dangerous-call detection Yes Bandit / CodeQL Covered

Binary string indicators Yes No Basic coverage

High-entropy anomaly detection Yes No Basic coverage

Secrets detection Yes detect-secrets Covered

Dependency vulnerability checks No pip-audit / Dependabot Covered for requirements.txt

Git provenance checks Yes No Basic coverage

Project code quality No Ruff / Pyright / mypy / pytest Covered

Repository semantic security analysis No CodeQL Covered in GitHub Actions

SARIF output Yes CodeQL native SARIF Partial

SBOM generation No No Planned

Sigstore / SLSA provenance No No Planned

Licence compatibility checks No No Planned

Model-card quality checks No No Planned

Weight-level backdoor detection No No Not reliably detectable

Runtime behavioural testing No No Planned separately

Risk scoring

Findings are normalised into severities and converted into a bounded score from 0 to 100.

Severity Current score contribution

INFO 0

LOW 3

MEDIUM 10

HIGH 30

CRITICAL 60

External scanner non-zero exits can also contribute to the score when the tool was available and produced reviewable signals.

Risk level Score range Meaning Recommended action

LOW 0-29 No obvious supported static artefact risks were found Acceptable for sandboxed first run

MEDIUM 30-69 Reviewable findings exist Do not import until findings are understood

HIGH 70-89 Material risk indicators exist Do not load unless every finding is justified

CRITICAL 90-100 Severe or multiple high-risk indicators exist Treat as unsafe by default

The score is intentionally conservative. It is a decision aid, not an automated trust verdict.

Install

Prerequisites

Python 3.11 or later;

Git;

a Unix-like shell for the provided scripts;

optional external scanner CLIs if you want full coverage.

Recommended local setup

python3 -m venv .venv source .venv/bin/activate python -m pip install --upgrade pip python -m pip install -e ".[dev,scanners]"

Or use the setup script:

./scripts/dev-setup.sh

For a lighter install without optional scanner integrations:

./scripts/dev-setup.sh --no-scanners

Verify installation

mdd --help mdd-ollama --help model-due-diligence --help python -m model_due_diligence --help

Quick start

Scan a cloned model repository:

mdd ./downloaded-model --out ./audit

Scan a local GGUF file:

mdd ~/models/qwen.gguf --out ./audit-qwen

Scan an installed Ollama model by name:

mdd-ollama qwen3:4b --out ./audit-qwen3-ollama

Fail the command when the risk level is medium or above:

mdd ./downloaded-model --out ./audit --fail-on medium

Run a fast smoke scan without optional external tools:

mdd tests/fixtures/safe_repo \ --out ./audit-smoke \ --fail-on critical \ --skip-external

Generate only JSON output:

mdd ./downloaded-model \ --out ./audit-json \ --format json

CLI reference

usage: model-due-diligence [-h] [--out OUT] [--timeout TIMEOUT] [--format FORMATS] [--skip-external] [--skip-modelscan] [--skip-semgrep] [--skip-bandit] [--skip-pip-audit] [--skip-detect-secrets] [--skip-quality-self-check] [--quality-self-check] [--fail-on {low,medium,high,critical}] [--version] target

Argument Description

target Path to a model file or model directory

--out Output report directory

--timeout Per-tool timeout in seconds

--format Comma-separated report formats: markdown,json,sarif

--skip-external Skip all optional external scanner tools

--skip-modelscan Skip ModelScan only

--skip-semgrep Skip Semgrep only

--skip-bandit Skip Bandit only

--skip-pip-audit Skip pip-audit only

--skip-detect-secrets Skip detect-secrets only

--quality-self-check Run Ruff, Pyright and mypy against this project as optional self-checks

--skip-quality-self-check Skip quality self-checks

--fail-on Return non-zero when risk is at or above the selected level

--version Print package version

mdd-ollama

mdd-ollama resolves an installed Ollama model from the local OLLAMA_MODELS store, stages scan-friendly filenames in a temporary directory, and then runs the normal static due-diligence flow on that staged directory.

It does not require the Ollama server to be running as long as the local manifest and blob store is present.

usage: mdd-ollama [-h] [--ollama-models-dir OLLAMA_MODELS_DIR] [--out OUT] [--timeout TIMEOUT] [--format FORMATS] [--skip-external] [--skip-modelscan] [--skip-semgrep] [--skip-bandit] [--skip-pip-audit] [--skip-detect-secrets] [--skip-quality-self-check] [--quality-self-check] [--fail-on {low,medium,high,critical}] [--keep-staged] model

Typical usage:

mdd-ollama llama3:8b --out ./audit-llama3

For an uninstalled checkout, run it with:

PYTHONPATH=src python3 -m model_due_diligence.ollama_cli llama3:8b --out ./audit-llama3

Example workflows

Audit a Hugging Face clone

Use the helper script:

./examples/audit-huggingface-clone.sh \ https://huggingface.co/Qwen/Qwen3-8B-GGUF \ ./audit-qwen3

The script clones into a temporary directory, runs the scanner, writes reports to the output directory and removes the temporary clone afterwards.

Audit a local GGUF file

./examples/audit-local-gguf.sh \ ~/models/qwen3-

[truncated for AI cost control]