123% meer gemelde CVE's per dag in 2026 dan vorig jaar.

Afhankelijkheden

Wat is een transitieve afhankelijkheid?

Een transitieve afhankelijkheid is een pakket dat uw software gebruikt omdat een ander pakket het nodig heeft. U hebt het nooit gedeclareerd, maar het zit in wat u levert, met eigen kwetsbaarheden en een eigen licentie. Zo ziet u ze, lost u ze op en bewijst u welke in elke release terecht zijn gekomen.

Bijgewerkt op

A vulnerable transitive dependency and its fix path An app declares two direct dependencies, express and hbs. Express brings in body-parser and send, which bring in raw-body and mime-types. Hbs brings in handlebars, which is vulnerable. The path from the app through hbs to handlebars is highlighted: upgrade hbs, or override the handlebars version. your project direct transitive, depth 1 depth 2 my-app express hbs body-parser send handlebars vulnerable, required by hbs raw-body mime-types Fix path: upgrade hbs, or override the handlebars version

Directe en transitieve afhankelijkheden

Een directe afhankelijkheid is er een die u in uw manifest declareert. Een transitieve afhankelijkheid is er een die uw afhankelijkheden declareren, en die van hen op hun beurt, zo diep als de boom gaat. De pakketbeheerder lost bij het installeren de hele boom op en legt het resultaat meestal vast in een lockfile.

Neem een app die één pakket declareert, express. Als u npm ls --all uitvoert, ziet u wat die ene regel meebrengt:

my-app
└─┬ express            ← direct: door u gedeclareerd
  ├─┬ body-parser      ← transitief, niveau 1
  │ └── raw-body       ← transitief, niveau 2
  ├── cookie           ← transitief, niveau 1
  └─┬ send             ← transitief, niveau 1
    └── mime-types     ← transitief, niveau 2

De echte boom is langer. De meeste pakketten in een doorsnee applicatie komen op deze manier binnen, wat betekent dat het grootste deel van de code die u levert, is gekozen door de pakketbeheerder en niet door u.

Directe afhankelijkheid Transitieve afhankelijkheid
Wie heeft hem gekozen Uw team, in het manifest De pakketbeheerder, op basis van de manifests van uw afhankelijkheden
Waar hij staat Manifest en lockfile Lockfile, als die er is
Wie de versie bepaalt U Het versiebereik van het bovenliggende pakket, tenzij u het overschrijft
Hoe u een kwetsbaarheid oplost De afhankelijkheid upgraden Het bovenliggende pakket upgraden, of de versie overschrijven

Waarom transitieve afhankelijkheden ertoe doen

Ze brengen dezelfde risico's mee als directe afhankelijkheden, met minder zicht en minder controle.

  • Kwetsbaarheden. Toen Log4Shell, een zero-day in log4j, bekend werd, vond het Open Source Insights-team van Google 35.863 Maven Central-artefacten die afhankelijk waren van de getroffen log4j-code. Ongeveer 7.000 waren er direct van afhankelijk. De meerderheid haalde die binnen als transitieve afhankelijkheid, en veel ervan konden pas worden gepatcht nadat een tussenliggend pakket eerst een fix had uitgebracht.
  • Aanvallen op de softwareketen. De backdoor in xz utils (CVE-2024-3094) bereikte OpenSSH zonder dat OpenSSH afhankelijk was van xz. Verschillende Linux-distributies patchen OpenSSH voor ondersteuning van systemd-notificaties, en libsystemd is afhankelijk van liblzma, de bibliotheek die xz levert.
  • Licenties. Elk transitief pakket heeft zijn eigen licentie. Een copyleftlicentie drie niveaus diep is even bindend als een licentie die u zelf hebt toegevoegd.
  • Breaking changes. Een bovenliggend pakket dat een versiebereik accepteert, laat bij de volgende schone installatie een nieuwe transitieve release binnen, ook als er niets in uw manifest is gewijzigd.

Hoe u uw transitieve afhankelijkheden opvraagt

Elke grote pakketbeheerder kan de opgeloste boom afdrukken:

Ecosysteem Opdracht
npmnpm ls --all
pnpmpnpm list --depth Infinity
Python (uv)uv tree
Python (pip)pipdeptree
Gogo mod graph
Rustcargo tree
Mavenmvn dependency:tree
Gradlegradle dependencies

Wilt u weten waarom één pakket er staat, vraag dan het pad ernaartoe op: npm explain <package>, pnpm why, yarn why of cargo tree -i <crate>. Zie dependency graphs om met de paden te werken in plaats van ze af te drukken.

Hoe u een kwetsbare transitieve afhankelijkheid oplost

  1. Upgrade het bovenliggende pakket. Zoek de directe afhankelijkheid die het pakket binnenhaalt en stap over op een release die een gepatchte versie vereist. Dit is de netste oplossing, omdat de maintainers van het bovenliggende pakket die hebben getest.
  2. Vernieuw de lockfile. Staat het versiebereik van het bovenliggende pakket de gepatchte versie al toe, dan volstaat het om de vermelding in de lockfile bij te werken, bijvoorbeeld met cargo update -p <crate> of een nieuwe npm update.
  3. Overschrijf de versie. Heeft het bovenliggende pakket nog geen fix uitgebracht, forceer de versie dan zelf: overrides in npm en pnpm, resolutions in Yarn, een constraints-bestand bij pip of een hogere eis in go.mod. Test dit, want het bovenliggende pakket is nooit tegen die versie uitgebracht.
  4. Vervang of verwijder het bovenliggende pakket. Werkt geen van beide, dan is de afhankelijkheid die het pakket binnenhaalt het misschien niet meer waard om te houden.

Sommige tools voor software composition analysis voegen reachability-analyse toe, die controleert of uw code de kwetsbare functie überhaupt kan aanroepen, om te bepalen welke van deze oplossingen eerst komt. Zie SCA-tools vergeleken voor de opties.

Waarom de lockfile niet het hele verhaal is

De meeste tools baseren hun beeld van transitieve afhankelijkheden op de lockfile. Dat werkt als de lockfile er is, actueel is en het enige is waaruit de build installeert. In CI is dat vaak niet zo:

  • Geen lockfile. Een requirements.txt met versiebereiken of een project dat zijn lockfile nooit heeft gecommit, wordt bij elke build opnieuw opgelost, dus de transitieve versies kunnen per build verschillen.
  • Caches. CI-caches zetten pakketten uit eerdere runs terug in plaats van ze op te halen, en wat erin zit, is niet altijd wat de huidige lockfile zegt. Zie wat de GitHub Actions-cache verbergt.
  • Buildtools. Linters, compilers, codegeneratoren en setupstappen brengen hun eigen afhankelijkheidsbomen mee in de build. Ze staan niet in het manifest van uw applicatie, maar draaien wel met toegang tot uw broncode en secrets.
  • Installatiescripts en downloads. Het installatiescript van een pakket kan meer code ophalen, en een buildstap kan direct een binary downloaden. Geen van beide laat een vermelding in een lockfile achter.

Het enige volledige record van welke transitieve afhankelijkheden in een release terecht zijn gekomen, is er een dat is gemaakt terwijl die release werd gebouwd. SBOM's tijdens de build legt het verschil uit tussen een build vastleggen en de bestanden ervan achteraf scannen.

Transitieve afhankelijkheden en de CRA

De EU Cyber Resilience Act vraagt fabrikanten een SBOM op te stellen die "ten minste de afhankelijkheden op het hoogste niveau van de producten" dekt (bijlage I, deel II, punt 1). Transitieve afhankelijkheden worden niet genoemd, dus de wettelijke ondergrens ligt lager dan veel gidsen suggereren. Maar de CRA vraagt u ook kwetsbaarheden te identificeren en af te handelen in de componenten die uw product bevat, en een kwetsbaar transitief pakket is daar een van. Een SBOM die bij het eerste niveau stopt, laat het grootste deel van uw afhankelijkheidsboom buiten dat proces. Zie SBOM-eisen van de CRA voor de details.

Hoe CRACI transitieve afhankelijkheden volgt

CRACI is een GitHub Actions-runner die de build vastlegt in plaats van hem te scannen. Terwijl elke job draait, legt een proxy die pakketten herkent, elk pakket vast dat de job ophaalt, op elk niveau van de boom en inclusief pakketten die uit CI-caches zijn teruggezet. Dat geeft een completer beeld van de gebruikte afhankelijkheden dan op scanners gebaseerde tools.

  • Elk niveau, eerlijk vermeld. De SBOM van elke job heeft een volledigheidsstatus, zodat u weet wanneer een vastlegging niet alles kon zien.
  • De boom, zichtbaar. Een dependency graph toont de afhankelijkheidslagen van de SBOM, in meerdere weergaven.
  • Exports waar ze in staan. CycloneDX- en SPDX-exports bevatten transitieve afhankelijkheden, elk met de gedeclareerde licentie.
  • Welke builds zijn getroffen. Wanneer een nieuwe kwetsbaarheid opduikt, toont de buildhistorie welke eerdere builds het kwetsbare pakket bevatten, direct of transitief, zodat u weet in welke periode u het hebt geleverd (zie zijn we getroffen door een CVE?). De historie volgt uw bewaartermijn, standaard 180 dagen.
  • Policy gates. Een build die een specifieke CVE bevat, kan worden geblokkeerd voordat hij wordt geleverd.

CRACI doet geen reachability-analyse. Wilt u weten of uw code een kwetsbare functie aanroept, combineer het dan met een SCA-tool die dat wel doet. Zie SBOM-generatie in CRACI of boek een demo om een van uw eigen builds vast te leggen.

Transitieve afhankelijkheden: veelgestelde vragen

Wat is een transitieve afhankelijkheid?

Een transitieve afhankelijkheid is een pakket dat uw software gebruikt omdat een ander pakket het nodig heeft. Als uw app afhankelijk is van bibliotheek A en A afhankelijk is van bibliotheek B, dan is B een transitieve, of indirecte, afhankelijkheid van uw app. U hebt hem nooit gedeclareerd, maar hij wordt met uw software meegeleverd.

Wat is het verschil tussen een directe en een transitieve afhankelijkheid?

Een directe afhankelijkheid is er een die u in uw manifest declareert, zoals package.json, pyproject.toml of go.mod. Een transitieve afhankelijkheid is er een die uw directe afhankelijkheden declareren, op elk niveau. Directe afhankelijkheden kiest u zelf; transitieve kiest de pakketbeheerder wanneer hij de boom oplost.

Is een transiënte afhankelijkheid hetzelfde als een transitieve afhankelijkheid?

Ja, in de praktijk. "Transiënte afhankelijkheid" is een veelgemaakte verschrijving van "transitieve afhankelijkheid". De juiste term is transitief, naar de transitieve relatie: als A afhankelijk is van B en B afhankelijk is van C, dan is A afhankelijk van C.

Hoe los ik een kwetsbaarheid in een transitieve afhankelijkheid op?

Upgrade eerst de directe afhankelijkheid die hem binnenhaalt, als er een release met een gepatchte versie bestaat. Zo niet, forceer dan de gepatchte versie met het overschrijfmechanisme van uw pakketbeheerder, zoals npm overrides, Yarn resolutions of een constraints-bestand voor pip, en test of het bovenliggende pakket er nog mee werkt.

Hoe vraag ik alle transitieve afhankelijkheden op?

Elke pakketbeheerder kan de opgeloste boom afdrukken: npm ls --all, pnpm list --depth Infinity, uv tree of pipdeptree, go mod graph, cargo tree, mvn dependency:tree of gradle dependencies. Voor wat een productiebuild daadwerkelijk heeft geïnstalleerd, legt u de build zelf vast.

Vereist de EU Cyber Resilience Act transitieve afhankelijkheden in de SBOM?

De verordening legt de ondergrens bij "ten minste de afhankelijkheden op het hoogste niveau van de producten" (bijlage I, deel II, punt 1). Transitieve afhankelijkheden worden niet genoemd, maar een kwetsbaar transitief pakket zit nog steeds in uw product, en de CRA vraagt u kwetsbaarheden af te handelen in de componenten die u levert.

Zie elke afhankelijkheid die uw build binnenhaalde

Laat één GitHub Actions-workflow op CRACI draaien en krijg een vastgelegde SBOM van elk pakket dat hij ophaalde, op elk niveau van de boom.

Demo boeken