How it works
- Upload a file (.gguf, .onnx, .safetensors) or point us at a Hugging Face repo/file path — no model execution, ever.
- We stream the upload, compute SHA256/SHA512, and parse the file's actual structure (header, tensor table, graph) safely.
- We run format-specific checks across four areas (below), each producing findings with a severity.
- Findings roll up into a security grade + score, plus (for Hugging Face scans) a Repository Trust score and an overall Deployment Confidence rating.
- You get a readable HTML report and a machine-readable
.llmscanJSON — same data, two formats.
What gets checked, in four areas
Every scan runs checks across these four areas — not every check applies to every format (see Supported formats for exact per-format coverage).
| Area | What it covers |
|---|---|
| Artifact integrity | SHA256/512, magic bytes and version, tensor/header offsets, overlapping or out-of-bounds tensor data, alignment. |
| Architecture & compatibility | Quantization type, opset/graph structure, custom operators and domains, and realistic runtime-compatibility notes (llama.cpp, ONNX Runtime, TensorRT, OpenVINO, CoreML, vLLM). |
| Supply-chain & provenance | Embedded license/metadata presence, SPDX normalization, injection-pattern detection in chat templates and string metadata, external-data reference safety (path traversal, missing/OOB refs), Hugging Face repo-level provenance when scanning from a repo URL. |
| Deployment readiness | The three scores described below — Artifact Security, Repository Trust, Deployment Confidence. |
Scoring: grade, score, and the three-score block
Artifact Security is a letter grade (A–F) plus a 0–100 score, computed only from that file's own findings:
- Start at 100, subtract a fixed penalty per finding severity: CRITICAL −40, HIGH −15, MEDIUM −8, LOW −3 (INFO findings don't affect the score).
- The score maps to a letter grade: A ≥ 92, B ≥ 75, C ≥ 52, D ≥ 30, otherwise F.
- Hard caps apply on top of the score-based grade — e.g. 3+ CRITICAL findings force an F regardless of score, 1 CRITICAL caps at D, 4+ HIGH caps at C. A high score can't buy back a genuinely dangerous finding.
Repository Trust (Hugging Face scans only) reflects repo-level signals — license presence, README, popularity/provenance metadata. It's N/A for local file uploads, since there's no external repo to verify against.
Deployment Confidence combines the two: High/Medium/Low when both scores are available, or based on Artifact Security alone when Repository Trust is N/A. If the scan hit a real limitation (e.g. a multi-shard GGUF where only one shard was available), it's marked Incomplete instead — we'd rather say "we couldn't fully check this" than hand you a confident-looking grade built on partial data.
CVE Advisory: what it actually means
We currently match one CVE category: known onnxruntime CVEs, checked against the operators actually present in an ONNX model's graph. A CVE Advisory finding means "this graph uses ops affected by CVE-X; the runtime CVE is unsafe below onnxruntime vX.Y" — it is not a claim that the model file itself is malicious or compromised. See the FAQ for more on this distinction.
Compare Models
Scanning tells you a file is well-formed and free of known risky patterns — it doesn't tell you whether two files with the same architecture have identical weights. Compare Models (requires a free account) classifies and localizes model changes at the tensor level — whether a model was fine-tuned, structurally modified, or locally edited. It infers the pattern of a change from the tensor diff; it cannot prove which historical process (a specific fine-tuning run, an editing algorithm, or something else) actually produced a file. Under the hood, it diffs two GGUF files tensor-by-tensor:
- A SHA-256 hash per tensor — if it matches, the tensor is reported identical, no further computation.
- For plain float tensors (F32/F16/BF16/F64, dequantized to float32 first):
l2_diff = ‖B − A‖₂over all elements,l2_pct = l2_diff / ‖A‖₂ × 100(relative to the reference tensor's own norm), andrms_diff = l2_diff / √N— pluschanged_elements/changed_elements_pct(positions where A ≠ B exactly), andmax_abs_diff/mean_abs_diff. L2 alone can't distinguish "a few large changes" from "many small ones" — the changed-element count and max/mean can. Tensors are ranked by relative L2, not absolute L2: absolute L2 scales with tensor size, so a huge embedding table with only tiny per-element noise would otherwise outrank a small tensor with a much larger relative change. Each differing tensor's report also expands to showp50/p95/p99of the absolute difference, cosine similarity between the two tensors, a NaN/Inf-introduced flag, and an 8-bucket histogram of the difference distribution — useful for telling a dense broad shift (most elements moved a little) apart from a sparse spike (a few elements moved a lot). - For quantized tensors (Q4_K, IQ-series, etc.): a byte-level diff percentage, since a numeric norm on raw quantized bytes isn't meaningful. This is reported separately and isn't comparable to the L2 numbers above.
Each report also gets a heuristic change classification — IDENTICAL, METADATA_ONLY, LOCALIZED_WEIGHT_EDIT, FINE_TUNED, ARCHITECTURE_MODIFIED, or INCOMPATIBLE — based on how many tensors differ and how concentrated the change is across transformer blocks, plus a plain-language explanation. Each classification is split into an observed pattern (what the diff directly measures — high confidence) and a likely origin (an inference about what process produced it, always phrased as "consistent with", capped at medium confidence, never a claim of proof). A global change summary aggregates the per-tensor stats across the whole file — total tensors changed, share of individual weight values that differ, median/max relative L2, and whether any NaN/Inf values were introduced — and a block map shows which transformer blocks (and which non-block tensors) were touched at a glance.
Findings vs. Suspicions
A "finding" is a concrete, verifiable property (e.g. an external data reference, a missing SPDX-normalized license). A "suspicion" is a heuristic pattern that warrants human review rather than a confirmed fact — both appear in the report, labeled accordingly.
The .llmscan file
Every report is also downloadable as a .llmscan JSON file — the exact same data behind the HTML report, meant for CI/CD or programmatic consumption. Top-level fields include format_version (report schema version, currently 1.0), generator.version (scanner engine version, currently 2.0.0), source, model, security (grade/score/findings), and scan_context (timing, limits).
Safety
Scanning is static and sandboxed; we never execute code from a model file, load it into a runtime, or run inference.