Lisensskanning
Lisensskanning svarer på spørsmålet hva har jeg lov til å levere – som et supplement til SBOM-ens hva finnes i dette imaget og avhengighetsskanningens hva er sårbart. fremforge kjører lisensdeteksjon med Trivy på hver PR, klassifiserer lisensen til hver oppløste avhengighet i en alvorlighetskategori etter SPDX-policy, og viser resultatet ved PR-en og på et eget dashbord for organisasjonen. Den er på som standard for alle organisasjoner – du trenger ikke slå den på.
Slik fungerer det
fremforges sentrale skanner (den samme som kjører SAST, avhengighetsskanning og skanning av imager) leser lisensen til hver avhengighet som låsefilene dine løser opp, og kjører Trivys deteksjon av lisensfiler over repositoryets egne LICENSE/COPYING-filer, på hver PR. Den kjører på fremforges egne runnere – ingenting installeres i repositoryet ditt. I motsetning til SAST og avhengighetsskanning – som poster en liste med bare problemer – poster lisensskanningen en full lisensoversikt: ett funn per (pakke × lisens) på tvers av alle oppløste avhengigheter. Workflowen POST-er så oversikten til fremforge:
POST https://frem.sh/_app/api/v1/license-scan/findingsRunnerens PAT trenger scopet findings:write (utstedt per repository av runner-kontrolleren når workflowen installeres). Etter en vellykket innsending gjør fremforge følgende:
- Lagrer funnene. Én rad per funn i
license_scan_findings. Hver skanning erstatter hele den forrige skanningen for samme(tenant, repo, pr_number)– en PR som fjerner en avhengighet, slutter å vise de utdaterte radene. Push-skanninger (pr_numbernull) har sin egen kategori, slik at de ikke erstatter PR-skanninger. - Poster en PR-kommentar som oppsummerer funnene (alvorlighetsgrad, lisens, pakke, manifest), via Forgejos issues-API.
- Setter commit-statusen
fremforge/license-scanpå PR-ens head-SHA –successnår alt bare er rådgivende eller står på tillatelseslisten,failurenår policysperren slår til.
Kommentaren og statusen på Forgejo-siden er best-effort: et Forgejo-avbrudd logges og nedgraderes til rådgivende, men får aldri innsendingen til å feile. De lagrede funnene er sannhetskilden; PR-visningen er et bekvemmelighetslag.
Hvor lisensene til avhengighetene kommer fra
Ingenting installeres: lisensene leses fra registermetadata for nøyaktig den versjonen hver låsefil låser. For npm, PyPI, Go og Maven leses metadataene gjennom organisasjonens avhengighetsproxy, på skannetjenestens interne rute, eller gjennom fremforges delte skannebuffer når organisasjonen din ikke har noen proxy; bare når ingen av dem kan svare, leses de fra det offentlige registeret gjennom fremforges utgående proxy. Lisenser for NuGet, crates.io, RubyGems og Packagist leses fra det offentlige registeret gjennom den utgående proxyen. fremforges tidligere pakkespeil i klyngen er faset ut.
| Økosystem | Kilde | Hva som leses |
|---|---|---|
| npm (låsefiler for npm / pnpm / yarn / bun) | registry.npmjs.org, gjennom avhengighetsproxyen | pakkens license-felt |
Go (go.mod) | proxy.golang.org, gjennom avhengighetsproxyen; private moduler (frem.sh, din deklarerte GOPRIVATE) etterspørres aldri | modulens LICENSE-/COPYING-fil i roten, klassifisert med den samme deteksjonen av lisensfiler som dine egne filer (Go har ingen lisensmetadata) |
Maven (pom.xml, Gradle-låsefiler) | Maven Central, gjennom avhengighetsproxyen | POM-ens <licenses>, med oppfølging av relokasjoner og overordnede POM-er |
NuGet (packages.lock.json) | nuget.org, gjennom den utgående proxyen | pakkens lisensuttrykk, eller en gjenkjennelig lisens-URL |
PyPI (requirements.txt, Poetry, uv) | pypi.org, gjennom avhengighetsproxyen | utgivelsens JSON-metadata (license_expression, en kort license, lisensklassifikatorer) |
| crates.io, RubyGems, Packagist | det offentlige registeret, gjennom den utgående proxyen | registerets lisensfelt per versjon |
Lisensnavn i fritekst (GPL v3, GNU AFFERO GPL 3.0, Apache License, Version 2.0, …) oversettes til SPDX-id-er. Alt som ikke kan leses med sikkerhet, rapporteres som unknown og vises for gjennomgang – det antas aldri å være permissivt.
Når en kilde er nede
Hvis et speil eller register ikke svarer (nettverksfeil, tidsavbrudd, 5xx), blir de berørte pakkene verken lest som rene eller tillatt å blokkere: statusen fremforge/license-scan beholder det resten av skanningen ga, og legger til lookup-unavailable for N package(s), re-scan queued. fremforge kjører skanningen på nytt automatisk med noen minutters mellomrom til kilden svarer, og merknaden forsvinner.
Alvorlighetskategorier
Lisensskanning bruker et vokabular med 7 kategorier (ikke de fire nivåene CRITICAL/HIGH/… som CVE-skanning bruker), i tråd med Trivys klassifisering etter SPDX-policy – ordnet fra minst til mest bebyrdende:
| Kategori | Betydning | Eksempler på lisenser |
|---|---|---|
unknown | Lisensen kunne ikke fastslås (vises likevel for gjennomgang) | NOASSERTION |
unencumbered | Tilsvarer offentlig eiendom (public domain) | CC0-1.0, Unlicense |
permissive | Fri bruk, minimale forpliktelser | – |
notice | Permissiv med forpliktelser om kreditering / merknad | MIT, BSD-3-Clause, Apache-2.0, ISC |
reciprocal | Svak copyleft – endringer 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 | Sterk copyleft | GPL-2.0, GPL-3.0, CC-BY-SA-4.0 |
forbidden | Nettverks-copyleft / kildetilgjengelig | AGPL-3.0, SSPL-1.0, BUSL-1.1, CC-BY-NC-4.0 |
LGPL er reciprocal i fremforge (Trivys egen tabell plasserer den i restricted): et program kan lenke til et LGPL-bibliotek uten selv å bli LGPL, så det er svak copyleft og blokkerer ikke ved standardterskelen.
Policykontroller
Konfigurer på siden for lisenspolicy, gjennom API-et (PUT /orgs/<org>/code-security/licenses/settings, bare pull requests) eller som kode med Terraform- og OpenTofu-modulen:
- Slått på – når den er av, returnerer innsendingen
202 {accepted: 0, status: 'disabled'}(workflowen kjører fortsatt, funnene forkastes bare). På som standard. - Block severity – terskelen der et funn på eller over blokkerer merge. Standard er
restricted(siden 2026-09-29; den varforbidden, som slapp GPL-avhengigheter gjennom som rådgivende): sterk copyleft og over blokkerer, svak copyleft forblir rådgivende. En lisens som ikke står på noen av listene nedenfor, vurderes etter denne terskelen. Kombiner med branchbeskyttelse som krever sjekkenfremforge/license-scanfor faktisk å blokkere merge. - Lisenspolicy – eksplisitte lister og to regler, beskrevet i neste avsnitt.
Funn på forbidden (→ critical) eller restricted (→ high), eller som avvises av blokkeringslisten, og som er nye og ikke avvist i en skanning, utløser også sanntidshooken for sikkerhetsvarsler; lavere kategorier er bare rådgivende.
Lisenspolicy
Hvert funn avgjøres per SPDX-operand, med samme tolkning som pakkepolicyen bruker: i MIT OR GPL-3.0-only kan forbrukeren velge, så den minst restriktive operanden avgjør; i Apache-2.0 AND GPL-2.0-only gjelder begge, så den mest restriktive operanden avgjør. Nestede parenteser, WITH-unntak (GPL-2.0-only WITH Classpath-exception-2.0) og LicenseRef-…-id-er leses slik SPDX definerer dem.
| Innstilling | API-felt | Standard | Virkning |
|---|---|---|---|
| Tillatelsesliste | allowlisted_license_ids | tom | En operand på listen er tillatt: funnet avvises ved innsending (aktør tenant_allowlist), uansett kategori. |
| Blokkeringsliste | denied_license_ids | tom | En operand på listen blokkerer, uansett kategori. Blokkering vinner over tillatelse for samme id. |
| Advarselsliste | warned_license_ids | tom | En operand på listen er rådgivende: rapporteres, blokkerer aldri, uansett kategori. |
| Ukjente lisenser | unknown_action | threshold | En lisens uten SPDX-id, NOASSERTION, NONE, UNKNOWN, en streng som ikke er et SPDX-uttrykk, eller en id fremforge ikke kan klassifisere (som en LicenseRef-… som ikke står på noen liste): threshold vurderer den som kategorien unknown under blokkeringsgraden; allow avviser den, warn rapporterer den, deny blokkerer den. |
| Flere lisenser | multiple_mode | all | En komponent som deklarerer flere lisenser (separate funn, ikke ett uttrykk): all vurderer hver lisens for seg, så hvilken som helst av dem kan blokkere; any lar den minst restriktive avgjøre for alle. |
Oppføringer i listene er SPDX-id-er (MIT, LicenseRef-acme) eller en id med unntaket sitt (GPL-2.0-only WITH Classpath-exception-2.0), og matches uten hensyn til store og små bokstaver; GPL-2.0+ matcher GPL-2.0-or-later. En oppføring for grunnlisensen dekker den med et hvilket som helst unntak; en oppføring for paret dekker bare paret. Opptil 100 oppføringer per liste. En PUT erstatter innstillingene: et felt som utelates, får standardverdien.
Standardverdiene endrer ingenting. Med tomme blokkerings- og advarselslister, unknown_action: threshold og multiple_mode: all avgjøres hvert funn nøyaktig slik blokkeringsgraden og tillatelseslisten avgjorde det før denne policyen fantes. Den ene forskjellen er selve tillatelseslisten, som nå matcher operander: MIT OR GPL-3.0-only slipper gjennom en tillatelsesliste som inneholder MIT.
Hvorfor et funn blokkerer
På en pull request blokkerer bare funn som pull requesten introduserer: en pakke og lisens som branchen allerede har, blokkerer ikke.
Hvert funn har avgjørelsen som policy i Findings-API-et (GET /orgs/<org>/code-security/licenses/findings), og dashbordet viser den i kolonnen Policyavgjø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 lister hvert blokkerende funn med begrunnelsen, og statusen fremforge/license-scan navngir reglene (2 finding(s) blocked by the licence policy (1 denied, 1 at or above restricted)).
For å prøve en policy før en PR gjør det, avgjør POST /orgs/<org>/code-security/licenses/evaluate en liste med lisenser uten å skrive noe:
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/evaluateOverstyringer per repository
Et repository kan overstyre hvilket som helst av de fem policyfeltene: PUT /orgs/<org>/repos/<repo>/code-security/licenses/settings med feltene som skal settes. Et felt som er satt, erstatter organisasjonens verdi for det repositoryet (en liste erstatter organisasjonens liste; en tom matrise er en verdi); et felt som mangler eller er null, arves. GET på samme sti viser overstyringen og den gjeldende policyen, DELETE fjerner den. I konsollen ligger overstyringene under Overstyringer per repository på Sikkerhet › Policyer › Lisenspolicy.
Lisensunntak for én komponent
For å godta én lisens for én komponent – for eksempel et GPL-verktøy som juristene dine har klarert – ber du om et unntak (POST /orgs/<org>/exceptions med kind: package, eller i konsollen) med scope component, regel licence, komponentens purl uten versjon (pkg:npm/left-pad, pkg:pypi/requests, pkg:maven/org.example:lib) og lisens-id-en som selektor (la selektoren stå tom for en hvilken som helst lisens for den komponenten). Det er det samme unntaket som for pakkepolicyer: en begrunnelse, en obligatorisk utløpstid på høyst ett år, og godkjenning av en eier eller navngitt godkjenner som ikke er den som ba om det. Når det er godkjent (POST /orgs/<org>/exceptions/<id>/approve, av en annen enn den som ba om det), avviser neste skanning av hvilket som helst repository de matchende funnene (aktør licence-exception:<id>, dismissal_approved_by godkjenneren, begrunnelse med rettferdiggjøringen), til unntaket utløper (funnet gjenåpnes den dagen) eller tilbakekalles (neste skanning gjenåpner det).
Avvise funn
En organisasjonsadministrator kan avvise enkeltfunn (med en påkrevd begrunnelse som skrives til revisjonsloggen) fra fanen Licenses. Manuelle avvisninger føres videre gjennom skanningen som erstatter den forrige ved innsending, med nøkkelen (lisens-id, pakke, manifeststi) – en ny skanning av en PR vekker ikke til live et funn som en administrator allerede har godtatt. En avvisning kan ha en valgfri utløpsdato, og etter den gjenåpnes funnet av seg selv – se når et funn lukkes. Avvisninger fra lisenspolicyen (tillatelseslisten, allow for ukjente, multiple_mode: any, et unntak) føres ikke videre; de utledes på nytt fra innstillingene ved hver skanning, så en endring i policyen får virkning ved neste skanning.
Hvor funnene vises
Sikkerhet › Funn › Lisenser lister aktive funn og nylige avvisninger. Hvert funn viser kategorien, SPDX-lisens-id-en, ecosystem:package, manifeststien og policyavgjørelsen med begrunnelse. PR-kommentaren lenker tilbake til dette dashbordet.
Oppbevaring
Funn følger oppbevaringspolicyen for revisjonsdata per kunde, i tråd med de andre kodesikkerhetsskannerne. Funn fra lukkede PR-er ryddes av den daglige jobben /jobs/license-scan-closed-pr-cleanup, siden en lukket PR aldri skannes på nytt.
Se også
- OSV-anbefalinger – CVE-skanning av avhengigheter (sårbarhetsmotstykket til denne lisensvisningen).
- SBOM-er – pakkeoversikten som lisensene knyttes til.
- Skanning av imager – søsterskanneren som deler den samme sentrale skannekjøringen og innsendingsmønsteret med
findings:write. - Sikkerhetsfunksjoner i korte trekk – hele kontrollmatrisen.