Version in base suite: 6.0.5+ds1+~cs11.3.9-1 Base version: node-tar_6.0.5+ds1+~cs11.3.9-1 Target version: node-tar_6.0.5+ds1+~cs11.3.9-1+deb11u1 Base file: /srv/ftp-master.debian.org/ftp/pool/main/n/node-tar/node-tar_6.0.5+ds1+~cs11.3.9-1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/n/node-tar/node-tar_6.0.5+ds1+~cs11.3.9-1+deb11u1.dsc changelog | 10 ++ patches/CVE-2021-32803.patch | 106 ++++++++++++++++++++++++++ patches/CVE-2021-32804.patch | 175 +++++++++++++++++++++++++++++++++++++++++++ patches/series | 2 4 files changed, 293 insertions(+) diff: /srv/release.debian.org/tmp/krvltWFV9j/node-tar-6.0.5+ds1+~cs11.3.9/test/fixtures/files/strip-dir/symlink: No such file or directory diff: /srv/release.debian.org/tmp/CyDQgm110W/node-tar-6.0.5+ds1+~cs11.3.9/test/fixtures/files/strip-dir/symlink: No such file or directory diff -Nru node-tar-6.0.5+ds1+~cs11.3.9/debian/changelog node-tar-6.0.5+ds1+~cs11.3.9/debian/changelog --- node-tar-6.0.5+ds1+~cs11.3.9/debian/changelog 2021-01-07 08:48:29.000000000 +0000 +++ node-tar-6.0.5+ds1+~cs11.3.9/debian/changelog 2021-08-11 19:50:15.000000000 +0000 @@ -1,3 +1,13 @@ +node-tar (6.0.5+ds1+~cs11.3.9-1+deb11u1) bullseye; urgency=medium + + * Team upload + * Remove paths from dirCache when no longer dirs + (Closes: #992110, CVE-2021-32803) + * Strip absolute paths more comprehensively + (Closes: #992111, CVE-2021-32804) + + -- Yadd Wed, 11 Aug 2021 21:50:15 +0200 + node-tar (6.0.5+ds1+~cs11.3.9-1) unstable; urgency=medium [ Xavier Guimard ] diff -Nru node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32803.patch node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32803.patch --- node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32803.patch 1970-01-01 00:00:00.000000000 +0000 +++ node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32803.patch 2021-08-11 19:50:15.000000000 +0000 @@ -0,0 +1,106 @@ +Description: Remove paths from dirCache when no longer dirs +Author: isaacs +Origin: upstream, https://github.com/npm/node-tar/commit/9dbdeb6 +Bug: https://github.com/npm/node-tar/security/advisories/GHSA-r628-mhmh-qjhw +Bug-Debian: https://bugs.debian.org/992110 +Forwarded: not-needed +Reviewed-By: Yadd +Last-Update: 2021-08-11 + +--- a/lib/unpack.js ++++ b/lib/unpack.js +@@ -461,6 +461,19 @@ + this.reservations.reserve(paths, done => this[CHECKFS2](entry, done)) + } + [CHECKFS2] (entry, done) { ++ // if we are not creating a directory, and the path is in the dirCache, ++ // then that means we are about to delete the directory we created ++ // previously, and it is no longer going to be a directory, and neither ++ // is any of its children. ++ if (entry.type !== 'Directory') { ++ for (const path of this.dirCache.keys()) { ++ if (path === entry.absolute || ++ path.indexOf(entry.absolute + '/') === 0 || ++ path.indexOf(entry.absolute + '\\') === 0) ++ this.dirCache.delete(path) ++ } ++ } ++ + this[MKDIR](path.dirname(entry.absolute), this.dmode, er => { + if (er) { + done() +@@ -528,6 +541,15 @@ + } + + [CHECKFS] (entry) { ++ if (entry.type !== 'Directory') { ++ for (const path of this.dirCache.keys()) { ++ if (path === entry.absolute || ++ path.indexOf(entry.absolute + '/') === 0 || ++ path.indexOf(entry.absolute + '\\') === 0) ++ this.dirCache.delete(path) ++ } ++ } ++ + const er = this[MKDIR](path.dirname(entry.absolute), this.dmode, neverCalled) + if (er) + return this[ONERROR](er, entry) +--- a/test/unpack.js ++++ b/test/unpack.js +@@ -2577,3 +2577,56 @@ + cwd: dir + '/sync', strict: true, + }).end(data), poop, 'sync') + }) ++ ++t.test('drop entry from dirCache if no longer a directory', t => { ++ const dir = path.resolve(unpackdir, 'dir-cache-error') ++ mkdirp.sync(dir + '/sync/y') ++ mkdirp.sync(dir + '/async/y') ++ const data = makeTar([ ++ { ++ path: 'x', ++ type: 'Directory', ++ }, ++ { ++ path: 'x', ++ type: 'SymbolicLink', ++ linkpath: './y', ++ }, ++ { ++ path: 'x/ginkoid', ++ type: 'File', ++ size: 'ginkoid'.length, ++ }, ++ 'ginkoid', ++ '', ++ '', ++ ]) ++ t.plan(2) ++ const WARNINGS = {} ++ const check = (t, path) => { ++ t.equal(fs.statSync(path + '/x').isDirectory(), true) ++ t.equal(fs.lstatSync(path + '/x').isSymbolicLink(), true) ++ t.equal(fs.statSync(path + '/y').isDirectory(), true) ++ t.strictSame(fs.readdirSync(path + '/y'), []) ++ t.throws(() => fs.readFileSync(path + '/x/ginkoid'), { code: 'ENOENT' }) ++ t.strictSame(WARNINGS[path], [ ++ 'TAR_ENTRY_ERROR', ++ 'Cannot extract through symbolic link', ++ ]) ++ t.end() ++ } ++ t.test('async', t => { ++ const path = dir + '/async' ++ new Unpack({ cwd: path }) ++ .on('warn', (code, msg) => WARNINGS[path] = [code, msg]) ++ .on('end', () => check(t, path)) ++ .end(data) ++ }) ++ t.test('sync', t => { ++ const path = dir + '/sync' ++ new UnpackSync({ cwd: path }) ++ .on('warn', (code, msg) => WARNINGS[path] = [code, msg]) ++ .end(data) ++ check(t, path) ++ }) ++}) diff -Nru node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32804.patch node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32804.patch --- node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32804.patch 1970-01-01 00:00:00.000000000 +0000 +++ node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/CVE-2021-32804.patch 2021-08-11 19:50:15.000000000 +0000 @@ -0,0 +1,175 @@ +Description: strip absolute paths more comprehensively +Author: isaacs +Origin: upstream, https://github.com/npm/node-tar/commit/1f036ca2 +Bug: https://github.com/npm/node-tar/security/advisories/GHSA-3jfq-g458-7qm9 +Bug-Debian: https://bugs.debian.org/992111 +Forwarded: not-needed +Reviewed-By: Yadd +Last-Update: 2021-08-11 + +--- /dev/null ++++ b/lib/strip-absolute-path.js +@@ -0,0 +1,14 @@ ++// unix absolute paths are also absolute on win32, so we use this for both ++const { isAbsolute, parse } = require('path').win32 ++ ++// returns [root, stripped] ++module.exports = path => { ++ let r = '' ++ while (isAbsolute(path)) { ++ // windows will think that //x/y/z has a "root" of //x/y/ ++ const root = path.charAt(0) === '/' ? '/' : parse(path).root ++ path = path.substr(root.length) ++ r += root ++ } ++ return [r, path] ++} +--- a/lib/unpack.js ++++ b/lib/unpack.js +@@ -16,6 +16,7 @@ + const mkdirSync = mkdir.sync + const wc = require('./winchars.js') + const pathReservations = require('./path-reservations.js') ++const stripAbsolutePath = require('./strip-absolute-path.js') + + const ONENTRY = Symbol('onEntry') + const CHECKFS = Symbol('checkFs') +@@ -223,11 +224,10 @@ + + // absolutes on posix are also absolutes on win32 + // so we only need to test this one to get both +- if (path.win32.isAbsolute(p)) { +- const parsed = path.win32.parse(p) +- entry.path = p.substr(parsed.root.length) +- const r = parsed.root +- this.warn('TAR_ENTRY_INFO', `stripping ${r} from absolute path`, { ++ const [root, stripped] = stripAbsolutePath(p) ++ if (root) { ++ entry.path = stripped ++ this.warn('TAR_ENTRY_INFO', `stripping ${root} from absolute path`, { + entry, + path: p, + }) +--- a/lib/write-entry.js ++++ b/lib/write-entry.js +@@ -25,6 +25,7 @@ + const MODE = Symbol('mode') + const warner = require('./warn-mixin.js') + const winchars = require('./winchars.js') ++const stripAbsolutePath = require('./strip-absolute-path.js') + + const modeFix = require('./mode-fix.js') + +@@ -54,12 +55,12 @@ + this.on('warn', opt.onwarn) + + let pathWarn = false +- if (!this.preservePaths && path.win32.isAbsolute(p)) { +- // absolutes on posix are also absolutes on win32 +- // so we only need to test this one to get both +- const parsed = path.win32.parse(p) +- this.path = p.substr(parsed.root.length) +- pathWarn = parsed.root ++ if (!this.preservePaths) { ++ const [root, stripped] = stripAbsolutePath(this.path) ++ if (root) { ++ this.path = stripped ++ pathWarn = root ++ } + } + + this.win32 = !!opt.win32 || process.platform === 'win32' +@@ -353,10 +354,12 @@ + this.on('warn', opt.onwarn) + + let pathWarn = false +- if (path.isAbsolute(this.path) && !this.preservePaths) { +- const parsed = path.parse(this.path) +- pathWarn = parsed.root +- this.path = this.path.substr(parsed.root.length) ++ if (!this.preservePaths) { ++ const [root, stripped] = stripAbsolutePath(this.path) ++ if (root) { ++ this.path = stripped ++ pathWarn = root ++ } + } + + this.remain = readEntry.size +--- /dev/null ++++ b/test/strip-absolute-path.js +@@ -0,0 +1,14 @@ ++const t = require('tap') ++const stripAbsolutePath = require('../lib/strip-absolute-path.js') ++ ++const cases = { ++ '/': ['/', ''], ++ '////': ['////', ''], ++ 'c:///a/b/c': ['c:///', 'a/b/c'], ++ '\\\\foo\\bar\\baz': ['\\\\foo\\bar\\', 'baz'], ++ '//foo//bar//baz': ['//', 'foo//bar//baz'], ++ 'c:\\c:\\c:\\c:\\\\d:\\e/f/g': ['c:\\c:\\c:\\c:\\\\d:\\', 'e/f/g'], ++} ++ ++for (const [input, [root, stripped]] of Object.entries(cases)) ++ t.strictSame(stripAbsolutePath(input), [root, stripped], input) +--- a/test/unpack.js ++++ b/test/unpack.js +@@ -780,6 +780,9 @@ + }) + + const absolute = path.resolve(dir, 'd/i/r/absolute') ++ const root = path.parse(absolute).root ++ const extraAbsolute = root + root + root + absolute ++ t.ok(path.isAbsolute(extraAbsolute)) + t.ok(path.isAbsolute(absolute)) + const parsed = path.parse(absolute) + const relative = absolute.substr(parsed.root.length) +@@ -787,7 +790,7 @@ + + const data = makeTar([ + { +- path: absolute, ++ path: extraAbsolute, + type: 'File', + size: 1, + atime: new Date('1979-07-01T19:10:00.000Z'), +@@ -802,7 +805,7 @@ + t.test('warn and correct', t => { + const check = t => { + t.match(warnings, [[ +- 'stripping / from absolute path', ++ `stripping ${root}${root}${root}${root} from absolute path`, + { path: absolute, code: 'TAR_ENTRY_INFO' }, + ]]) + t.ok(fs.lstatSync(path.resolve(dir, relative)).isFile(), 'is file') +--- a/test/write-entry.js ++++ b/test/write-entry.js +@@ -399,7 +399,10 @@ + + /* + t.test('absolute path', t => { +- const f = path.resolve(files, '512-bytes.txt') ++ const absolute = path.resolve(files, '512-bytes.txt') ++ const { root } = path.parse(absolute) ++ const f = root + root + root + absolute ++ const warn = root + root + root + root + t.test('preservePaths=false strict=false', t => { + const warnings = [] + const ws = new WriteEntry(f, { +@@ -412,13 +415,13 @@ + out = Buffer.concat(out) + t.equal(out.length, 1024) + t.match(warnings, [[ +- 'TAR_ENTRY_INFO', /stripping .* from absolute path/, { path: f } ++ 'TAR_ENTRY_INFO', `stripping ${warn} from absolute path`, { path: f }, + ]]) + + t.match(ws.header, { + cksumValid: true, + needPax: false, +- path: f.replace(/^(\/|[a-z]:\\\\)/, ''), ++ path: f.replace(/^(\/|[a-z]:\\\\){4}/, ''), + mode: 0o644, + size: 512, + linkpath: null, diff -Nru node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/series node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/series --- node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/series 2021-01-07 08:48:29.000000000 +0000 +++ node-tar-6.0.5+ds1+~cs11.3.9/debian/patches/series 2021-08-11 19:50:15.000000000 +0000 @@ -1,2 +1,4 @@ api-backward-compatibility.patch disable-failing-tests.diff +CVE-2021-32803.patch +CVE-2021-32804.patch