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.
| Ekosystem | Hur paketet låses | Hur 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 matchar | Som för npm |
Yarn 1 (yarn.lock) | En post i resolutions i package.json, och posten i yarn.lock flyttad | Som 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.toml | Som 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.xml | Lå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 flyttad | Som för npm |
| Go | Inget 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.jsonharlockfileVersion2 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 ipnpm.overridesipackage.jsonnärpackageManageranger pnpm 9 eller äldre, eller, utanpackageManager, när projektet har sina overrides där och saknarpnpm-workspace.yaml; pnpm-lock.yaml: dess avsnittoverrides:, paketets poster ipackagesochsnapshotsmed 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:
sdistochwheels, 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, ellerfiles.pythonhosted.orgnär du låser mot PyPI. En låsfil från något annat index avvisas medlock_entry_unsupported. - Poetry: versionen, beskrivningen,
python-versionsochfiles. Poetryscontent-hashtäcker barapyproject.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 sha256sjä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:
| Orsak | Betydelse |
|---|---|
transitive_already_declared | Paketet är ett direkt beroende. Öppna fix-PR höjer det där det deklareras |
transitive_transitive_dependencies_changed | Den å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_conflict | En 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_checksum | Yarn 2 eller senare: dess låsfil behöver en kontrollsumma som bara Yarn kan beräkna |
transitive_gradle_verification_metadata | Gradle: bygget verifierar beroenden, och den nya versionen behöver kontrollsummor som Gradle beräknar |
transitive_gradle_configuration_unsupported | Gradle: paketet är låst i en konfiguration som en implementation-constraint inte når |
transitive_build_file_unsupported | Gradle: byggfilen har inget block dependencies { på översta nivån |
transitive_lock_entry_unsupported | Lå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_property | Maven: den hanterade versionen kommer från en property |
transitive_already_managed | Maven: hanteras redan med den åtgärdade versionen eller högre |
transitive_lock_version_unsupported | npm: lockfileVersion 1. pnpm: en annan låsfilsversion än 9.0 |
transitive_manifest_not_supported | En låsfil som inte listas ovan, till exempel bun.lock, pdm.lock eller Pipfile.lock |
transitive_target_metadata_missing | Beroendeproxyn gav ingen metadata för den åtgärdade versionen, till exempel för att en paketpolicy avvisar den |
transitive_manifest_missing, transitive_lock_missing | Manifestet eller låsfilen finns inte på den förväntade sökvägen på standard-branchen |
transitive_release_age_pending | Den åtgärdade versionen är yngre än din versionsålder; fråga igen efter det datum som detaljen anger |
transitive_release_age_unknown | Beroendeproxyn gav ingen publiceringstid för den åtgärdade versionen |
transitive_branch_exists | Branchen security-fix/<package>-<version> finns redan |
transitive_repo_not_writable | Repot ä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.