Modellskanning
En modellfil kan köra kod när den läses in. En PyTorch-checkpoint är en Python-pickle,
och att packa upp den kan importera och anropa vad som helst. Keras Lambda-lager,
chattmallar i GGUF och anpassade operatorer i ONNX är andra vägar in. fremforge skannar
modellfiler var de än hamnar i din organisation och sparar en dom per
fil. Organisationens policy avgör vad som händer med en fil som skanningen hittar.
Ingen modell läses någonsin in eller körs för att skannas. Skannern läser filens struktur: pickle-filens importlista, huvudena i safetensors och GGUF, ONNX-grafen och Keras-konfigurationen.
Var modellfiler hittas
| Yta | När den skannas | Vad Tvinga gör |
|---|---|---|
| Git LFS | Vid uppladdning | Nekar uppladdningen med 422 |
| Paket (generiska, och varje typ vars uppladdningsbody är filen) | Vid uppladdning | Nekar uppladdningen med 422 |
Container-registry (ModelPack, KitOps, Docker ai/ och modellfiler i vanliga images) | Efter pushen | Nekar en hämtning som skulle leverera lagret med 403 |
| Beroendeproxy (container-images) | När den hämtas första gången | Nekar senare hämtningar av lagret med 403 |
| Actions-artefakter | När körningen är klar | Ingenting. Artefakter får bara fynd |
En fil känns igen som en modell på sina första byte, inte på sitt namn. En pickle
som sparats som weights.txt hittas ändå.
Format och domar
| Format | Vad som kontrolleras |
|---|---|
Pickle, joblib, PyTorch (.pt, .pth, .bin, äldre och zip), NumPy-objektarrayer | Varje import i pickle-filen mot en tillåtelselista. Tillåtelselistan är det som PyTorchs torch.load(weights_only=True) accepterar, plus de NumPy-typer som en state dict behöver |
| safetensors | Huvudet: längder, datatyper och offset, och att filen inte också kan öppnas som zip eller pickle |
| GGUF | Metadata, chattmallen (mot en säker delmängd av Jinja) och samma zip- och pickle-kontroller |
| ONNX | Domäner för anpassade operatorer, och sökvägar för extern data som lämnar modellens katalog |
Keras (.keras, .h5) | Lambda-lager och anpassade objekt utanför Keras-namnrymden |
Varje fil får en dom, som delas av alla organisationer som har samma byte:
- clean (ren): ingenting hittades.
- Misstänkt (
suspicious): en import utanför tillåtelselistan (pickle-unknown-import), en klass från ett känt ML-ramverk (pickle-framework-object, se Ramverks-pickles), en konstruktion i en GGUF-mall utanför den säkra delmängden, en anpassad ONNX-operator, eller en fil som också kan öppnas som ett annat format. - Skadlig (
malicious): en import på skannerns blockeringslista (till exempelos.systemellerbuiltins.eval), eller ett KerasLambda-lager. - Kan inte skannas (
unscannable): filen kunde inte läsas till slutet: en trasig zip, ett 7z-arkiv, en avkortad ström. - format-verified (formatverifierad): en safetensors- eller GGUF-fil över 4 GiB vars strukturkontroll gick igenom. Se Över 4 GiB.
Skannern är öppen källkod: picklescan för genomgången av pickle-filen, med fremforges tillåtelselista ovanpå.
Organisationens policy
Policyn har tre lägen. Ställ in den under Säkerhet › Policyer › Modellskanning, eller med API:t.
| Läge | Vad som händer |
|---|---|
| Skugga (standard) | Varje fil skannas och de filer som policyn skulle blockera listas som fynd. Ingenting nekas. |
| Tvinga | Som skugga, och filerna med ett öppet fynd nekas, enligt listan under Var modellfiler hittas. |
| Av | Filerna skannas fortfarande och fynden listas fortfarande, men ingenting nekas eller räknas som skulle-blockeras. |
I läget Tvinga blockerar policyn en fil vars dom är Skadlig, Kan inte skannas eller Misstänkt, om inte fyndet har avfärdats. En fil som är clean eller format-verified går alltid igenom.
Hur Tvinga ser ut
En uppladdning som nekas svarar 422 med domen, fyndkoderna och
filens sha256:
{
"error": "model file rejected",
"rule": "suspicious",
"verdict": "suspicious",
"findings": ["pickle-unknown-import"],
"sha256": "d24ec017bdcf45a8737b4a9398e6eea64fa37a5821f473041cb3bc03f8489687"
}En fil som inte har skannats tidigare skannas under uppladdningen. Det lägger till högst några sekunder för en fil på upp till 256 MiB. En safetensors- eller GGUF-fil över 256 MiB kontrolleras via sitt huvud och sina sista 64 KiB. Alla andra modellfiler över 256 MiB accepteras och skannas direkt efter uppladdningen, så de dyker upp som ett fynd en minut senare i stället för att nekas.
Om skannern inte går att nå svarar en uppladdning av en modellfil i en organisation
som tvingar 503 med Retry-After: 30. Uppladdningar av andra filer
påverkas inte.
En container-hämtning som skulle leverera ett blockerat lager svarar 403 DENIED, med
orsaken model-malicious, model-suspicious eller model-unscannable och
lagrets digest. Ett modellager som du nyss har pushat skannas inom ungefär en
minut. Fram till dess svarar en hämtning 403 med orsaken model-scan-pending,
på samma sätt som för en image med ett lager som fortfarande skannas efter skadlig kod. Hämtningar
från runners som hostas av fremforge hålls inte tillbaka för en skanning som väntar.
Uploading failed due to unsupported Content-Type header(s), och
git lfs push --object-id avslutar med 0. En vanlig git push misslyckas däremot. Orsaken
finns i svarets body, och filen listas som ett fynd under
Modellskanning.Ramverks-pickles
Vissa checkpoints lagrar hela Python-objekt, inte bara vikter: en YOLO-modell, en
TrainingArguments-fil, en scikit-learn-pipeline. Att läsa in en sådan kör den klassens
kod. Skannern märker dessa pickle-framework-object när varje import
utanför vikternas tillåtelselista kommer från ett känt ramverk: PyTorch-moduler,
Transformers, Ultralytics, scikit-learn, joblib, timm, Lightning, SpeechBrain eller
Diffusers.
De är Misstänkt som alla andra okända importer. För att släppa igenom dem i läget Tvinga
slår du på Tillåt pickles från kända ML-ramverk (allow_framework_pickles). En
fil med något annat fynd blockeras fortfarande.
Avfärda ett fynd
En organisationsägare kan avfärda ett fynd för en fil (en sha256), med en motivering och ett utgångsdatum: 90 dagar som standard, högst 365. En avfärdad fil går igenom Tvinga och försvinner ur listan över fynd. Avfärdanden gäller per organisation; en annan organisation som har samma byte påverkas inte. Ett avfärdande kan återkallas när som helst.
Över 4 GiB
Skannern för skadlig kod, ClamAV, kan inte läsa mer än 4 GiB av en fil, och fremforge nekar en fil som det inte kan skanna. För modellvikter, som ofta är större än 4 GiB, finns ett undantag, i alla lägen: en safetensors- eller GGUF-fil vars strukturkontroll går igenom accepteras som format-verified. De här formaten innehåller bara tensorer och metadata, och strukturkontrollen bevisar att filen inte innehåller något annat. Filen skannas inte efter skadlig kod.
Alla andra filer över 4 GiB, inklusive alla pickles, nekas fortfarande. Gränsen för en fil är 10 GiB. Stora modeller publiceras som shards på 5 GB eller mindre, så det här är sällan den gräns som spelar roll.
Actions-artefakter
När en arbetsflödeskörning är klar kontrolleras de filer den laddade upp med
actions/upload-artifact efter modellfiler, och de skannas.
Ett fynd listas under Modellskanning med repot, körningens
nummer och artefakten. Artefakter nekas eller tas aldrig bort: att blockera en
uppladdning skulle få ditt arbetsflöde att misslyckas utan möjlighet att åsidosätta det.
AI-inventering
Säkerhet › Komponenter › AI-inventering listar de maskininlärningsmodeller som din organisation har och de som dess CI hämtar. Den är skrivskyddad; policyn och avfärdandena finns kvar på sidan för modellskanning.
Modellfiler
En rad per modellfil (per sha256) som hittats i din Git LFS, dina paket, container-images, hämtningar via beroendeproxyn och Actions-artefakter, med:
- dess format och storlek, och dess dom (pending medan den väntar på sin skanning);
- dess licens, så som den är angiven (se Var licenser kommer från), med var den angavs. En modell utan angiven licens visar Okänd. fremforge gissar inte en licens utifrån ett filnamn;
- vad dess huvud anger: antal parametrar, tensorernas datatyper, och för GGUF arkitekturen och modellnamnet;
- var den förekommer: varje repo, paket, image eller artefakt där den setts.
Varje modellfil finns också i komponentinventeringen
som pkg:generic/<file name>@<sha256>, så att en komponentsökning hittar den och
“var används den” fungerar även för modeller. En modell som din CI hämtar från Hugging
Face finns där som pkg:huggingface/<org>/<name>@<revision>, i det repo
vars arbetsflöde hämtar den.
Var licenser kommer från
fremforge läser en licens bara från metadata som redan finns lagrad på fremforge, och frågar ingen annan: ingenting om dina modeller lämnar plattformen. I ordning:
- Själva filen: GGUF
general.license, eller en licensnyckel i en safetensors-fils metadata. - Den modellartefakt som filen är ett lager i, när du pushar den till
organisationens registry eller hämtar den via
beroendeproxyn. fremforge läser
artefaktens små metadatablobbar från sin egen lagring, aldrig vikterna och
aldrig uppströms-registryt:
Paketering Läses ModelPack ( modctl)konfigurationens descriptor.licensesKitOps ( kit)Kitfile: en modelldels licenseför den delen, sedanmodel.license, sedanpackage.licenseDocker Model Runner ( docker model,ai/…)GGUF general.licensei modellkonfigurationen, annars licensfilen i artefakten (application/vnd.docker.ai.license): Apache-2.0 och MIT känns igen på sin text, alla andra licenser på sin titel, till exempelLLAMA 3.2 COMMUNITY LICENSE AGREEMENTvilken som helst av dem manifestannoteringen org.opencontainers.image.licenses
API:t returnerar var varje licens angavs som licence_from, till exempel
ModelPack config descriptor.licenses; AI-inventeringen visar det bredvid
licensen. En licens från en image som du har pushat visas bara för din
organisation. En modell som hämtades före den 13 oktober 2026 får sin licens från
artefakten inom en timme, när artefakten fortfarande finns lagrad.
Modeller som din CI hämtar
Den dagliga arbetsflödesskanningen läser varje arbetsflöde på varje repos standardbranch,
och de Python- och skalskript som ett arbetsflöde kör från samma
repo (python scripts/fetch.py, bash ci/prepare.sh). Den hittar:
| Verktyg | Hittas som |
|---|---|
| Hugging Face | from_pretrained("org/name"), hf_hub_download(repo_id=…), snapshot_download(…), huggingface-cli download, hf download |
| Ollama | ollama pull name:tag |
| Docker | docker model pull ref |
| KitOps | kit pull ref |
| ModelPack | modctl pull ref |
En referens är låst när den inte kan ändras under ditt bygge: en Hugging
Face-modell med en fullständig commit-sha på 40 tecken som revision
(revision="<sha>" eller --revision <sha>), och en Ollama-, Docker-, KitOps- eller
ModelPack-modell med en @sha256:-digest. En branch eller tagg som main eller
v1.0 är inte låst. Varje olåst referens är ett fynd, som en action
som låsts till en tagg i stället för en commit.
Ett modellnamn som inte står skrivet i filen, till exempel
from_pretrained(model_name), listas som Namn satt vid körning. En
HF_ENDPOINT som sätts i samma fil visas, eftersom hämtningen då går till den
endpointen i stället för huggingface.co. En referens som du tar bort försvinner inom
tre dagar.
Hittas inte: oras pull (en ORAS-artefakt är en modell bara genom sin mediatyp,
som kommandot inte visar; hämtad via beroendeproxyn klassificeras den
utifrån sitt manifest), och Python-kod utanför de skript som ett arbetsflöde
kör.
Licensen för en Hugging Face-modell som din CI hämtar via namn visas som Okänd: fremforge kontaktar inte Hugging Face, eller någon annan, för att slå upp den. En modell som din CI hämtar som OCI-artefakt via beroendeproxyn får sin licens från artefakten (se Var licenser kommer från).
ML-BOM
Ladda ned ML-BOM (CycloneDX) på sidan, eller GET /ai/ml-bom, returnerar
inventeringen som ett CycloneDX 1.6-dokument. Varje modellfil är en komponent av typen
machine-learning-model med sin sha256, sin angivna licens (var den
angavs är egenskapen fremforge:model:licence_from), sin
arkitektur som modelCard.modelParameters.modelArchitecture när huvudet
anger en, och sitt format, sin dom, sina ytor, sin storlek, sitt antal parametrar och sina datatyper
som egenskaper fremforge:model:*. Bara fakta som lästs från filen
anges.
De SBOM:er som din CI laddar upp ändras inte: de är dina filer. ML-BOM:en är ett separat dokument.
Compliancerapport
Compliancerapporten har ett avsnitt AI-inventering: antalet modellfiler, de filer som din policy blockerar eller skulle blockera, filerna med ett avfärdat fynd, ditt läge för modellskanning, de modellhämtningar i CI som saknar fast revision, och filerna per angiven licens. Dess CSV listar varje modellfil. Avsnittet omfattar alltid hela organisationen, även i en rapport för ett enda repo.
Granskningslogg
| Händelse | När |
|---|---|
security_policy.model_scan.updated | Läget eller inställningen för ramverks-pickles ändrades |
security_policy.model_scan.dismissed | Ett fynd avfärdades, eller ett avfärdande förnyades |
security_policy.model_scan.dismissal_revoked | Ett avfärdande återkallades |
registry.pull.blocked med gate: model | En hämtning nekades av modellpolicyn |
En nekad paketuppladdning eller container-hämtning finns också i
åtkomstloggen för registry, med orsaken model_policy:<rule>.
Åtkomstloggen omfattar inte Git LFS.
API
| Anrop | Scope |
|---|---|
GET /_app/api/v1/orgs/{org}/security/model-policy | policy:read |
PUT /_app/api/v1/orgs/{org}/security/model-policy | policy:write |
GET /_app/api/v1/orgs/{org}/security/model-findings?status=open|dismissed|all | findings:read |
POST /_app/api/v1/orgs/{org}/security/model-findings/{sha256}/dismiss | policy:write |
DELETE /_app/api/v1/orgs/{org}/security/model-findings/{sha256}/dismissal | policy:write |
GET /_app/api/v1/orgs/{org}/ai/models?verdict=&format=&licence=&surface= | components:read |
GET /_app/api/v1/orgs/{org}/ai/models/{sha256} | components:read |
GET /_app/api/v1/orgs/{org}/ai/ml-bom | components:read |
GET /_app/api/v1/orgs/{org}/ai/references?tool=&unpinned=true&repository=&model= | components:read |
PUT ersätter hela policyn: ett fält som du utelämnar får sitt standardvärde.
curl -X PUT \
-H "Authorization: Bearer ${FREMFORGE_PAT}" \
-H "Content-Type: application/json" \
-d '{"mode":"enforce","allow_framework_pickles":false}' \
https://frem.sh/_app/api/v1/orgs/acme/security/model-policyAtt avfärda kräver policy:write, inte findings:write, eftersom en avfärdad fil
går igenom Tvinga. Ett CI-jobbs runner-token har aldrig det scopet.
curl -X POST \
-H "Authorization: Bearer ${FREMFORGE_PAT}" \
-H "Content-Type: application/json" \
-d '{"reason":"Our own YOLO checkpoint, built in CI","expires_at":"2027-01-31"}' \
https://frem.sh/_app/api/v1/orgs/acme/security/model-findings/<sha256>/dismissSe API-referensen för svarens form.
Vad som inte omfattas
- Filer som bifogats till issues, pull requests och releaser, och paketuppladdningar vars body innehåller mer än filen (PyPI, NuGet och andra multipart-uppladdningar). Dessa skannas efter skadlig kod som tidigare.
- Modeller i dina repons vanliga Git-objekt. Stora modellfiler hör hemma i Git LFS.
- Att läsa in eller köra en modell, och att hitta en bakdörr i dess vikter. Ingen skanner kan skilja en förgiftad vikt från en normal.