Licensscanning
Licensscanning svarer på spørgsmålet hvad må jeg udgive og supplerer SBOM’ens hvad er der i dette image og afhængighedsscanningens hvad er sårbart. fremforge kører Trivys licensdetektion på hver PR, klassificerer licensen for hver opløst afhængighed i en alvorlighedskategori fra SPDX-politikken og viser resultatet ved PR’en og på et særskilt dashboard for organisationen. Den er slået til som standard for alle organisationer — der er intet at aktivere.
Sådan virker det
fremforges centrale scanner (den samme, der kører SAST, afhængighedsscanning og image-scanning) læser licensen for hver afhængighed, dine lockfiler opløser, og kører Trivys detektion af licensfiler over dit repositorys egne LICENSE/COPYING-filer ved hver PR. Den kører på fremforges egne runners — intet installeres i dit repository. I modsætning til SAST og afhængighedsscanning, der sender en liste med kun problemer, sender licensscanningen en fuld licensoversigt: ét fund pr. (pakke × licens) på tværs af alle opløste afhængigheder. Workflowet sender derefter oversigten til fremforge med POST:
POST https://frem.sh/_app/api/v1/license-scan/findingsRunnerens PAT skal have scopet findings:write (udstedes pr. repository af runner-controlleren, når workflowet installeres). Ved en vellykket indlæsning gør fremforge følgende:
- Gemmer fundene. Én række pr. fund i
license_scan_findings. Hver scanning erstatter den tidligere scanning for samme(tenant, repo, pr_number)fuldstændigt — en PR, der fjerner en afhængighed, holder op med at vise dens forældede rækker. Push-scanninger (pr_numbernull) har deres egen kategori, så de ikke erstatter PR-scanninger på kryds. - Skriver en PR-kommentar, der opsummerer fundene (alvorlighed, licens, pakke, manifest), via Forgejos issues-API.
- Sætter commit-statussen
fremforge/license-scanpå PR’ens head-SHA —success, når der kun er fund til orientering, eller alle er på tilladelseslisten, ogfailure, når politikkens gate rammes.
Kommentaren og statussen i Forgejo er best-effort: et nedbrud i Forgejo logges og nedgraderes til orientering, men får aldrig indlæsningen til at fejle. De gemte fund er sandheden; visningen i PR’en er et bekvemmelighedslag.
Hvor afhængighedernes licenser kommer fra
Intet installeres: licenserne læses fra registry-metadata for præcis den version, hver lockfil fastlåser. For npm, PyPI, Go og Maven læses metadataene gennem din organisations dependency-proxy på scanningstjenestens interne rute, eller gennem fremforges delte scanningscache, når din organisation ikke har nogen proxy; kun når ingen af dem kan svare, læses de fra det offentlige registry gennem fremforges egress-proxy. Licenser fra NuGet, crates.io, RubyGems og Packagist læses fra det offentlige registry gennem egress-proxyen. fremforges tidligere pakkespejle i clusteret er udfaset.
| Økosystem | Kilde | Hvad der læses |
|---|---|---|
| npm (lockfiler fra npm / pnpm / yarn / bun) | registry.npmjs.org, gennem dependency-proxyen | pakkens felt license |
Go (go.mod) | proxy.golang.org, gennem dependency-proxyen; private moduler (frem.sh, din angivne GOPRIVATE) forespørges aldrig | modulets LICENSE- / COPYING-fil i roden, klassificeret med den samme detektion af licensfiler som dine egne filer (Go har ingen licensmetadata) |
Maven (pom.xml, Gradle-lockfiler) | Maven Central, gennem dependency-proxyen | POM’ens <licenses>, med relocations og forælder-POM’er fulgt |
NuGet (packages.lock.json) | nuget.org, gennem egress-proxyen | pakkens licensudtryk eller en genkendelig licens-URL |
PyPI (requirements.txt, Poetry, uv) | pypi.org, gennem dependency-proxyen | udgivelsens JSON-metadata (license_expression, en kort license, licens-classifiers) |
| crates.io, RubyGems, Packagist | det offentlige registry, gennem egress-proxyen | registry’ets licensfelt pr. version |
Licensnavne i fritekst (GPL v3, GNU AFFERO GPL 3.0, Apache License, Version 2.0, …) oversættes til SPDX-id’er. Alt, der ikke kan læses med sikkerhed, rapporteres som unknown og vises til gennemgang; det gættes aldrig til at være permissivt.
Når en kilde er nede
Hvis et spejl eller registry ikke svarer (netværksfejl, timeout, 5xx), bliver de berørte pakker hverken læst som rene eller får lov at blokere: statussen fremforge/license-scan beholder det, resten af scanningen gav, og tilføjer lookup-unavailable for N package(s), re-scan queued. fremforge kører automatisk scanningen igen med få minutters mellemrum, indtil kilden svarer, og noten forsvinder.
Alvorlighedskategorier
Licensscanning bruger et ordforråd med 7 kategorier for alvorlighed (ikke de 4 niveauer CRITICAL/HIGH/… fra CVE-scanning), der svarer til Trivys klassificering efter SPDX-politik — ordnet fra mindst til mest bebyrdende:
| Kategori | Betydning | Eksempler på licenser |
|---|---|---|
unknown | Licensen kunne ikke bestemmes (vises stadig til gennemgang) | NOASSERTION |
unencumbered | Svarer til public domain | CC0-1.0, Unlicense |
permissive | Frit brug, minimale forpligtelser | — |
notice | Permissiv med forpligtelser til kreditering / meddelelse | MIT, BSD-3-Clause, Apache-2.0, ISC |
reciprocal | Svag copyleft — ændringer i selve biblioteket deles | LGPL-2.1, LGPL-3.0, MPL-2.0, EPL-2.0, CDDL-1.0, GPL-2.0 WITH Classpath-exception-2.0 |
restricted | Stærk copyleft | GPL-2.0, GPL-3.0, CC-BY-SA-4.0 |
forbidden | Netværks-copyleft / source-available | AGPL-3.0, SSPL-1.0, BUSL-1.1, CC-BY-NC-4.0 |
LGPL er reciprocal i fremforge (Trivys egen tabel placerer den i restricted): et program kan linke til et LGPL-bibliotek uden selv at blive LGPL, så det er svag copyleft og blokerer ikke ved standardtærsklen.
Politikindstillinger
Konfigurér på siden Licenspolitik, gennem API’et (PUT /orgs/<org>/code-security/licenses/settings, kun pull requests) eller som kode med Terraform- og OpenTofu-modulet:
- Slået til — når den er slået fra, svarer indlæsningen
202 {accepted: 0, status: 'disabled'}(workflowet kører stadig, fundene kasseres bare). Slået til som standard. - Blokeringsalvorlighed — tærsklen, hvor et fund på eller over den blokerer merge. Standard er
restricted(siden 2026-09-29; den varforbidden, som lod GPL-afhængigheder slippe igennem til orientering): stærk copyleft og derover blokerer, svag copyleft forbliver til orientering. En licens, der ikke står på nogen af listerne nedenfor, vurderes efter denne tærskel. Kombinér med branch protection, der kræver tjekketfremforge/license-scan, for rent faktisk at blokere merge. - Licenspolitik — eksplicitte lister og to regler, beskrevet i næste afsnit.
Fund i forbidden (→ critical) eller restricted (→ high), eller afvist af listen over afviste licenser, som en scanning har tilføjet, og som ikke er afvist, udløser også hooket til sikkerhedsalarmer i realtid; lavere kategorier er kun til orientering.
Licenspolitik
Hvert fund afgøres pr. SPDX-operand, med samme læsning som pakkepolitikken bruger: i MIT OR GPL-3.0-only kan forbrugeren vælge, så den mindst begrænsende operand afgør; i Apache-2.0 AND GPL-2.0-only gælder begge, så den mest begrænsende operand afgør. Indlejrede parenteser, WITH-undtagelser (GPL-2.0-only WITH Classpath-exception-2.0) og LicenseRef-…-id’er læses, som SPDX definerer dem.
| Indstilling | API-felt | Standard | Virkning |
|---|---|---|---|
| Tilladte licenser | allowlisted_license_ids | tom | En operand på listen er tilladt: fundet afvises ved indlæsningen (actor tenant_allowlist), uanset kategori. |
| Afviste licenser | denied_license_ids | tom | En operand på listen blokerer, uanset kategori. Afvist vinder over tilladt for samme id. |
| Licenser, der giver advarsel | warned_license_ids | tom | En operand på listen er til orientering: rapporteret, aldrig blokerende, uanset kategori. |
| Ukendte licenser | unknown_action | threshold | En licens uden SPDX-id, NOASSERTION, NONE, UNKNOWN, en streng, der ikke er et SPDX-udtryk, eller et id, fremforge ikke kan klassificere (som et LicenseRef-…, der ikke står på en liste): threshold vurderer den som kategorien unknown under blokeringsalvorligheden; allow afviser fundet, warn rapporterer det, deny blokerer det. |
| Flere licenser | multiple_mode | all | En komponent, der angiver flere licenser (separate fund, ikke ét udtryk): all vurderer hver licens for sig, så enhver af dem kan blokere; any lader den mindst begrænsende afgøre for dem alle. |
Listernes poster er SPDX-id’er (MIT, LicenseRef-acme) eller et id med sin undtagelse (GPL-2.0-only WITH Classpath-exception-2.0), og der skelnes ikke mellem store og små bogstaver; GPL-2.0+ matcher GPL-2.0-or-later. En post for grundlicensen dækker den med enhver undtagelse; en post for parret dækker kun parret. Op til 100 poster pr. liste. En PUT erstatter indstillingerne: et felt, der udelades, får sin standardværdi.
Standardværdierne ændrer intet. Med tomme lister over afviste og advarende licenser, unknown_action: threshold og multiple_mode: all afgøres hvert fund præcis, som blokeringsalvorligheden og tilladelseslisten afgjorde det, før denne politik fandtes. Den eneste forskel er selve tilladelseslisten, der nu matcher operander: MIT OR GPL-3.0-only består med en tilladelsesliste, der indeholder MIT.
Hvorfor et fund blokerer
På en pull request blokerer kun fund, som pull requesten tilføjer: en pakke og licens, som branchen allerede har, blokerer ikke.
Hvert fund bærer afgørelsen som policy i fund-API’et (GET /orgs/<org>/code-security/licenses/findings), og dashboardet viser den i kolonnen Politikkens afgørelse:
{
"license_id": "MIT OR GPL-3.0-only",
"policy": {
"action": "allow",
"rule": "allow_list",
"decided_by": ["MIT"],
"operands": [
{ "operand": "MIT", "action": "allow", "rule": "allow_list", "matched": "MIT" },
{ "operand": "GPL-3.0-only", "action": "block", "rule": "deny_list", "matched": "GPL-3.0-only" }
],
"why": "allowed: MIT is on the allow list (OR takes the least restrictive operand)"
}
}action er allow, warn eller block; rule er allow_list, deny_list, warn_list, threshold, below_threshold, unknown, multiple_any eller exception. PR-kommentaren viser hvert blokerende fund med sin årsag, og statussen fremforge/license-scan nævner reglerne (2 finding(s) blocked by the licence policy (1 denied, 1 at or above restricted)).
For at afprøve en politik, før en PR gør det, afgør POST /orgs/<org>/code-security/licenses/evaluate en liste af licenser uden at skrive noget:
curl -sS -X POST -H "Authorization: Bearer $FREMFORGE_TOKEN" -H 'Content-Type: application/json' \
-d '{"licenses":[{"license_id":"MIT OR GPL-3.0-only"},{"license_id":"Apache-2.0 AND GPL-2.0-only"}]}' \
https://frem.sh/_app/api/v1/orgs/<org>/code-security/licenses/evaluateTilsidesættelser pr. repository
Et repository kan tilsidesætte hvert af de fem politikfelter: PUT /orgs/<org>/repos/<repo>/code-security/licenses/settings med de felter, der skal sættes. Et felt, der er sat, erstatter organisationens værdi for det repository (en liste erstatter organisationens liste; et tomt array er en værdi); et felt, der mangler eller er null, arves. GET på samme sti viser tilsidesættelsen og den gældende politik, DELETE fjerner den. I konsollen findes tilsidesættelserne under Tilsidesættelser pr. repository på Sikkerhed › Politikker › Licenspolitik.
Licensundtagelser for én komponent
For at acceptere én licens for én komponent — for eksempel et GPL-værktøj, som din juridiske afdeling har godkendt — anmoder du om en undtagelse (POST /orgs/<org>/exceptions med kind: package, eller i konsollen) med omfanget component, reglen licence, komponentens purl uden version (pkg:npm/left-pad, pkg:pypi/requests, pkg:maven/org.example:lib) og licens-id’et som selektor (lad selektoren være tom for enhver licens for den komponent). Det er den samme undtagelse som for pakkepolitikker: en begrundelse, en obligatorisk udløbsdato på højst et år og godkendelse af en ejer eller navngiven godkender, der ikke er den, som anmoder. Når den er godkendt (POST /orgs/<org>/exceptions/<id>/approve, af en anden end den, som anmoder), afviser den næste scanning af et hvilket som helst repository de matchende fund (actor licence-exception:<id>, dismissal_approved_by godkenderen, årsag med begrundelsen), indtil undtagelsen udløber (fundet genåbnes den dag) eller tilbagekaldes (den næste scanning genåbner det).
Afvisning af fund
En administrator i organisationen kan afvise enkelte fund (med en påkrævet årsag, der skrives i revisionsloggen) fra fanen Licenser. Manuelle afvisninger føres videre gennem genscanningen, der erstatter ved indlæsning, med nøglen (licens-id, pakke, manifeststi) — en genscanning af en PR genopliver ikke et fund, som en administrator allerede har accepteret. En afvisning kan have en valgfri udløbsdato, hvorefter fundet genåbnes af sig selv — se hvornår et fund lukkes. Afvisninger foretaget af licenspolitikken (listen over tilladte, unknown allow, multiple_mode: any, en undtagelse) føres ikke videre; de udledes igen af indstillingerne ved hver scanning, så en ændring af politikken får virkning ved næste scanning.
Hvor fund vises
Sikkerhed › Fund › Licenser viser aktive fund og nylige afvisninger. Hvert fund viser alvorlighedskategorien, SPDX-licens-id’et, ecosystem:package, manifeststien og politikkens afgørelse med årsag. PR-kommentaren linker tilbage til dette dashboard.
Opbevaring
Fund følger revisionslogens opbevaringspolitik pr. tenant, ligesom de andre kodesikkerhedsscannere. Fund fra lukkede PR’er ryddes af det daglige job /jobs/license-scan-closed-pr-cleanup, da en lukket PR aldrig scannes igen.
Se også
- OSV-anbefalinger — scanning af afhængigheder for CVE’er (sårbarheds-supplementet til denne licens-visning).
- SBOM’er — pakkeoversigten, som licenserne knyttes til.
- Image-scanning — søsterscanneren, der deler den samme centrale scanningskørsel og indlæsningsmønster med
findings:write. - Sikkerhedsfunktioner i overblik — den fulde kontrolmatrix.