[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#992114: marked as done (bullseye-pu: package node-tar/6.0.5+ds1+~cs11.3.9-1+deb11u1)



Your message dated Sat, 09 Oct 2021 12:09:40 +0100
with message-id <81741a2f4e370c14a3bec08b7fe6e2b10c32267b.camel@adam-barratt.org.uk>
and subject line Closing p-u bugs for updates in 11.1
has caused the Debian Bug report #992114,
regarding bullseye-pu: package node-tar/6.0.5+ds1+~cs11.3.9-1+deb11u1
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact owner@bugs.debian.org
immediately.)


-- 
992114: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=992114
Debian Bug Tracking System
Contact owner@bugs.debian.org with problems
--- Begin Message ---
Package: release.debian.org
Severity: normal
Tags: bullseye
User: release.debian.org@packages.debian.org
Usertags: pu

[ Reason ]
node-tar is vulnerable to 2 CVE:
 * #992110, CVE-2021-32803: arbitrary File Creation/Overwrite
   vulnerability via insufficient symlink protection
 * #992111, CVE-2021-32804: arbitrary File Creation/Overwrite
   vulnerability due to insufficient absolute path sanitization

[ Impact ]
2 medium vulnerabilities

[ Tests ]
Test updated (not fully launched because it needs a newer node-tap)

[ Risks ]
Low risk: test passed and upstream patch applied with minor changes

[ Checklist ]
  [X] *all* changes are documented in the d/changelog
  [X] I reviewed all changes and I approve them
  [X] attach debdiff against the package in (old)stable
  [X] the issue is verified as fixed in unstable

[ Changes ]
Add new checks

Cheers,
Yadd
diff --git a/debian/changelog b/debian/changelog
index f8f5426..e16bf2f 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -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 <yadd@debian.org>  Wed, 11 Aug 2021 21:50:15 +0200
+
 node-tar (6.0.5+ds1+~cs11.3.9-1) unstable; urgency=medium
 
   [ Xavier Guimard ]
diff --git a/debian/patches/CVE-2021-32803.patch b/debian/patches/CVE-2021-32803.patch
new file mode 100644
index 0000000..5328879
--- /dev/null
+++ b/debian/patches/CVE-2021-32803.patch
@@ -0,0 +1,106 @@
+Description: Remove paths from dirCache when no longer dirs
+Author: isaacs <i@izs.me>
+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 <yadd@debian.org>
+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 --git a/debian/patches/CVE-2021-32804.patch b/debian/patches/CVE-2021-32804.patch
new file mode 100644
index 0000000..304892d
--- /dev/null
+++ b/debian/patches/CVE-2021-32804.patch
@@ -0,0 +1,175 @@
+Description: strip absolute paths more comprehensively
+Author: isaacs <i@izs.me>
+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 <yadd@debian.org>
+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 --git a/debian/patches/series b/debian/patches/series
index 30956fd..931f724 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1,4 @@
 api-backward-compatibility.patch
 disable-failing-tests.diff
+CVE-2021-32803.patch
+CVE-2021-32804.patch

--- End Message ---
--- Begin Message ---
Package: release.debian.org
Version: 11.1

Hi,

The updates relating to these bugs were included in this morning's 11.1
point release for bullseye.

Regards,

Adam

--- End Message ---

Reply to: