Hoppa till huvudinnehållet

Transitiva beroenden

Ett sårbart paket är ofta inte ett som du har deklarerat. Ett annat beroende drar in det, och det beroendet väljer dess version. Att höja versionen där paketet deklareras går inte, eftersom det inte deklareras någonstans i ditt repo. Säkerhetsfixar låser i stället ett sådant paket från roten av ditt projekt, med den mekanism som varje ekosystem har för det.

EkosystemHur paketet låsesHur du begär det
npm (package-lock.json)En post i overrides i package.json, och paketets poster i package-lock.json flyttade till den åtgärdade versionenÖppna fix-PR på fyndet, som för alla andra. När hostad Renovate inte hittar något att höja låser fremforge det inom ungefär tio minuter
pnpm (pnpm-lock.yaml)En post i overrides i pnpm-workspace.yaml (eller pnpm.overrides i package.json för pnpm 9 och äldre), och låsfilen flyttad så att den matcharSom för npm
Yarn 1 (yarn.lock)En post i resolutions i package.json, och posten i yarn.lock flyttadSom för npm. Yarn 2 och senare avvisas
PyPI (uv.lock, poetry.lock)Paketets låspost flyttad till den åtgärdade versionen, utan någon låsning i pyproject.tomlSom för npm
Maven (pom.xml)En post i <dependencyManagement> i den pom.xml som fyndet kom frånÖppna fix-PR, som för npm
Maven (sett i din SBOM)En post i <dependencyManagement> i rotens pom.xmlLås som transitiv på fyndet, eller POST /orgs/{org}/dependency-updates/security-fixes/transitive-requests
Gradle (gradle.lockfile)En post i constraints i byggfilen, och raden i låsfilen flyttadSom för npm
GoInget extra: go.mod listar varje modul som ditt bygge använder, markerad // indirect, och fixen höjer den radenÖppna fix-PR

Det är en vanlig pull request med en säkerhetsfix, och samma versionsålder gäller: en åtgärdad version som är yngre än organisationens Versionsålder (dagar) (3 som standard) föreslås inte än, om den inte åtgärdar en känd utnyttjad sårbarhet. Eftersom ingen Renovate-körning kontrollerar en transitiv låsning läser fremforge själv publiceringstiden, via din beroendeproxy: time i npm-registret, uppladdningstiden för den åtgärdade versionens filer i PyPI, och för Maven och Gradle den tid som Maven Central anger för pom-filen. En åtgärdad version vars publiceringstid inte kan läsas föreslås inte.

Den har också samma branch (security-fix/<package>-<version>), etikett (security-fix), gränser och merge-inställning som alla andra, och samma val under När fixar mergas gäller. Pull requesten anger att paketet är transitivt och hur det låstes.

npm

Pull requesten ändrar två filer och inget annat:

"overrides": {
  "lodash": "4.18.0"
}

Det läggs till i package.json. Varje post i package-lock.json för paketet i en sårbar version flyttas till den åtgärdade versionen, med ändrad version och tarball-URL, och ändrad integritetshash där posten hade en. URL:en behåller låsfilens register.

fremforge kör inte npm install, eller något annat från ditt repo. Den skriver låsposterna utifrån registrets metadata, läst via din organisations beroendeproxy, så att dina paketpolicyer även gäller den åtgärdade versionen. Den skriver dem bara när det är allt som en upplösning skulle ändra:

  • Den åtgärdade versionen deklarerar exakt samma beroenden som den version den ersätter. Annars avvisas begäran med transitive_dependencies_changed, eftersom trädet skulle ändras utöver det här paketet.
  • Varje kopia av paketet i låsfilen är i en sårbar version. En override flyttar varje kopia, så en kopia i en annan, opåverkad version avvisar begäran med override_conflict.
  • package-lock.json har lockfileVersion 2 eller 3.

npm ci godtar resultatet. Ta bort overriden när de paket som drar in paketet själva kräver den åtgärdade versionen.

pnpm

pnpm registrerar även overrides i låsfilen, och avvisar en låsfil vars avsnitt overrides: skiljer sig från konfigurationen. Pull requesten ändrar därför båda:

  • overriden, i pnpm-workspace.yaml (som skapas om repot inte har någon). Den hamnar i stället i pnpm.overrides i package.json när packageManager anger pnpm 9 eller äldre, eller, utan packageManager, när projektet har sina overrides där och saknar pnpm-workspace.yaml;
  • pnpm-lock.yaml: dess avsnitt overrides:, paketets poster i packages och snapshots med integriteten från registret, och varje referens till den gamla versionen.

pnpm install --frozen-lockfile godtar resultatet. Bara lockfileVersion: '9.0' stöds; ett paket som är låst med ett suffix för peer-beroenden, som foo@1.0.0(react@18.3.1), avvisas med lock_entry_unsupported.

Yarn

För Yarn 1 lägger pull requesten till en post i resolutions i package.json och flyttar paketets post i yarn.lock: dess version, dess resolved-URL (i låsfilens register, med tarballens kontrollsumma) och dess integrity. yarn install --frozen-lockfile godtar resultatet.

Yarn 2 och senare (en yarn.lock med __metadata) avvisas med yarn_berry_checksum. Deras låsfil registrerar en kontrollsumma för det arkiv som Yarn bygger av paketet, och den kan bara Yarn själv beräkna. Lägg till en post i resolutions själv och kör yarn install.

Samma regler som för npm gäller för pnpm och Yarn: den åtgärdade versionen måste deklarera samma beroenden, och varje kopia av paketet i låsfilen måste vara i en sårbar version.

Python

För uv.lock och poetry.lock flyttar pull requesten paketets låspost till den åtgärdade versionen och lägger inte till något i pyproject.toml. Ett paket som pyproject.toml deklarerar avvisas med already_declared, eftersom Öppna fix-PR höjer det där. Den nya posten innehåller den åtgärdade versionens filer med deras hashar, lästa från organisationens PyPI-beroendeproxy:

  • uv: sdist och wheels, med URL, hash, storlek och uppladdningstid där din låsfil registrerar dem. URL:erna behåller låsfilens index: beroendeproxyns egen URL när du låser mot den, eller files.pythonhosted.org när du låser mot PyPI. En låsfil från något annat index avvisas med lock_entry_unsupported.
  • Poetry: versionen, beskrivningen, python-versions och files. Poetrys content-hash täcker bara pyproject.toml, så den ändras inte.

Regeln är densamma som för npm, tillämpad på Pythons metadata: den åtgärdade versionen måste ha samma Requires-Dist och Requires-Python som den version den ersätter, lästa från dess wheel-metadata. Annars avvisas begäran med transitive_dependencies_changed. uv sync --locked och poetry install godtar resultatet. Paketet stannar på den åtgärdade versionen tills en senare uv lock eller poetry lock löser upp beroendena igen.

Maven

Beroendeskanningen löser upp varje pom.xml, så ett sårbart paket som ditt bygge drar in är ett fynd i den pom.xml. Öppna fix-PR på det fungerar som för npm: när hostad Renovate inte hittar något att höja, eftersom pom-filen inte namnger paketet, låser fremforge det i den pom.xml inom ungefär tio minuter. Ett paket som bara syns i en SBOM som du laddar upp, till exempel en från cyclonedx-maven-plugin (se beroendesökvägar), visar i stället Lås som transitiv i kolumnen Åtgärd och låses i rotens pom.xml. I båda fallen lägger pull requesten till:

<dependencyManagement>
  <dependencies>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.18.11</version>
    </dependency>
  </dependencies>
</dependencyManagement>

Det anger paketets version överallt där det förekommer i bygget. Om pom-filen redan hanterar det med en lägre literal version höjs den versionen på plats. Det avvisas när:

  • pom-filen deklarerar paketet som ett eget beroende (already_declared). Öppna fix-PR höjer det där;
  • den hanterade versionen läses från en property, som ${jackson.version}, som även andra poster kan läsa (managed_by_property);
  • det redan hanteras med den åtgärdade versionen eller högre (already_managed).

Kontrollera att de beroenden som drar in paketet fungerar med den nya versionen, eftersom ett bibliotek oftast är testat mot den version det bad om. Ta bort posten när de själva kräver den åtgärdade versionen.

Gradle

För ett Gradle-bygge med en låsfil (gradle.lockfile) lägger pull requesten till en constraint i blocket dependencies i byggfilen bredvid låsfilen (build.gradle.kts eller build.gradle):

dependencies {
    constraints {
        implementation("org.yaml:snakeyaml:2.0") {
            because("fremforge security fix: pins a transitive dependency to a version that fixes its open advisories")
        }
    }
}

Den flyttar också paketets rad i gradle.lockfile, så att låsfilen och bygget stämmer överens och Gradle godtar låsfilen i strikt läge. Precis som för Maven måste den åtgärdade versionens pom deklarera samma beroenden och parent som den den ersätter. Det avvisas när:

  • byggfilen redan namnger paketet (already_declared);
  • bygget verifierar beroenden med gradle/verification-metadata.xml (gradle_verification_metadata). Den nya versionen behöver kontrollsummor i den filen, som Gradle beräknar; kör ./gradlew --write-verification-metadata sha256 själv;
  • paketet är låst i en konfiguration som en implementation-constraint inte når, till exempel ett plugins egen classpath (gradle_configuration_unsupported);
  • byggfilen har inget block dependencies { på översta nivån (build_file_unsupported).

fremforge kör inte Gradle. Ett Gradle-bygge av pull requesten i din CI kontrollerar låsfilen mot bygget och misslyckas om de inte stämmer överens.

När ingen pull request öppnas

Begäran visar då orsaken. Via API:t är reason transitive_<why> och detail förklarar den:

OrsakBetydelse
transitive_already_declaredPaketet är ett direkt beroende. Öppna fix-PR höjer det där det deklareras
transitive_transitive_dependencies_changedDen åtgärdade versionen deklarerar andra beroenden (för Python även en annan Requires-Python), så låsfilen behöver en fullständig upplösning
transitive_override_conflictEn annan kopia av paketet, i en opåverkad version, skulle också flyttas, eller så låser en override, resolution eller constraint det redan annorlunda
transitive_yarn_berry_checksumYarn 2 eller senare: dess låsfil behöver en kontrollsumma som bara Yarn kan beräkna
transitive_gradle_verification_metadataGradle: bygget verifierar beroenden, och den nya versionen behöver kontrollsummor som Gradle beräknar
transitive_gradle_configuration_unsupportedGradle: paketet är låst i en konfiguration som en implementation-constraint inte når
transitive_build_file_unsupportedGradle: byggfilen har inget block dependencies { på översta nivån
transitive_lock_entry_unsupportedLåsposten har en form som fremforge inte redigerar, till exempel ett peer-suffix i pnpm, flera låsta versioner eller en uv-låsfil från ett index som den inte betjänar
transitive_managed_by_propertyMaven: den hanterade versionen kommer från en property
transitive_already_managedMaven: hanteras redan med den åtgärdade versionen eller högre
transitive_lock_version_unsupportednpm: lockfileVersion 1. pnpm: en annan låsfilsversion än 9.0
transitive_manifest_not_supportedEn låsfil som inte listas ovan, till exempel bun.lock, pdm.lock eller Pipfile.lock
transitive_target_metadata_missingBeroendeproxyn gav ingen metadata för den åtgärdade versionen, till exempel för att en paketpolicy avvisar den
transitive_manifest_missing, transitive_lock_missingManifestet eller låsfilen finns inte på den förväntade sökvägen på standard-branchen
transitive_release_age_pendingDen åtgärdade versionen är yngre än din versionsålder; fråga igen efter det datum som detaljen anger
transitive_release_age_unknownBeroendeproxyn gav ingen publiceringstid för den åtgärdade versionen
transitive_branch_existsBranchen security-fix/<package>-<version> finns redan
transitive_repo_not_writableRepot är arkiverat eller en spegling

För en begäran via Öppna fix-PR (alla ekosystem utom Maven) behåller begäran Ingen PR: inget att ändra och detaljen anger orsaken.

API

npm, pnpm, Yarn, Python och Gradle behöver inget utöver POST /orgs/{org}/dependency-updates/security-fixes/requests. För Maven:

curl -sS -X POST "https://frem.sh/_app/api/v1/orgs/acme/dependency-updates/security-fixes/transitive-requests" \
  -H "Authorization: Bearer $FREMFORGE_TOKEN" -H "Content-Type: application/json" \
  -d '{"finding_id": "<dependency finding id>"}'

Den svarar 201 med begäran och dess utfall, pr_opened med pr_number och pr_url, eller rejected med reason och detail. Ett avslag innan en begäran finns är ett problem av typen security-fix-<reason>, till exempel security-fix-not-transitive. Scope är dependency-updates:write. Begäran listas sedan av GET …/security-fixes/requests som alla andra.