Pakketbeleid
Een pakketbeleid bepaalt per bestand of een pakket in het register van je organisatie mag worden gedownload. Het dekt de registertypen die fremforge host: npm, Maven, PyPI, NuGet, Cargo, Go, Conda, CRAN, Debian, RPM, Helm en generieke pakketten, en containerimages (zie Containerimages). Elke organisatie heeft één beleid, met optionele instellingen per pakkettype.
Wat een beleid controleert
Als een bestand wordt gepubliceerd, pakt fremforge het uit, somt het de componenten op die erin zijn gebundeld en leest het de licentie die het pakket zelf declareert (zie SBOM’s). Elk component wordt vergeleken met de advisories van OSV, en dat wordt elke dag opnieuw gedaan. Het beleid wordt geëvalueerd tegen die resultaten en het oordeel wordt bij het bestand opgeslagen: allow, warn of block. Een wijziging aan het beleid, een lijst of een uitzondering evalueert elk bestand meteen opnieuw, zonder nieuwe scan.
Bij het downloaden beslist het opgeslagen oordeel. Dat gebeurt voor downloads
via de register-API (/api/packages/…, die elke pakketbeheerder gebruikt) en
via de webinterface. Pakketmetadata, zoals een npm-packument of een simple
index van PyPI, wordt nooit gefilterd. Zodra afdwingen aanstaat, krijgt een
resolver die om een geblokkeerde versie vraagt 403 voor dat bestand; hij wordt
niet naar een andere versie gestuurd.
Kwetsbaarheden
| Instelling | Betekenis |
|---|---|
vuln_min_severity | Blokkeer een bestand met een openstaand advisory van deze ernst of hoger: low, medium, high of critical. |
vuln_min_cvss | Blokkeer vanaf deze CVSS-basisscore (0–10). De score wordt berekend uit de CVSS-vector van het advisory. |
vuln_require_fix_available | Tel alleen advisories mee die een gefixte versie hebben. |
vuln_min_epss | Tel alleen advisories mee waarvan de EPSS-kans minstens deze waarde is (0–1). Een advisory zonder EPSS-score telt niet mee. |
De kwetsbaarheidsregel staat alleen aan als er een drempel voor ernst of CVSS is ingesteld. Zijn beide ingesteld, dan telt een advisory mee dat een van de twee haalt.
Licenties
Licenties zijn SPDX-identifiers, gecontroleerd tegen zowel de gedeclareerde licentie als de licenties die in gebundelde componenten worden gevonden.
De licentielijsten worden ingesteld op de pagina licentiebeleid,
één set regels voor pull requests en downloads. De pagina Pakketbeleid linkt
daarheen. De velden hieronder zijn de downloadkant van dat beleid, en
PUT /orgs/{org}/package-policy accepteert ze nog steeds.
| Instelling | Betekenis |
|---|---|
licence_allow | Als deze niet leeg is, zijn alleen deze licenties toegestaan. |
licence_deny | Deze licenties blokkeren. |
licence_warn | Deze licenties geven een waarschuwing. |
licence_unknown_action | Wat er gebeurt als een licentie geen geldige SPDX-expressie is of ontbreekt: allow, warn of block. |
licence_multiple_mode | Voor een expressie met meerdere licenties (MIT OR GPL-3.0-only): any_allowed slaagt als één ervan is toegestaan, all_allowed vereist ze allemaal. |
Expressies worden per operand geëvalueerd. OR slaagt als een operand is
toegestaan, AND vereist elke operand, en X WITH exception wordt
geëvalueerd als X.
Toestaan- en weigerlijsten
Een lijstregel noemt een pakkettype (of elk type), een naampatroon (met de
wildcards * en ?, bijvoorbeeld @acme/* of org.apache.logging.log4j:log4j-core)
en een optioneel versiebereik in vers:-syntaxis,
bijvoorbeeld vers:maven/<2.17.1. Bereiken volgen de eigen versieregels van
elk ecosysteem voor npm, Cargo en Go (semver), PyPI (PEP 440), Maven en NuGet.
match_target: packagematcht het pakket dat je hebt gepubliceerd.match_target: componentmatcht een component dat erin is gebundeld.
Een deny-regel blokkeert. Een allow-regel op een pakket laat het langs de regels voor kwetsbaarheden, licenties en leeftijd, en een allow-regel op een component stelt dat component vrij van de regels voor kwetsbaarheden en licenties. Allow-regels gaan nooit boven bekende malware of een deny-regel. Een regel kan een vervaldatum hebben.
Minimumleeftijd
min_age_hours houdt bestanden tegen die minder dan dit aantal uur geleden zijn
gepubliceerd, optioneel alleen voor de typen in age_types. De leeftijd wordt
bepaald op het moment van downloaden, dus een bestand wordt downloadbaar zodra
het oud genoeg is. Als je de releaseleeftijd
van de organisatie opslaat, wordt deze ingesteld op dat aantal dagen (0 zet
haar uit); een type met een eigen minimumleeftijd in type_overrides houdt die.
Een versie die een bekend misbruikte kwetsbaarheid oplost (een CVE in de EU Vulnerability Database van ENISA of de KEV-catalogus van CISA) wordt niet tegengehouden: een fix voor een kwetsbaarheid die in het wild wordt misbruikt, hoort niet te wachten. De leeftijdsregel blijft wel zichtbaar in de beslissing, gemarkeerd als vrijgesteld met de CVE waarvoor dat gebeurde, zodat de vrijstelling te zien is in het oordeel, de auditlog en SIEM. Een fix voor een CVE waarvan niet bekend is dat hij wordt misbruikt, wacht zoals gewoonlijk.
Altijd aan
Twee controles draaien wat het beleid ook zegt, zelfs met de modus op Uit:
- Bekende malware. Een bestand dat een pakket bevat dat in een OSV-advisory
MAL-*staat, wordt geweigerd met403, in elke modus, ook Schaduw en Uit. Om één bestand door te laten, is een goedgekeurde uitzondering voor de regelmalwarenodig. Zie kwaadaardige pakketten. - Naamconflict. Een advisory dat de naam en versie van je eigen pakket noemt, meestal omdat een openbaar pakket dezelfde naam heeft, geeft een waarschuwing. Het blokkeert nooit.
Containerimages
Een containerimage wordt per manifestdigest geëvalueerd, tegen dezelfde regels, met de eigen gegevens van het image:
- Kwetsbaarheden zijn de CVE-bevindingen van het scannen van
containerimages voor die digest (of de tag ervan),
met hun ernst en CVSS-score. Imagebevindingen hebben geen EPSS-score, dus een
beleid met
vuln_min_epsstelt ze niet mee. Een afgewezen bevinding telt niet mee. - Malware is een openstaande bevinding voor een kwaadaardig pakket op die digest.
- Licenties zijn de licenties die de imagescan rapporteert voor de pakketten in het image. Een pakket zonder licentie telt als onbekend.
- Toestaan- en weigerlijsten matchen het image als type
container(naam en tag) en de pakketten erin als componenten. - Minimumleeftijd telt vanaf het moment dat de tag is gepusht, als
containerinage_typesstaat (ofage_typesleeg is).
Het image wordt gecontroleerd als het wordt gepulld (docker pull, of elke
client die /v2/…/manifests/… leest), voor elk manifest dat de pull levert,
inclusief elk platform van een multiplatform-image. Images staan alleen in
schaduwmodus: een pull die het beleid overtreedt, wordt vastgelegd onder
Zou geblokkeerd hebben en wordt nooit geweigerd, welke modus je ook kiest.
Imagebevindingen worden minstens elke 12 uur opnieuw geëvalueerd, en meteen als
je het beleid, een lijst of een uitzondering wijzigt.
Dit staat los van de bestaande blokkades voor het pullen van images, die
ongewijzigd blijven: de instelling block_pull van het scannen van
containerimages en de blokkade voor kwaadaardige
pakketten weigeren nu al pulls.
Handtekeningen
signature_requirement controleert de handtekeningen van npm-pakketten die je
organisatie via de dependency proxy van fremforge van npmjs.org ophaalt, zodra
de dependency proxy voor je organisatie beschikbaar is, voordat de tarball wordt
geleverd:
- de registerhandtekening die npmjs.org over elke versie zet
(
name@version:integrity), geverifieerd tegen de gepubliceerde registersleutels van npm; - de build-provenance, als de versie die heeft: een met Sigstore ondertekende SLSA-attestatie, offline geverifieerd tegen de openbare trust root van Sigstore en gebonden aan de SHA-512 van de tarball.
| Instelling | Betekenis |
|---|---|
off | Niet gecontroleerd. De standaard. |
if_present | Een aanwezige handtekening of provenance moet kloppen. Een versie zonder slaagt. |
require | Als if_present, en een versie moet een geldige handtekening hebben (of, met vermelde ondertekenaars, vertrouwde provenance). |
signature_identities somt de build-identiteiten op die je vertrouwt, elk een
OIDC-issuer en een subject_regex waaraan het hele certificaatsubject moet
voldoen. Staat er een in de lijst, dan telt alleen provenance van een van hen;
een registerhandtekening alleen telt dan niet, want die bewijst wat npmjs.org
heeft geleverd, niet wie het heeft gebouwd. Voor pakketten die door GitHub
Actions in één organisatie zijn gebouwd:
"signature_requirement": "require",
"signature_identities": [
{ "issuer": "https://token.actions.githubusercontent.com",
"subject_regex": "https://github\\.com/sigstore/.+" }
]Net als elke regel volgt deze de modus van het beleid. Een versie die
niet slaagt, wordt vastgelegd met de regel signature en een reden zoals
no provenance from a trusted identity of the registry signature does not verify. Een goedgekeurde uitzondering met de regel signature
stelt haar vrij.
Wat niet wordt gecontroleerd: pakketten die naar frem.sh zijn gepubliceerd
(het register bewaart daarvoor geen npm-handtekening of provenance, dus de regel
geldt er helemaal niet voor), en elk ander pakkettype. Maven-handtekeningen in
.asc en NuGet-handtekeningen worden niet geverifieerd.
Verzwakte provenance
provenance_downgrade vangt een versie waarvan de build-provenance zwakker is
dan die van de versie ervoor. Dat is het typische spoor van een release die
vanaf een laptop of met een gestolen token is gepubliceerd, terwijl de eerdere
releases uit CI kwamen. De regel gebruikt dezelfde geverifieerde feiten als
handtekeningen, dus hij geldt voor npm-pakketten die via de
dependency proxy van npmjs.org worden opgehaald.
De versie ervoor is de hoogste lagere versie die eerder is gepubliceerd. Een backport op een oudere lijn, zoals een 4.5.x-beveiligingsfix die na 5.4.x is uitgebracht, wordt dus vergeleken met de vorige release van die lijn, niet met de nieuwste. Oudere lijnen worden vaak nog zonder provenance gebouwd.
Een versie is een verzwakking als de versie ervoor build-provenance had, en deze versie:
- geen provenance heeft;
- provenance heeft die niet klopt; of
- door een andere ondertekenaar is gebouwd: een andere OIDC-issuer, bronrepository of buildworkflow, of een self-hosted runner waar de vorige versie op een gehoste draaide. Deze worden gelezen uit het geverifieerde Sigstore-certificaat. Een nieuwe tag van dezelfde workflow is dezelfde ondertekenaar.
De eerste versie van een pakket, en een versie waarvan de voorganger geen
provenance had, slagen altijd. Deze regel vergelijkt versies. Om provenance op
elke versie te vereisen, gebruik je signature_requirement.
| Instelling | Betekenis |
|---|---|
off | Niet gecontroleerd. De standaard. |
warn | Een verzwakking wordt vastgelegd als waarschuwing; de versie wordt geleverd. |
block | Een verzwakking blokkeert de versie. Zoals bij elke regel beslist de modus van het beleid: enforce weigert de download, shadow legt hem alleen vast. |
"provenance_downgrade": "block"In de console staat dit onder Beleid › Pakketbeleid › Handtekeningen. Met
Terraform is het het argument provenance_downgrade van
fremforge_package_signature_policy. Het kan ook voor één pakkettype worden
ingesteld in type_overrides.
Een versie die niet slaagt, wordt vastgelegd met de regel provenance_downgrade
en een reden die de vorige versie noemt, bijvoorbeeld
2.4.1 has no build provenance, but the previous version 2.4.0 had of
... built by a different signer than the previous version 2.4.0: source repository https://github.com/acme/lib -> https://github.com/fork/lib. Een
goedgekeurde uitzondering met de regel signature en de selector
provenance_downgrade stelt alleen deze regel vrij. Een uitzondering met de
regel signature en zonder selector stelt beide handtekeningregels vrij.
Waar de regel nog niet geldt:
| Ecosysteem | Waarom |
|---|---|
| PyPI | PyPI publiceert PEP 740-attestaties, maar fremforge verifieert ze nog niet. |
| Maven Central, RubyGems | Ondertekenen met Sigstore is upstream optioneel en wordt niet door de proxy geverifieerd. |
| Cargo, Go, NuGet, Composer, Helm, conda, Debian, RPM, Terraform-modules | Upstream is er geen build-provenance per versie. Handtekeningen van repository’s of checksumdatabases bewijzen integriteit, niet wie een versie heeft gebouwd. |
| Pakketten die naar frem.sh zijn gepubliceerd | Daarvoor wordt geen provenance vastgelegd. |
De lockfilecontrole van de proxy en het filteren van pakketmetadata passen de regel ook niet toe; hij wordt beoordeeld als een tarball wordt gedownload.
Instellingen per type
type_overrides stelt elk van de regels hierboven anders in voor één
pakkettype, bijvoorbeeld een strengere licentielijst voor npm.
"enabled": false zet het beleid uit voor dat type, behalve de malwarecontrole.
Modi
| Modus | Wat er gebeurt |
|---|---|
off | Alleen bekende malware en naamconflicten worden gecontroleerd. |
shadow | Elke regel wordt geëvalueerd. Niets wordt geweigerd, en elke download die geweigerd zou zijn, wordt vastgelegd. Dit is de standaard. |
enforce | Een download die het beleid overtreedt, wordt geweigerd met 403. Het antwoord noemt de regels die van toepassing waren en linkt naar de beleidspagina. |
pending_action bepaalt wat er gebeurt met een bestand waarvan de evaluatie nog
niet klaar is, bijvoorbeeld een bestand dat enkele seconden geleden is
gepubliceerd. allow (de standaard) laat het door. refuse antwoordt 503 met
Retry-After, alleen in de modus enforce.
Uitzonderingen
Een uitzondering laat een pakket voor beperkte tijd langs één regel. Er is een tweede persoon voor nodig: de aanvrager kan zijn eigen aanvraag niet goedkeuren, en de goedkeurder moet een eigenaar van de organisatie zijn of op de goedkeurderslijst van de organisatie staan.
Een uitzondering heeft:
- een bereik: één bestand, een versiebereik van een pakket, elke versie van een pakket, of één gebundeld component (via purl);
- een regel:
vuln,licence,age,deny_list,name_clashofmalware, optioneel beperkt tot één advisory-ID of één SPDX-licentie; - een onderbouwing;
- een vervaldatum van hoogstens 366 dagen vooruit. Die is verplicht.
Een uitzondering wordt aangevraagd en daarna goedgekeurd of afgewezen. Een
goedgekeurde uitzondering kan op elk moment worden ingetrokken, en wordt
automatisch ingetrokken als ze verloopt. Elke stap wordt vastgelegd in de
auditlog (package_policy.exception.*).
Uitzonderingen op het pakketbeleid delen één wachtrij, API en instellingen met uitzonderingen voor bevindingen: zie Uitzonderingen, waar ook het goedkeurdersteam, de maximale looptijd en het automatisch intrekken worden beschreven.
De weergave “Zou geblokkeerd hebben”
Open de beheerpagina’s van je organisatie onder Beveiliging › Beleid › Pakketbeleid. De pagina heeft vier weergaven:
- Beleid stelt de modus en de regels in, en toont de huidige oordelen over je gepubliceerde bestanden. Om te zien wat nieuwe instellingen zouden blokkeren voordat je ze opslaat, gebruik je de proefrun in de API (hieronder).
- Toestaan- en weigerlijsten bevat de lijstregels.
- Uitzonderingen toont aanvragen, de goedkeuringswachtrij en de geschiedenis. Bij je eigen aanvragen staat geen knop Goedkeuren.
- Zou geblokkeerd hebben somt de downloads op die het beleid in de afgelopen 14 dagen heeft geweigerd, of in schaduwmodus zou hebben geweigerd: één rij per bestand, regel, gebruiker en dag, met het aantal downloads. Dit is het bewijs om te bekijken voordat je een organisatie op afdwingen zet.
Downloads die zo zijn geweigerd of vastgelegd, staan ook in de
toegangslog van het register met de beslissing
would_block of blocked.
Wat er veranderde toen afdwingen begon
Op 2026-10-11:
- De modus Afdwingen weigert. Daarvoor legde Afdwingen dezelfde resultaten vast als Schaduw. Stond je beleid al op Afdwingen, dan worden downloads die het overtreden nu geweigerd. Bekijk eerst Zou geblokkeerd hebben, of schakel over naar Schaduw totdat je dat hebt gedaan.
- Bekende malware wordt voor iedereen geweigerd. Daarvoor werd die alleen vastgelegd.
- Je resultaten worden getoond. Oordelen over bestanden, Zou geblokkeerd hebben, pakketbevindingen en de API’s voor evaluatie, proefrun, beslissingen en bevindingen antwoorden nu voor je organisatie. Vanaf dat moment leveren pakketbevindingen en beleidsbeslissingen ook gebeurtenissen in de auditlog op (en dus SIEM- en webhookgebeurtenissen), e-mails met beveiligingsmeldingen, regels in de wekelijkse samenvatting en rijen op het tabblad Malware, voor wijzigingen vanaf die dag. Zou geblokkeerd hebben somt ook de downloads op die in de 14 dagen daarvoor zijn vastgelegd.
- Containerimages zijn ongewijzigd: nog steeds alleen schaduw, zie Containerimages.
API
Gebruik een token met policy:read of policy:write (findings:read voor
pakketbevindingen). Basis-URL: https://frem.sh/_app/api/v1. Zie de
API-referentie voor de volledige vorm van verzoeken en
antwoorden.
| Methode en pad | Wat het doet |
|---|---|
GET /orgs/{org}/package-policy | Het beleid, met zijn version. |
PUT /orgs/{org}/package-policy | Het beleid wijzigen. Stuur de version mee die je hebt gelezen; een verouderde versie krijgt 409 policy-version-conflict. |
GET, POST /orgs/{org}/package-policy/rules | Allow- en deny-regels opvragen of toevoegen. Een regel heeft list, name_glob en reason nodig. |
DELETE /orgs/{org}/package-policy/rules/{id} | Een regel verwijderen. |
GET, PUT /orgs/{org}/package-policy/approvers | De benoemde goedkeurders, naast de eigenaren. PUT neemt {"logins": [...]}. |
GET, POST /orgs/{org}/package-policy/exceptions | Uitzonderingen opvragen (filter met status) of er een aanvragen. |
GET /orgs/{org}/package-policy/exceptions/{id} | Eén uitzondering. |
POST /orgs/{org}/package-policy/exceptions/{id}/approve | Goedkeuren. Geweigerd als self-approve voor de aanvrager en als not-an-approver voor iedereen anders die dat niet mag. |
POST /orgs/{org}/package-policy/exceptions/{id}/reject | Afwijzen. |
DELETE /orgs/{org}/package-policy/exceptions/{id} | Intrekken. |
GET /orgs/{org}/package-policy/evaluation?type=&name=&version= | Het oordeel over elk bestand van één pakketversie, met de redenen, bevindingen en licenties. |
POST /orgs/{org}/package-policy/evaluate | Proefrun. Met {"policy": {...}} meldt hij wat elk huidig bestand onder die instellingen zou krijgen, en schrijft hij niets. Met {"components": [{"purl": "...", "licence": "..."}]} evalueert hij een lijst componenten, bijvoorbeeld vanuit een CI-stap voordat je publiceert. |
GET /orgs/{org}/package-policy/decisions | De gegevens van Zou geblokkeerd hebben: days (1–90, standaard 14), decision (blocked, would_block of pending_refused). |
GET /orgs/{org}/findings/packages | Advisorybevindingen op registerpakketten. Blader met cursor (de next_cursor van de vorige pagina) en limit (tot 500). |
Een beleid in schaduwmodus dat hoge en kritieke advisories en AGPL blokkeert, en nieuwe npm-releases 24 uur tegenhoudt:
TOKEN=ffp_...
ORG=acme
V=$(curl -sS -H "Authorization: Bearer $TOKEN" \
"https://frem.sh/_app/api/v1/orgs/$ORG/package-policy" | jq .version)
curl -sS -X PUT -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
"https://frem.sh/_app/api/v1/orgs/$ORG/package-policy" \
-d "{\"version\": $V, \"mode\": \"shadow\", \"vuln_min_severity\": \"high\",
\"licence_deny\": [\"AGPL-3.0-only\", \"AGPL-3.0-or-later\"],
\"min_age_hours\": 24, \"age_types\": [\"npm\"]}"log4j-core onder 2.17.1 weigeren, waar het ook is gebundeld:
curl -sS -X POST -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' \
"https://frem.sh/_app/api/v1/orgs/$ORG/package-policy/rules" \
-d '{"list": "deny", "pkg_type": "maven", "name_glob": "org.apache.logging.log4j:log4j-core",
"version_range": "vers:maven/<2.17.1", "match_target": "component",
"reason": "Log4Shell"}'Audit
Elke wijziging staat in de auditlog: package_policy.updated
(met wat er is gewijzigd), package_policy.rule.created en .deleted,
package_policy.approvers.updated, de gebeurtenissen voor uitzonderingen, en
package_policy.verdict_changed als een bestand van allow richting warn of block
gaat. Een geweigerde of te weigeren download wordt vastgelegd als
package_policy.download_blocked of package_policy.download_would_block,
één keer per bestand, regel, gebruiker en dag.
Gerelateerd
- Kwaadaardige pakketten
- Containerimages scannen, met een eigen pull-blokkade voor containerimages
- SBOM’s
- Toegangslog van het register