Gå til hovedinnhold

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/findings

Runnerens PAT trenger scopet findings:write (utstedt per repository av runner-kontrolleren når workflowen installeres). Etter en vellykket innsending gjør fremforge følgende:

  1. 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_number null) har sin egen kategori, slik at de ikke erstatter PR-skanninger.
  2. Poster en PR-kommentar som oppsummerer funnene (alvorlighetsgrad, lisens, pakke, manifest), via Forgejos issues-API.
  3. Setter commit-statusen fremforge/license-scan på PR-ens head-SHA – success når alt bare er rådgivende eller står på tillatelseslisten, failure nå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.

ØkosystemKildeHva som leses
npm (låsefiler for npm / pnpm / yarn / bun)registry.npmjs.org, gjennom avhengighetsproxyenpakkens license-felt
Go (go.mod)proxy.golang.org, gjennom avhengighetsproxyen; private moduler (frem.sh, din deklarerte GOPRIVATE) etterspørres aldrimodulens 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 avhengighetsproxyenPOM-ens <licenses>, med oppfølging av relokasjoner og overordnede POM-er
NuGet (packages.lock.json)nuget.org, gjennom den utgående proxyenpakkens lisensuttrykk, eller en gjenkjennelig lisens-URL
PyPI (requirements.txt, Poetry, uv)pypi.org, gjennom avhengighetsproxyenutgivelsens JSON-metadata (license_expression, en kort license, lisensklassifikatorer)
crates.io, RubyGems, Packagistdet offentlige registeret, gjennom den utgående proxyenregisterets 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:

KategoriBetydningEksempler på lisenser
unknownLisensen kunne ikke fastslås (vises likevel for gjennomgang)NOASSERTION
unencumberedTilsvarer offentlig eiendom (public domain)CC0-1.0, Unlicense
permissiveFri bruk, minimale forpliktelser–
noticePermissiv med forpliktelser om kreditering / merknadMIT, BSD-3-Clause, Apache-2.0, ISC
reciprocalSvak copyleft – endringer i selve biblioteket delesLGPL-2.1, LGPL-3.0, MPL-2.0, EPL-2.0, CDDL-1.0, GPL-2.0 WITH Classpath-exception-2.0
restrictedSterk copyleftGPL-2.0, GPL-3.0, CC-BY-SA-4.0
forbiddenNettverks-copyleft / kildetilgjengeligAGPL-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

Én lisenspolicy siden 2026-10-15. Listene, reglene for ukjente og flere lisenser og blokkeringsgraden redigeres under Sikkerhet › Policyer › Lisenspolicy, sammen med lisensreglene for pakkenedlastinger. Se Lisenspolicy. Sikkerhet › Funn › Lisenser beholder av/på-bryteren og overstyringene per repository. API-feltene nedenfor virker fortsatt og endrer bare pull requests.

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 var forbidden, 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 sjekken fremforge/license-scan for 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.

InnstillingAPI-feltStandardVirkning
Tillatelseslisteallowlisted_license_idstomEn operand på listen er tillatt: funnet avvises ved innsending (aktør tenant_allowlist), uansett kategori.
Blokkeringslistedenied_license_idstomEn operand på listen blokkerer, uansett kategori. Blokkering vinner over tillatelse for samme id.
Advarselslistewarned_license_idstomEn operand på listen er rådgivende: rapporteres, blokkerer aldri, uansett kategori.
Ukjente lisenserunknown_actionthresholdEn 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 lisensermultiple_modeallEn 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/evaluate

Overstyringer 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å