Version in base suite: 5.8.0+ds6-4 Base version: npm_5.8.0+ds6-4 Target version: npm_5.8.0+ds6-4+deb10u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/npm/npm_5.8.0+ds6-4.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/npm/npm_5.8.0+ds6-4+deb10u1.dsc changelog | 7 patches/CVE-2019-16775-add-npm-normalize-package-bin.diff | 167 ++++++++++++++ patches/CVE-2019-16775-bin-links.diff | 60 +++++ patches/CVE-2019-16775-npm-packlist.diff | 29 ++ patches/CVE-2019-16775-pacote.diff | 58 ++++ patches/series | 4 6 files changed, 325 insertions(+) diff -Nru npm-5.8.0+ds6/debian/changelog npm-5.8.0+ds6/debian/changelog --- npm-5.8.0+ds6/debian/changelog 2019-02-13 22:01:53.000000000 +0000 +++ npm-5.8.0+ds6/debian/changelog 2019-12-15 15:19:02.000000000 +0000 @@ -1,3 +1,10 @@ +npm (5.8.0+ds6-4+deb10u1) buster; urgency=medium + + * Add patches to fix arbitrary path access + (Closes: CVE-2019-16775, CVE-2019-16776, CVE-2019-16777) + + -- Xavier Guimard Sun, 15 Dec 2019 16:19:02 +0100 + npm (5.8.0+ds6-4) unstable; urgency=medium * Team upload diff -Nru npm-5.8.0+ds6/debian/patches/CVE-2019-16775-add-npm-normalize-package-bin.diff npm-5.8.0+ds6/debian/patches/CVE-2019-16775-add-npm-normalize-package-bin.diff --- npm-5.8.0+ds6/debian/patches/CVE-2019-16775-add-npm-normalize-package-bin.diff 1970-01-01 00:00:00.000000000 +0000 +++ npm-5.8.0+ds6/debian/patches/CVE-2019-16775-add-npm-normalize-package-bin.diff 2019-12-15 15:19:02.000000000 +0000 @@ -0,0 +1,167 @@ +Description: Add npm-normalize-package-bin package + Needed to CVE-2019-16775 fix +Author: isaacs +Bug: https://github.com/npm/cli/security/advisories/GHSA-x8qc-rrcw-4r46 +Forwarded: not-needed +Reviewed-By: Xavier Guimard +Last-Update: 2019-12-15 + +--- /dev/null ++++ b/node_modules/npm-normalize-package-bin/LICENSE +@@ -0,0 +1,15 @@ ++The ISC License ++ ++Copyright (c) npm, Inc. ++ ++Permission to use, copy, modify, and/or distribute this software for any ++purpose with or without fee is hereby granted, provided that the above ++copyright notice and this permission notice appear in all copies. ++ ++THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES ++WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF ++MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ++ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES ++WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ++ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR ++IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +--- /dev/null ++++ b/node_modules/npm-normalize-package-bin/README.md +@@ -0,0 +1,14 @@ ++# npm-normalize-package-bin ++ ++Turn any flavor of allowable package.json bin into a normalized object. ++ ++## API ++ ++```js ++const normalize = require('npm-normalize-package-bin') ++const pkg = {name: 'foo', bin: 'bar'} ++console.log(normalize(pkg)) // {name:'foo', bin:{foo: 'bar'}} ++``` ++ ++Also strips out weird dots and slashes to prevent accidental and/or ++malicious bad behavior when the package is installed. +--- /dev/null ++++ b/node_modules/npm-normalize-package-bin/index.js +@@ -0,0 +1,60 @@ ++// pass in a manifest with a 'bin' field here, and it'll turn it ++// into a properly santized bin object ++const {join, basename} = require('path') ++ ++const normalize = pkg => ++ !pkg.bin ? removeBin(pkg) ++ : typeof pkg.bin === 'string' ? normalizeString(pkg) ++ : Array.isArray(pkg.bin) ? normalizeArray(pkg) ++ : typeof pkg.bin === 'object' ? normalizeObject(pkg) ++ : removeBin(pkg) ++ ++const normalizeString = pkg => { ++ if (!pkg.name) ++ return removeBin(pkg) ++ pkg.bin = { [pkg.name]: pkg.bin } ++ return normalizeObject(pkg) ++} ++ ++const normalizeArray = pkg => { ++ pkg.bin = pkg.bin.reduce((acc, k) => { ++ acc[basename(k)] = k ++ return acc ++ }, {}) ++ return normalizeObject(pkg) ++} ++ ++const removeBin = pkg => { ++ delete pkg.bin ++ return pkg ++} ++ ++const normalizeObject = pkg => { ++ const orig = pkg.bin ++ const clean = {} ++ let hasBins = false ++ Object.keys(orig).forEach(binKey => { ++ const base = join('/', basename(binKey.replace(/\\|:/g, '/'))).substr(1) ++ ++ if (typeof orig[binKey] !== 'string' || !base) ++ return ++ ++ const binTarget = join('/', orig[binKey]) ++ .replace(/\\/g, '/').substr(1) ++ ++ if (!binTarget) ++ return ++ ++ clean[base] = binTarget ++ hasBins = true ++ }) ++ ++ if (hasBins) ++ pkg.bin = clean ++ else ++ delete pkg.bin ++ ++ return pkg ++} ++ ++module.exports = normalize +--- /dev/null ++++ b/node_modules/npm-normalize-package-bin/package.json +@@ -0,0 +1,58 @@ ++{ ++ "_from": "npm-normalize-package-bin", ++ "_id": "npm-normalize-package-bin@1.0.1", ++ "_inBundle": false, ++ "_integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA==", ++ "_location": "/npm-normalize-package-bin", ++ "_phantomChildren": {}, ++ "_requested": { ++ "type": "tag", ++ "registry": true, ++ "raw": "npm-normalize-package-bin", ++ "name": "npm-normalize-package-bin", ++ "escapedName": "npm-normalize-package-bin", ++ "rawSpec": "", ++ "saveSpec": null, ++ "fetchSpec": "latest" ++ }, ++ "_requiredBy": [ ++ "#USER", ++ "/" ++ ], ++ "_resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz", ++ "_shasum": "6e79a41f23fd235c0623218228da7d9c23b8f6e2", ++ "_spec": "npm-normalize-package-bin", ++ "_where": "/tmp/tt", ++ "author": { ++ "name": "Isaac Z. Schlueter", ++ "email": "i@izs.me", ++ "url": "https://izs.me" ++ }, ++ "bugs": { ++ "url": "https://github.com/npm/npm-normalize-package-bin/issues" ++ }, ++ "bundleDependencies": false, ++ "deprecated": false, ++ "description": "Turn any flavor of allowable package.json bin into a normalized object", ++ "devDependencies": { ++ "tap": "^14.10.2" ++ }, ++ "homepage": "https://github.com/npm/npm-normalize-package-bin#readme", ++ "license": "ISC", ++ "name": "npm-normalize-package-bin", ++ "repository": { ++ "type": "git", ++ "url": "git+https://github.com/npm/npm-normalize-package-bin.git" ++ }, ++ "scripts": { ++ "postpublish": "git push origin --follow-tags", ++ "postversion": "npm publish", ++ "preversion": "npm test", ++ "snap": "tap", ++ "test": "tap" ++ }, ++ "tap": { ++ "check-coverage": true ++ }, ++ "version": "1.0.1" ++} diff -Nru npm-5.8.0+ds6/debian/patches/CVE-2019-16775-bin-links.diff npm-5.8.0+ds6/debian/patches/CVE-2019-16775-bin-links.diff --- npm-5.8.0+ds6/debian/patches/CVE-2019-16775-bin-links.diff 1970-01-01 00:00:00.000000000 +0000 +++ npm-5.8.0+ds6/debian/patches/CVE-2019-16775-bin-links.diff 2019-12-15 15:19:02.000000000 +0000 @@ -0,0 +1,60 @@ +Description: sanitize and validate bin and man link targets + Part of CVE-2019-16776 fix +Author: isaacs +Origin: upstream, https://github.com/npm/bin-links/commit/25a34f9 +Bug: https://github.com/npm/cli/security/advisories/GHSA-x8qc-rrcw-4r46 +Forwarded: not-needed +Reviewed-By: Xavier Guimard +Last-Update: 2019-12-15 + +--- a/node_modules/bin-links/index.js ++++ b/node_modules/bin-links/index.js +@@ -12,10 +12,12 @@ + const chmod = BB.promisify(fs.chmod) + const Transform = require('stream').Transform + const fsWriteStreamAtomic = require('fs-write-stream-atomic') ++const normalize = require('npm-normalize-package-bin') + + module.exports = BB.promisify(binLinks) + + function binLinks (pkg, folder, global, opts, cb) { ++ pkg = normalize(pkg) + // if it's global, and folder is in {prefix}/node_modules, + // then bins are in {prefix}/bin + // otherwise, then bins are in folder/../.bin +@@ -107,6 +109,12 @@ + var dest = path.resolve(binRoot, bin) + var src = path.resolve(folder, pkg.bin[bin]) + ++ /* istanbul ignore if - that unpossible */ ++ if (src.indexOf(folder) !== 0) { ++ throw new Error('invalid bin entry for package ' + ++ pkg._id + '. key=' + bin + ', value=' + pkg.bin[bin]) ++ } ++ + linkBin(src, dest, linkOpts, function (er) { + if (er) return cb(er) + // bins should always be executable. +@@ -157,7 +165,8 @@ + // make sure that the mans are unique. + // otherwise, if there are dupes, it'll fail with EEXIST + var set = pkg.man.reduce(function (acc, man) { +- acc[path.basename(man)] = man ++ const cleanMan = path.join('/', man).replace(/\\|:/g, '/').substr(1) ++ acc[path.basename(man)] = cleanMan + return acc + }, {}) + pkg.man = pkg.man.filter(function (man) { +@@ -180,6 +189,12 @@ + var sxn = parseMan[2] + var bn = path.basename(stem) + var manSrc = path.resolve(folder, man) ++ /* istanbul ignore if - that unpossible */ ++ if (manSrc.indexOf(folder) !== 0) { ++ throw new Error('invalid man entry for package ' + ++ pkg._id + '. man=' + manSrc) ++ } ++ + var manDest = path.join(manRoot, 'man' + sxn, bn) + + linkIfExists(manSrc, manDest, getLinkOpts(opts, gtop && folder), cb) diff -Nru npm-5.8.0+ds6/debian/patches/CVE-2019-16775-npm-packlist.diff npm-5.8.0+ds6/debian/patches/CVE-2019-16775-npm-packlist.diff --- npm-5.8.0+ds6/debian/patches/CVE-2019-16775-npm-packlist.diff 1970-01-01 00:00:00.000000000 +0000 +++ npm-5.8.0+ds6/debian/patches/CVE-2019-16775-npm-packlist.diff 2019-12-15 15:19:02.000000000 +0000 @@ -0,0 +1,29 @@ +Description: sanitize and normalize package bin field + Part of CVE-2019-16776 fix +Author: isaacs +Origin: upstream, https://github.com/npm/npm-packlist/commit/ec4d56e +Bug: https://github.com/npm/cli/security/advisories/GHSA-x8qc-rrcw-4r46 +Forwarded: not-needed +Reviewed-By: Xavier Guimard +Last-Update: 2019-12-15 + +--- a/node_modules/npm-packlist/index.js ++++ b/node_modules/npm-packlist/index.js +@@ -17,6 +17,8 @@ + const packageNecessaryRules = Symbol('package-necessary-rules') + const path = require('path') + ++const normalizePackageBin = require('npm-normalize-package-bin') ++ + const defaultRules = [ + '.npmignore', + '.gitignore', +@@ -154,7 +156,7 @@ + onReadIgnoreFile (file, data, then) { + if (file === 'package.json') + try { +- this.onPackageJson(file, JSON.parse(data), then) ++ this.onPackageJson(file, normalizePackageBin(JSON.parse(data)), then) + } catch (er) { + // ignore package.json files that are not json + then() diff -Nru npm-5.8.0+ds6/debian/patches/CVE-2019-16775-pacote.diff npm-5.8.0+ds6/debian/patches/CVE-2019-16775-pacote.diff --- npm-5.8.0+ds6/debian/patches/CVE-2019-16775-pacote.diff 1970-01-01 00:00:00.000000000 +0000 +++ npm-5.8.0+ds6/debian/patches/CVE-2019-16775-pacote.diff 2019-12-15 15:19:02.000000000 +0000 @@ -0,0 +1,58 @@ +Description: sanitize and normalize package bin field + Part of CVE-2019-16776 fix +Author: isaacs +Origin: upstream, https://github.com/npm/pacote/commit/6f229f7 +Bug: https://github.com/npm/cli/security/advisories/GHSA-x8qc-rrcw-4r46 +Forwarded: not-needed +Reviewed-By: Xavier Guimard +Last-Update: 2019-12-15 + +--- a/node_modules/pacote/lib/fetchers/directory.js ++++ b/node_modules/pacote/lib/fetchers/directory.js +@@ -8,6 +8,7 @@ + const path = require('path') + const pipe = BB.promisify(require('mississippi').pipe) + const through = require('mississippi').through ++const normalizePackageBin = require('npm-normalize-package-bin') + + const readFileAsync = BB.promisify(require('fs').readFile) + +@@ -46,7 +47,7 @@ + } else { + return pkg + } +- }) ++ }).then(pkg => normalizePackageBin(pkg)) + }, + + // As of npm@5, the npm installer doesn't pack + install directories: it just +--- a/node_modules/pacote/lib/finalize-manifest.js ++++ b/node_modules/pacote/lib/finalize-manifest.js +@@ -13,6 +13,7 @@ + const pipe = BB.promisify(require('mississippi').pipe) + const ssri = require('ssri') + const tar = require('tar') ++const normalizePackageBin = require('npm-normalize-package-bin') + + // `finalizeManifest` takes as input the various kinds of manifests that + // manifest handlers ('lib/fetchers/*.js#manifest()') return, and makes sure +@@ -102,17 +103,8 @@ + this._shrinkwrap = pkg._shrinkwrap || fromTarball._shrinkwrap || null + this.bin = pkg.bin || fromTarball.bin || null + +- if (this.bin && Array.isArray(this.bin)) { +- // Code yanked from read-package-json. +- const m = (pkg.directories && pkg.directories.bin) || '.' +- this.bin = this.bin.reduce((acc, mf) => { +- if (mf && mf.charAt(0) !== '.') { +- const f = path.basename(mf) +- acc[f] = path.join(m, mf) +- } +- return acc +- }, {}) +- } ++ // turn arrays and strings into a legit object, strip out bad stuff ++ normalizePackageBin(this) + + this._id = null + diff -Nru npm-5.8.0+ds6/debian/patches/series npm-5.8.0+ds6/debian/patches/series --- npm-5.8.0+ds6/debian/patches/series 2019-02-13 21:44:36.000000000 +0000 +++ npm-5.8.0+ds6/debian/patches/series 2019-12-15 15:19:02.000000000 +0000 @@ -9,3 +9,7 @@ 2014_remove_readable_stream.patch 2015_use_system_libnpx_1.patch new-lru-cache.patch +CVE-2019-16775-bin-links.diff +CVE-2019-16775-npm-packlist.diff +CVE-2019-16775-pacote.diff +CVE-2019-16775-add-npm-normalize-package-bin.diff