From b2183a30c58722ea56cc24f8d353dbe2f7f3f866 Mon Sep 17 00:00:00 2001 From: Rhys Arkins Date: Fri, 11 Feb 2022 14:20:55 +0100 Subject: [PATCH] fix(npm): lockfileVersion 2+ transitiveRemediation only if package.json changes (#14173) --- docs/usage/configuration-options.md | 4 +- .../update/locked-dependency/index.spec.ts | 29 ++ .../__fixtures__/package-lock-v2.json | 359 ++++++++++++++++++ .../locked-dependency/package-lock/index.ts | 8 +- 4 files changed, 398 insertions(+), 2 deletions(-) create mode 100644 lib/manager/npm/update/locked-dependency/package-lock/__fixtures__/package-lock-v2.json diff --git a/docs/usage/configuration-options.md b/docs/usage/configuration-options.md index afeb15d755..ba4b5a41aa 100644 --- a/docs/usage/configuration-options.md +++ b/docs/usage/configuration-options.md @@ -2638,7 +2638,9 @@ Please see the above link for valid timezone names. When enabled, Renovate will attempt to remediate vulnerabilities even if they exist only in transitive dependencies. -Applicable only for GitHub platform (with vulnerability alerts enabled), `npm` manager, and when a `package-lock.json` v1 format is present. +Applicable only for GitHub platform (with vulnerability alerts enabled) and `npm` manager. +When the `lockfileVersion` is higher than `1` in `package-lock.json`, remediations are only possible when changes are made to `package.json`. + This is considered a feature flag with the aim to remove it and default to this behavior once it has been more widely tested. ## unicodeEmoji diff --git a/lib/manager/npm/update/locked-dependency/index.spec.ts b/lib/manager/npm/update/locked-dependency/index.spec.ts index db50a395d8..f06d2114a3 100644 --- a/lib/manager/npm/update/locked-dependency/index.spec.ts +++ b/lib/manager/npm/update/locked-dependency/index.spec.ts @@ -6,6 +6,7 @@ import { updateLockedDependency } from '.'; const packageFileContent = loadFixture('package.json', './package-lock'); const lockFileContent = loadFixture('package-lock.json', './package-lock'); +const lockFileV2Content = loadFixture('package-lock-v2.json', './package-lock'); const acceptsJson = JSON.parse(loadFixture('accepts.json', './package-lock')); const expressJson = JSON.parse(loadFixture('express.json', './common')); const mimeJson = JSON.parse(loadFixture('mime.json', './package-lock')); @@ -92,6 +93,16 @@ describe('manager/npm/update/locked-dependency/index', () => { JSON.parse(res.files['package-lock.json']).dependencies.mime.version ).toBe('1.2.12'); }); + it('rejects in-range remediation if lockfile v2+', async () => { + const res = await updateLockedDependency({ + ...config, + lockFileContent: lockFileV2Content, + depName: 'mime', + currentVersion: '1.2.11', + newVersion: '1.2.12', + }); + expect(res.status).toBe('unsupported'); + }); it('fails to remediate if parent dep cannot support', async () => { const acceptsModified = clone(acceptsJson); acceptsModified.versions['2.0.0'] = {}; @@ -120,6 +131,16 @@ describe('manager/npm/update/locked-dependency/index', () => { const packageLock = JSON.parse(res.files['package-lock.json']); expect(packageLock.dependencies.express.version).toBe('4.1.0'); }); + it('remediates lock file v2 express', async () => { + config.depName = 'express'; + config.currentVersion = '4.0.0'; + config.newVersion = '4.1.0'; + config.lockFileContent = lockFileV2Content; + const res = await updateLockedDependency(config); + expect(res.files['package.json']).toContain('"express": "4.1.0"'); + const packageLock = JSON.parse(res.files['package-lock.json']); + expect(packageLock.dependencies.express.version).toBe('4.1.0'); + }); it('returns already-updated if already remediated exactly', async () => { config.depName = 'mime'; config.currentVersion = '1.2.10'; @@ -127,6 +148,14 @@ describe('manager/npm/update/locked-dependency/index', () => { const res = await updateLockedDependency(config); expect(res.status).toBe('already-updated'); }); + it('returns already-updated if already v2 remediated exactly', async () => { + config.depName = 'mime'; + config.currentVersion = '1.2.10'; + config.newVersion = '1.2.11'; + config.lockFileContent = lockFileV2Content; + const res = await updateLockedDependency(config); + expect(res.status).toBe('already-updated'); + }); it('returns already-updated if already remediated higher', async () => { config.depName = 'mime'; config.currentVersion = '1.2.9'; diff --git a/lib/manager/npm/update/locked-dependency/package-lock/__fixtures__/package-lock-v2.json b/lib/manager/npm/update/locked-dependency/package-lock/__fixtures__/package-lock-v2.json new file mode 100644 index 0000000000..afeb819e0f --- /dev/null +++ b/lib/manager/npm/update/locked-dependency/package-lock/__fixtures__/package-lock-v2.json @@ -0,0 +1,359 @@ +{ + "name": "npm90", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "npm90", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "express": "4.0.0" + } + }, + "node_modules/accepts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.0.0.tgz", + "integrity": "sha1-NgTHZVhsO5z3h3tpN829RYf5R9w=", + "dependencies": { + "mime": "~1.2.11", + "negotiator": "~0.3.0" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", + "integrity": "sha1-vj5TgvwCttYySVasGvmKqYsIU0w=", + "engines": { + "node": "*" + } + }, + "node_modules/cookie": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", + "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=", + "engines": { + "node": "*" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.3.tgz", + "integrity": "sha1-kc2ZfMUftkFZVzjGnNoCAyj1D/k=" + }, + "node_modules/debug": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz", + "integrity": "sha1-IP9NJvXkIstoobrLu2EDmtjBwTA=", + "engines": { + "node": "*" + } + }, + "node_modules/escape-html": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", + "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=" + }, + "node_modules/express": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.0.0.tgz", + "integrity": "sha1-J03IKTPJ9XTMOKDOXqgXK+nGsJQ=", + "dependencies": { + "accepts": "1.0.0", + "buffer-crc32": "0.2.1", + "cookie": "0.1.0", + "cookie-signature": "1.0.3", + "debug": ">= 0.7.3 < 1", + "escape-html": "1.0.1", + "fresh": "0.2.2", + "merge-descriptors": "0.0.2", + "methods": "0.1.0", + "parseurl": "1.0.1", + "path-to-regexp": "0.1.2", + "qs": "0.6.6", + "range-parser": "1.0.0", + "send": "0.2.0", + "serve-static": "1.0.1", + "type-is": "1.0.0", + "utils-merge": "1.0.0" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/fresh": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.2.tgz", + "integrity": "sha1-lzHc9WeMf660T7kDxPct9VGH+nc=" + }, + "node_modules/merge-descriptors": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz", + "integrity": "sha1-w2pSp4FDdRPFcnXzndnTF1FKyMc=" + }, + "node_modules/methods": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.1.0.tgz", + "integrity": "sha1-M11Cnu/SG3us8unJIqjSvRSjDk8=" + }, + "node_modules/mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "node_modules/negotiator": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.3.0.tgz", + "integrity": "sha1-cG1pLv7d9XTVfqn7GriaT6fuj2A=", + "engines": { + "node": "*" + } + }, + "node_modules/parseurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.0.1.tgz", + "integrity": "sha1-Llfc5u/dN8NRhwEDCUTCK/OIt7Q=" + }, + "node_modules/path-to-regexp": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.2.tgz", + "integrity": "sha1-mysVH5zDAYye6lDKlXKeBXgXErQ=" + }, + "node_modules/qs": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=", + "engines": { + "node": "*" + } + }, + "node_modules/range-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.0.tgz", + "integrity": "sha1-pLJkz+C+XONqvjdlrJwqJIdG28A=" + }, + "node_modules/send": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.2.0.tgz", + "integrity": "sha1-Bnq/Rc/4v/spy9t0OXJbMjiKLFg=", + "dependencies": { + "debug": "*", + "fresh": "~0.2.1", + "mime": "~1.2.9", + "range-parser": "~1.0.0" + } + }, + "node_modules/serve-static": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.0.1.tgz", + "integrity": "sha1-ENy/1Es+ApGhMfyatKslqfWnikI=", + "dependencies": { + "send": "0.1.4" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/serve-static/node_modules/fresh": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", + "integrity": "sha1-v9lALPPfEsSkwxDHn5mj3eE9NKc=" + }, + "node_modules/serve-static/node_modules/range-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", + "integrity": "sha1-wEJ//vUcEKy6B4KkbJYC50T/Ygs=", + "engines": { + "node": "*" + } + }, + "node_modules/serve-static/node_modules/send": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", + "integrity": "sha1-vnDY0b4B3mGCGvE3gLUDRaT3Gr0=", + "dependencies": { + "debug": "*", + "fresh": "0.2.0", + "mime": "~1.2.9", + "range-parser": "0.0.4" + } + }, + "node_modules/type-is": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.0.0.tgz", + "integrity": "sha1-T/Qk6XNJoe4ZELS/xIhZXs3EQ/w=", + "dependencies": { + "mime": "~1.2.11" + } + }, + "node_modules/utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=", + "engines": { + "node": ">= 0.4.0" + } + } + }, + "dependencies": { + "accepts": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.0.0.tgz", + "integrity": "sha1-NgTHZVhsO5z3h3tpN829RYf5R9w=", + "requires": { + "mime": "~1.2.11", + "negotiator": "~0.3.0" + } + }, + "buffer-crc32": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.1.tgz", + "integrity": "sha1-vj5TgvwCttYySVasGvmKqYsIU0w=" + }, + "cookie": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.1.0.tgz", + "integrity": "sha1-kOtGndzpBchm3mh+/EMTHYgB+dA=" + }, + "cookie-signature": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.3.tgz", + "integrity": "sha1-kc2ZfMUftkFZVzjGnNoCAyj1D/k=" + }, + "debug": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz", + "integrity": "sha1-IP9NJvXkIstoobrLu2EDmtjBwTA=" + }, + "escape-html": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.1.tgz", + "integrity": "sha1-GBoobq05ejmpKFfPsdQwUuNWv/A=" + }, + "express": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/express/-/express-4.0.0.tgz", + "integrity": "sha1-J03IKTPJ9XTMOKDOXqgXK+nGsJQ=", + "requires": { + "accepts": "1.0.0", + "buffer-crc32": "0.2.1", + "cookie": "0.1.0", + "cookie-signature": "1.0.3", + "debug": ">= 0.7.3 < 1", + "escape-html": "1.0.1", + "fresh": "0.2.2", + "merge-descriptors": "0.0.2", + "methods": "0.1.0", + "parseurl": "1.0.1", + "path-to-regexp": "0.1.2", + "qs": "0.6.6", + "range-parser": "1.0.0", + "send": "0.2.0", + "serve-static": "1.0.1", + "type-is": "1.0.0", + "utils-merge": "1.0.0" + } + }, + "fresh": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.2.tgz", + "integrity": "sha1-lzHc9WeMf660T7kDxPct9VGH+nc=" + }, + "merge-descriptors": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-0.0.2.tgz", + "integrity": "sha1-w2pSp4FDdRPFcnXzndnTF1FKyMc=" + }, + "methods": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/methods/-/methods-0.1.0.tgz", + "integrity": "sha1-M11Cnu/SG3us8unJIqjSvRSjDk8=" + }, + "mime": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", + "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=" + }, + "negotiator": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.3.0.tgz", + "integrity": "sha1-cG1pLv7d9XTVfqn7GriaT6fuj2A=" + }, + "parseurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.0.1.tgz", + "integrity": "sha1-Llfc5u/dN8NRhwEDCUTCK/OIt7Q=" + }, + "path-to-regexp": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.2.tgz", + "integrity": "sha1-mysVH5zDAYye6lDKlXKeBXgXErQ=" + }, + "qs": { + "version": "0.6.6", + "resolved": "https://registry.npmjs.org/qs/-/qs-0.6.6.tgz", + "integrity": "sha1-bgFQmP9RlouKPIGQAdXyyJvEsQc=" + }, + "range-parser": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.0.0.tgz", + "integrity": "sha1-pLJkz+C+XONqvjdlrJwqJIdG28A=" + }, + "send": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/send/-/send-0.2.0.tgz", + "integrity": "sha1-Bnq/Rc/4v/spy9t0OXJbMjiKLFg=", + "requires": { + "debug": "*", + "fresh": "~0.2.1", + "mime": "~1.2.9", + "range-parser": "~1.0.0" + } + }, + "serve-static": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.0.1.tgz", + "integrity": "sha1-ENy/1Es+ApGhMfyatKslqfWnikI=", + "requires": { + "send": "0.1.4" + }, + "dependencies": { + "fresh": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.2.0.tgz", + "integrity": "sha1-v9lALPPfEsSkwxDHn5mj3eE9NKc=" + }, + "range-parser": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-0.0.4.tgz", + "integrity": "sha1-wEJ//vUcEKy6B4KkbJYC50T/Ygs=" + }, + "send": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/send/-/send-0.1.4.tgz", + "integrity": "sha1-vnDY0b4B3mGCGvE3gLUDRaT3Gr0=", + "requires": { + "debug": "*", + "fresh": "0.2.0", + "mime": "~1.2.9", + "range-parser": "0.0.4" + } + } + } + }, + "type-is": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.0.0.tgz", + "integrity": "sha1-T/Qk6XNJoe4ZELS/xIhZXs3EQ/w=", + "requires": { + "mime": "~1.2.11" + } + }, + "utils-merge": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.0.tgz", + "integrity": "sha1-ApT7kiu5N1FTVBxPcJYjHyh8ivg=" + } + } +} diff --git a/lib/manager/npm/update/locked-dependency/package-lock/index.ts b/lib/manager/npm/update/locked-dependency/package-lock/index.ts index d04035db36..9848a76c93 100644 --- a/lib/manager/npm/update/locked-dependency/package-lock/index.ts +++ b/lib/manager/npm/update/locked-dependency/package-lock/index.ts @@ -39,6 +39,7 @@ export async function updateLockedDependency( logger.warn({ err }, 'Failed to parse files'); return { status: 'update-failed' }; } + const { lockfileVersion } = packageLockJson; const lockedDeps = getLockedDependencies( packageLockJson, depName, @@ -63,7 +64,7 @@ export async function updateLockedDependency( ); status = 'already-updated'; } else { - if (packageLockJson.lockfileVersion !== 1) { + if (lockfileVersion !== 1) { logger.debug( `Found lockfileVersion ${packageLockJson.lockfileVersion}` ); @@ -240,6 +241,11 @@ export async function updateLockedDependency( } if (newPackageJsonContent) { files[packageFile] = newPackageJsonContent; + } else if (lockfileVersion !== 1) { + logger.debug( + 'Remediations which change package-lock.json only are not supported unless lockfileVersion=1' + ); + return { status: 'unsupported' }; } return { status: 'updated', files }; } catch (err) /* istanbul ignore next */ {