| <!doctype html> |
| <html lang="en"> |
| <head> |
| <meta charset="utf-8" /> |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> |
| <title>modelvet — vet a Hugging Face model before you trust it</title> |
| <style> |
| :root { |
| color-scheme: light dark; |
| --bg: #0f1115; |
| --panel: #171a21; |
| --border: #2a2f3a; |
| --text: #e7e9ee; |
| --muted: #9aa3b2; |
| --accent: #ffb020; |
| --ok: #4fd18b; |
| --bad: #ff6b6b; |
| } |
| @media (prefers-color-scheme: light) { |
| :root { |
| --bg: #f7f7f9; |
| --panel: #ffffff; |
| --border: #e2e5ea; |
| --text: #14161a; |
| --muted: #5b6270; |
| --accent: #b5720a; |
| --ok: #1a8a53; |
| --bad: #c8321f; |
| } |
| } |
| * { box-sizing: border-box; } |
| body { |
| margin: 0; |
| background: var(--bg); |
| color: var(--text); |
| font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; |
| line-height: 1.55; |
| } |
| .wrap { |
| max-width: 820px; |
| margin: 0 auto; |
| padding: 48px 20px 80px; |
| } |
| header h1 { |
| font-size: 2rem; |
| margin: 0 0 4px; |
| letter-spacing: -0.02em; |
| } |
| header p.tagline { |
| color: var(--muted); |
| font-size: 1.1rem; |
| margin: 0 0 28px; |
| } |
| .badges { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 32px; } |
| .badge { |
| font-size: 0.78rem; |
| padding: 3px 10px; |
| border-radius: 999px; |
| border: 1px solid var(--border); |
| color: var(--muted); |
| } |
| section { margin-bottom: 36px; } |
| h2 { |
| font-size: 1.05rem; |
| text-transform: uppercase; |
| letter-spacing: 0.06em; |
| color: var(--muted); |
| margin: 0 0 14px; |
| } |
| p { margin: 0 0 12px; } |
| a { color: var(--accent); } |
| ul { margin: 0 0 12px; padding-left: 22px; } |
| li { margin-bottom: 6px; } |
| code { |
| background: var(--panel); |
| border: 1px solid var(--border); |
| border-radius: 4px; |
| padding: 1px 6px; |
| font-size: 0.92em; |
| } |
| pre { |
| background: var(--panel); |
| border: 1px solid var(--border); |
| border-radius: 8px; |
| padding: 16px; |
| overflow-x: auto; |
| font-size: 0.85rem; |
| } |
| pre code { background: none; border: none; padding: 0; } |
| .panel { |
| background: var(--panel); |
| border: 1px solid var(--border); |
| border-radius: 10px; |
| padding: 20px 22px; |
| } |
| .note { |
| font-size: 0.88rem; |
| color: var(--muted); |
| border-left: 3px solid var(--accent); |
| padding: 4px 0 4px 14px; |
| margin: 16px 0; |
| } |
| .example-row { |
| display: flex; |
| justify-content: space-between; |
| gap: 12px; |
| padding: 5px 0; |
| border-bottom: 1px solid var(--border); |
| font-size: 0.85rem; |
| } |
| .example-row:last-child { border-bottom: none; } |
| .ok { color: var(--ok); } |
| .tool { color: var(--muted); } |
| footer { |
| margin-top: 48px; |
| padding-top: 20px; |
| border-top: 1px solid var(--border); |
| color: var(--muted); |
| font-size: 0.85rem; |
| } |
| </style> |
| </head> |
| <body> |
| <div class="wrap"> |
|
|
| <header> |
| <h1>modelvet</h1> |
| <p class="tagline">Vet a Hugging Face model before you trust it.</p> |
| <div class="badges"> |
| <span class="badge">MIT licensed</span> |
| <span class="badge">CLI, stdlib + picklescan + model-signing</span> |
| <span class="badge">v1</span> |
| </div> |
| </header> |
|
|
| <section> |
| <h2>Why</h2> |
| <p> |
| Malicious PyTorch checkpoints have repeatedly bypassed <code>picklescan</code> |
| (Hugging Face's own scanning dependency) via documented, CVE-tracked techniques — |
| CVE-2025-1889, CVE-2025-10155, CVE-2026-53875, and more. |
| </p> |
| <p> |
| Hugging Face's website shows scan-result badges, but <code>huggingface_hub</code>'s |
| <code>hf_hub_download()</code> / <code>snapshot_download()</code> have no API to check |
| "was this scanned, and did it pass" from a script. A developer or agent downloading a |
| model programmatically never sees that signal at all. <code>modelvet</code> closes that |
| gap: it re-scans what you actually downloaded, and checks for a |
| Sigstore / OpenSSF Model Signing signature (real, working, but essentially unadopted |
| on Hugging Face today). |
| </p> |
| </section> |
|
|
| <section> |
| <h2>Install & use</h2> |
| <div class="panel"> |
| <p>Not on PyPI yet — install straight from the source repo:</p> |
| <pre><code>pip install git+https://github.com/sangmorg1-debug/modelvet.git |
|
|
| modelvet scan bert-base-uncased |
| modelvet scan bert-base-uncased --format json |
| modelvet scan local-check --local-dir ./downloaded-model</code></pre> |
| </div> |
| </section> |
|
|
| <section> |
| <h2>Example scan (captured output, not a live demo)</h2> |
| <div class="note"> |
| This Space is static — Hugging Face's free tier no longer includes compute for |
| Gradio or Docker Spaces, only static pages. So this isn't an interactive scanner; |
| it's a real, captured run of <code>modelvet scan hf-internal-testing/tiny-random-gpt2</code> |
| against an actual public model, shown as-is. Install the CLI locally to scan a real repo |
| of your own. |
| </div> |
| <div class="panel"> |
| <div class="example-row"><span>.gitattributes</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>config.json</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>merges.txt</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>model.safetensors</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>pytorch_model.bin</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>special_tokens_map.json</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>tf_model.h5</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>tokenizer.json</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>tokenizer_config.json</span><span class="tool ok">picklescan · clean</span></div> |
| <div class="example-row"><span>vocab.json</span><span class="tool ok">picklescan · clean</span></div> |
| <p style="margin-top:14px;"><strong>signature:</strong> <code>no_signature</code> — expected; this repo, like almost every repo on Hugging Face today, has never published one.</p> |
| <p><strong>exit code:</strong> <code>0</code></p> |
| </div> |
| </section> |
|
|
| <section> |
| <h2>What it does not do (yet)</h2> |
| <ul> |
| <li>No <code>transformers.from_pretrained()</code> wrapper — CLI only in v1.</li> |
| <li>No custom pickle-opcode engine — detection is built on <code>picklescan</code> (an actively maintained, if imperfect, tool), with two narrowly-scoped checks added for its documented bypasses (archive-format mismatch, hidden pickles under non-pickle extensions).</li> |
| <li>Signature verification only works against <code>--local-dir</code> today, not a bare Hub repo id — Hugging Face's cache layout gives no place to put a signature file for a model you didn't download and sign yourself.</li> |
| </ul> |
| </section> |
|
|
| <footer> |
| <p> |
| <a href="https://github.com/sangmorg1-debug/modelvet">Source on GitHub</a> · |
| MIT License |
| </p> |
| </footer> |
|
|
| </div> |
| </body> |
| </html> |
|
|