[Pkg-javascript-devel] Bug#1140683: bookworm-pu: package node-tar/6.1.13+~cs7.0.5-1+deb12u1
Daniel Leidert
dleidert at debian.org
Wed Jun 24 15:08:59 BST 2026
Package: release.debian.org
Severity: normal
Tags: bookworm
X-Debbugs-Cc: node-tar at packages.debian.org
Control: affects -1 + src:node-tar
User: release.debian.org at packages.debian.org
Usertags: pu
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512
[ Reason ]
node-tar in Bookworm is vulnerable to multiple CVEs.
- CVE-2024-28863: excessive memory consumption
- CVE-2026-23745: sanitize absolute linkpaths properly
(the fix opens CVE-2026-24842 and CVE-2026-31802)
- CVE-2026-26960: do not write linkpaths through symlinks
- CVE-2026-29786: parse root off paths before sanitizing parts
By fixing CVE-2026-23745, it becomes necessary to fix CVE-2026-24842 and
CVE-2026-31802 as well, because the fix introduces the vulnerable code for
these issues:
- CVE-2026-24842: properly sanitize hard links containing '..'
- CVE-2026-31802: prevent escaping symlinks with drive-relative paths
[ Impact ]
The issues are currently fixed in Bullseye and in Trixie. Thus, users of
Bookworm are, and users upgrading to Bookworm become vulnerable to these
issues.
[ Tests ]
All tests are passing.
[ Risks ]
There is the risk of regression. However, tests are passing and the fixes have
been successfully applied and tested in Bullseye and Trixie.
[ 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
-----BEGIN PGP SIGNATURE-----
iQIzBAEBCgAdFiEEvu1N7VVEpMA+KD3HS80FZ8KW0F0FAmo75PsACgkQS80FZ8KW
0F3iEhAA2OWEX4qZ9BGLa2gyoMr6XAf+cA4DWG97PL1jmVlabf6/ZkOhVnF6oWbA
Z4+pfKiYsI4uur23lwn4jgU0kEyB0OA6fYOGQf5madlb9O4AyX4D09js2PKPhllv
+PqSx9JYJEHVSWqGaaDSkGjUjYNd0xp3GkZNlPZAc3tj9fQ+JiGitBr5gtwv1bz8
bitozUS2k5aAnzTWZYfnObG6MvoleQaDxzW2y5dNs5xve0A72Ahc5K0UCuwbl/l9
rCiLMvDym0jjLhI0DNDtd1lBETQrArXZ/qwE/onnMKz+T4eY5XBtKXYfleQ3bSwz
qfoC/VPYbVkx7GzemZ0XaYCd8Vdv4hcasboF18qokGFVdNCLXXe+qbrDctwtaC8D
iBFFhJ2d0JP4Qyk2N1yDudzn3BXZYuXH8fyocc8b0IF8/R1249CEajas4bHYHM12
QOg7QE/vrlqMOPHeugkrYKbrpSHnwYbl+yvNyjgPcX2uKRd/3G1F0vvvRnr9Cdc8
4GacH84J5bJPExjtwEaAn7UxtovFIAER8LbXs90deB7eNjDWiBNk66v3PQRgDq+X
6CCzymP0w/tZ/yAwgN6aI3XSx1x/jZNHX+u+m9AoX2hynRV+KLa1XIwzsOoNo7LJ
VGWinuff6RL+2iHFa5L+GBjAtu1D42h4tvgfXmGM7nR6Wythj+w=
=yC0W
-----END PGP SIGNATURE-----
-------------- next part --------------
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/changelog node-tar-6.1.13+~cs7.0.5/debian/changelog
--- node-tar-6.1.13+~cs7.0.5/debian/changelog 2022-12-17 14:53:17.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/changelog 2026-04-30 04:21:06.000000000 +0200
@@ -1,3 +1,39 @@
+node-tar (6.1.13+~cs7.0.5-1+deb12u1) bookworm; urgency=medium
+
+ * Non-maintainer upload by the Debian LTS team.
+ * d/patches/CVE-2024-28863.patch: Add patch to fix CVE-2024-28863.
+ - Generating a large number of sub-folders can consume memory on the system
+ and even crash the Node.js client within a few seconds using a path with
+ too many sub-folders inside.
+ * d/patches/CVE-2026-23745.patch: Add patch to fix CVE-2026-23745.
+ - When preservePaths is false, the linkpath of Link (hardlink) and
+ SymbolicLink entries fail to be sanitized, allowing malicious archives to
+ bypass the extraction root restriction, leading to arbitrary file
+ overwrites via hardlinks and symlink poisoning via absolute symlink
+ targets.
+ * d/patches/CVE-2026-23745-regression-fix.patch: Add patch to fix a
+ regression introduced by the fix for CVE-2026-23745.
+ - The fix for CVE-2026-23745 introduces a regression that prevents
+ unpacking archives with valid linkpaths within the archive.
+ * d/patches/CVE-2026-24842.patch: Add patch to fix CVE-2026-24842.
+ - The security check for hardlink entries allows an attacker to craft a
+ malicious TAR archive that bypasses path traversal protections and
+ creates hardlinks to arbitrary files outside the extraction directory.
+ * d/patches/CVE-2026-26960-1.patch,
+ d/patches/CVE-2026-26960-2.patch: Add patch to fix CVE-2026-26960.
+ - An attacker-controlled archive can create a hardlink inside the
+ extraction directory that points to a file outside the extraction root,
+ enabling arbitrary file read and write as the extracting user.
+ * d/patches/CVE-2026-29786.patch: Add patch to fix CVE-2026-29786.
+ - An attacker-controlled archive can create a hardlink that points outside
+ the extraction directory by using a drive-relative link target.
+ * d/patches/CVE-2026-31802.patch: Add patch to fix CVE-2026-31802.
+ - An attacker-controlled archive can create a hardlink that points outside
+ the extraction directory by using a drive-relative link target.
+ * d/patches/fix-tests.patch: Fix autopkgtest.
+
+ -- Daniel Leidert <dleidert at debian.org> Thu, 30 Apr 2026 04:21:06 +0200
+
node-tar (6.1.13+~cs7.0.5-1) unstable; urgency=medium
* Team upload
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/gbp.conf node-tar-6.1.13+~cs7.0.5/debian/gbp.conf
--- node-tar-6.1.13+~cs7.0.5/debian/gbp.conf 2022-11-10 14:49:38.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/gbp.conf 2026-04-30 04:21:06.000000000 +0200
@@ -4,7 +4,7 @@
upstream-branch=upstream
# The default name for the Debian branch is "master".
# Change it if the name is different (for instance, "debian/unstable").
-debian-branch=master
+debian-branch=debian/bookworm
# git-import-orig uses the following names for the upstream tags.
# Change the value if you are not using git-import-orig
upstream-tag=upstream/%(version)s
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/api-backward-compatibility.patch node-tar-6.1.13+~cs7.0.5/debian/patches/api-backward-compatibility.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/api-backward-compatibility.patch 2021-11-11 18:42:10.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/api-backward-compatibility.patch 2026-04-30 04:21:06.000000000 +0200
@@ -1,7 +1,15 @@
-Description: expose old method names for backward compatibility
-Author: J?r?my Lal <kapouer at melix.org>
+From: =?utf-8?b?SsOpcsOpbXkgTGFs?= <kapouer at melix.org>
+Date: Sun, 1 Mar 2026 01:31:59 +0100
+Subject: expose old method names for backward compatibility
+
Last-Update: 2018-06-08
Forwarded: not-needed
+---
+ index.js | 10 +++++-----
+ 1 file changed, 5 insertions(+), 5 deletions(-)
+
+diff --git a/index.js b/index.js
+index c9ae06e..1c0b826 100644
--- a/index.js
+++ b/index.js
@@ -1,11 +1,11 @@
Bin?rdateien /tmp/_PugFZ0wrL/node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2024-28863.patch und /tmp/2mvRegGifr/node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2024-28863.patch sind verschieden.
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,163 @@
+From: isaacs <i at izs.me>
+Date: Fri, 16 Jan 2026 12:33:17 -0800
+Subject: [PATCH] fix: sanitize absolute linkpaths properly
+
+Fix: https://github.com/isaacs/node-tar/security/advisories/GHSA-8qq5-rm4j-mr97
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/340eb285b6d986e91969a1170d7fe9b0face405e
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-8qq5-rm4j-mr97
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-23745
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-23745
+---
+ lib/unpack.js | 61 ++++++++++++++++++++++++++++++++-------------
+ test/ghsa-8qq5-rm4j-mr97.js | 54 +++++++++++++++++++++++++++++++++++++++
+ 2 files changed, 97 insertions(+), 18 deletions(-)
+ create mode 100644 test/ghsa-8qq5-rm4j-mr97.js
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index 6b4ba4e..0c0d095 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -32,6 +32,7 @@ const SYMLINK = Symbol('symlink')
+ const HARDLINK = Symbol('hardlink')
+ const UNSUPPORTED = Symbol('unsupported')
+ const CHECKPATH = Symbol('checkPath')
++const STRIPABSOLUTEPATH = Symbol('stripAbsolutePath')
+ const MKDIR = Symbol('mkdir')
+ const ONERROR = Symbol('onError')
+ const PENDING = Symbol('pending')
+@@ -244,6 +245,43 @@ class Unpack extends Parser {
+ }
+ }
+
++ // return false if we need to skip this file
++ // return true if the field was successfully sanitized
++ [STRIPABSOLUTEPATH](entry, field) {
++ const path = entry[field]
++ if (!path || this.preservePaths) return true
++
++ const parts = path.split('/')
++ if (
++ parts.includes('..') ||
++ /* c8 ignore next */
++ (isWindows && /^[a-z]:\.\.$/i.test(parts[0]))
++ ) {
++ this.warn('TAR_ENTRY_ERROR', `${field} contains '..'`, {
++ entry,
++ [field]: path,
++ })
++ // not ok!
++ return false
++ }
++
++ // strip off the root
++ const [root, stripped] = stripAbsolutePath(path)
++ if (root) {
++ // ok, but triggers warning about stripping root
++ entry[field] = String(stripped)
++ this.warn(
++ 'TAR_ENTRY_INFO',
++ `stripping ${root} from absolute ${field}`,
++ {
++ entry,
++ [field]: path,
++ },
++ )
++ }
++ return true
++ }
++
+ [CHECKPATH] (entry) {
+ const p = normPath(entry.path)
+ const parts = p.split('/')
+@@ -274,24 +312,11 @@ class Unpack extends Parser {
+ return false
+ }
+
+- if (!this.preservePaths) {
+- if (parts.includes('..') || isWindows && /^[a-z]:\.\.$/i.test(parts[0])) {
+- this.warn('TAR_ENTRY_ERROR', `path contains '..'`, {
+- entry,
+- path: p,
+- })
+- return false
+- }
+-
+- // strip off the root
+- const [root, stripped] = stripAbsolutePath(p)
+- if (root) {
+- entry.path = stripped
+- this.warn('TAR_ENTRY_INFO', `stripping ${root} from absolute path`, {
+- entry,
+- path: p,
+- })
+- }
++ if (
++ !this[STRIPABSOLUTEPATH](entry, 'path') ||
++ !this[STRIPABSOLUTEPATH](entry, 'linkpath')
++ ) {
++ return false
+ }
+
+ if (path.isAbsolute(entry.path)) {
+diff --git a/test/ghsa-8qq5-rm4j-mr97.js b/test/ghsa-8qq5-rm4j-mr97.js
+new file mode 100644
+index 0000000..851aef5
+--- /dev/null
++++ b/test/ghsa-8qq5-rm4j-mr97.js
+@@ -0,0 +1,54 @@
++'use strict'
++
++const { readFileSync, readlinkSync, writeFileSync } = require('fs')
++const { resolve } = require('path')
++const t = require('tap')
++const mkdirp = require('mkdirp')
++const rimraf = require('rimraf')
++const { Header, x } = require('tar')
++
++const targetSym = '/some/absolute/path'
++const fixtures = resolve(__dirname, 'fixtures')
++const sanitizerdir = resolve(fixtures, 'ghsa-8qq5-rm4j-mr97')
++const dir = resolve(sanitizerdir, 'linkpath-sanitization')
++const secretFile = resolve(dir, 'secret.txt')
++const tarFile = resolve(dir, 'exploit.tar')
++const out = resolve(dir, 'out_repro')
++
++const getExploitTar = () => {
++ const exploitTar = Buffer.alloc(512 + 512 + 1024)
++
++ new Header({
++ path: 'exploit_hard',
++ type: 'Link',
++ size: 0,
++ linkpath: secretFile,
++ }).encode(exploitTar, 0)
++
++ new Header({
++ path: 'exploit_sym',
++ type: 'SymbolicLink',
++ size: 0,
++ linkpath: targetSym,
++ }).encode(exploitTar, 512)
++
++ return exploitTar
++}
++
++t.test('verify that linkpaths get sanitized properly', t => {
++ mkdirp.sync(out)
++ writeFileSync(secretFile, 'ORIGINAL DATA')
++ writeFileSync(tarFile, getExploitTar())
++ t.teardown(_ => rimraf.sync(dir))
++
++ x({
++ cwd: out,
++ file: tarFile,
++ preservePaths: false,
++ }).then(() => {
++ writeFileSync(resolve(out, 'exploit_hard'), 'OVERWRITTEN')
++ t.equal(readFileSync(secretFile, 'utf8'), 'ORIGINAL DATA')
++ t.not(readlinkSync(resolve(out, 'exploit_sym')), targetSym)
++ t.end()
++ })
++})
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745-regression-fix.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745-regression-fix.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745-regression-fix.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-23745-regression-fix.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,309 @@
+From: Nathan Sarang-Walters <nwalters512 at gmail.com>
+Date: Tue, 20 Jan 2026 14:35:00 -0800
+Subject: [PATCH] fix: do not prevent valid linkpaths within archive
+
+PR-URL: https://github.com/isaacs/node-tar/pull/450
+Credit: @nwalters512
+Close: #450
+Reviewed-by: @isaacs
+
+EDIT(@isaacs): fixed for test coverage and to disallow absolute
+linkpaths that contain `..`
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/e9a1ddb821b29ddee75b9470dd511066148c8070
+Bug: https://github.com/isaacs/node-tar/pull/450
+---
+ lib/unpack.js | 49 +++++++++++++++++++++-------
+ test/ghsa-8qq5-rm4j-mr97.js | 79 ++++++++++++++++++++++++++++++++++++++-------
+ test/unpack.js | 78 ++++++++++++++++++++++++++++++++++++++++++++
+ 3 files changed, 183 insertions(+), 23 deletions(-)
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index 0c0d095..baf8f1b 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -248,25 +248,52 @@ class Unpack extends Parser {
+ // return false if we need to skip this file
+ // return true if the field was successfully sanitized
+ [STRIPABSOLUTEPATH](entry, field) {
+- const path = entry[field]
+- if (!path || this.preservePaths) return true
++ const p = entry[field]
++ if (!p || this.preservePaths) return true
++
++ const parts = p.split('/')
+
+- const parts = path.split('/')
+ if (
+ parts.includes('..') ||
+ /* c8 ignore next */
+ (isWindows && /^[a-z]:\.\.$/i.test(parts[0]))
+ ) {
+- this.warn('TAR_ENTRY_ERROR', `${field} contains '..'`, {
+- entry,
+- [field]: path,
+- })
+- // not ok!
+- return false
++ // For linkpath, check if the resolved path escapes cwd rather than
++ // just rejecting any path with '..' - relative symlinks like
++ // '../sibling/file' are valid if they resolve within the cwd.
++ // For paths, they just simply may not ever use .. at all.
++ if (field === 'path') {
++ this.warn('TAR_ENTRY_ERROR', `${field} contains '..'`, {
++ entry,
++ [field]: p,
++ })
++ // not ok!
++ return false
++ } else {
++ // Resolve linkpath relative to the entry's directory.
++ // `path.posix` is safe to use because we're operating on
++ // tar paths, not a filesystem.
++ const entryDir = path.posix.dirname(entry.path)
++ const resolved = path.posix.normalize(
++ path.posix.join(entryDir, p),
++ )
++ // If the resolved path escapes (starts with ..), reject it
++ if (resolved.startsWith('../') || resolved === '..') {
++ this.warn(
++ 'TAR_ENTRY_ERROR',
++ `${field} escapes extraction directory`,
++ {
++ entry,
++ [field]: p,
++ },
++ )
++ return false
++ }
++ }
+ }
+
+ // strip off the root
+- const [root, stripped] = stripAbsolutePath(path)
++ const [root, stripped] = stripAbsolutePath(p)
+ if (root) {
+ // ok, but triggers warning about stripping root
+ entry[field] = String(stripped)
+@@ -275,7 +302,7 @@ class Unpack extends Parser {
+ `stripping ${root} from absolute ${field}`,
+ {
+ entry,
+- [field]: path,
++ [field]: p,
+ },
+ )
+ }
+diff --git a/test/ghsa-8qq5-rm4j-mr97.js b/test/ghsa-8qq5-rm4j-mr97.js
+index 851aef5..95b0e03 100644
+--- a/test/ghsa-8qq5-rm4j-mr97.js
++++ b/test/ghsa-8qq5-rm4j-mr97.js
+@@ -1,6 +1,6 @@
+ 'use strict'
+
+-const { readFileSync, readlinkSync, writeFileSync } = require('fs')
++const { lstatSync, readFileSync, readlinkSync, writeFileSync } = require('fs')
+ const { resolve } = require('path')
+ const t = require('tap')
+ const mkdirp = require('mkdirp')
+@@ -8,47 +8,102 @@ const rimraf = require('rimraf')
+ const { Header, x } = require('tar')
+
+ const targetSym = '/some/absolute/path'
++const absoluteWithDotDot = '/../a/target'
+ const fixtures = resolve(__dirname, 'fixtures')
+ const sanitizerdir = resolve(fixtures, 'ghsa-8qq5-rm4j-mr97')
+ const dir = resolve(sanitizerdir, 'linkpath-sanitization')
+ const secretFile = resolve(dir, 'secret.txt')
+ const tarFile = resolve(dir, 'exploit.tar')
+-const out = resolve(dir, 'out_repro')
++const out = resolve(dir, 'out')
+
+ const getExploitTar = () => {
+- const exploitTar = Buffer.alloc(512 + 512 + 1024)
++ const chunks = []
+
++ const hardHeader = Buffer.alloc(512)
+ new Header({
+ path: 'exploit_hard',
+ type: 'Link',
+ size: 0,
+ linkpath: secretFile,
+- }).encode(exploitTar, 0)
++ }).encode(hardHeader, 0)
++ chunks.push(hardHeader)
+
++ const symHeader = Buffer.alloc(512)
+ new Header({
+ path: 'exploit_sym',
+ type: 'SymbolicLink',
+ size: 0,
+ linkpath: targetSym,
+- }).encode(exploitTar, 512)
++ }).encode(symHeader, 0)
++ chunks.push(symHeader)
+
+- return exploitTar
++ const escapeHeader = Buffer.alloc(512)
++ new Header({
++ path: 'secret.txt',
++ type: 'SymbolicLink',
++ linkpath: '../secret.txt',
++ }).encode(escapeHeader, 0)
++ chunks.push(escapeHeader)
++
++ const aDirHeader = Buffer.alloc(512)
++ new Header({
++ path: 'a/',
++ type: 'Directory',
++ mode: 0o755,
++ }).encode(aDirHeader, 0)
++ chunks.push(aDirHeader)
++
++ const absWithDotDotHeader = Buffer.alloc(512)
++ new Header({
++ path: 'a/link',
++ type: 'SymbolicLink',
++ linkpath: absoluteWithDotDot,
++ }).encode(absWithDotDotHeader, 0)
++ chunks.push(absWithDotDotHeader)
++
++ chunks.push(Buffer.alloc(1024))
++ return Buffer.concat(chunks)
+ }
+
+-t.test('verify that linkpaths get sanitized properly', t => {
++t.test('hardlink escape does not clobber target', t => {
+ mkdirp.sync(out)
+ writeFileSync(secretFile, 'ORIGINAL DATA')
+ writeFileSync(tarFile, getExploitTar())
+ t.teardown(_ => rimraf.sync(dir))
+
+- x({
+- cwd: out,
+- file: tarFile,
+- preservePaths: false,
+- }).then(() => {
++ x({ cwd: out, file: tarFile }).then(() => {
+ writeFileSync(resolve(out, 'exploit_hard'), 'OVERWRITTEN')
+ t.equal(readFileSync(secretFile, 'utf8'), 'ORIGINAL DATA')
++ t.end()
++ })
++})
++
++t.test('symlink escapes are sanitized', t => {
++ mkdirp.sync(out)
++ writeFileSync(secretFile, 'ORIGINAL DATA')
++ writeFileSync(tarFile, getExploitTar())
++ t.teardown(_ => rimraf.sync(dir))
++
++ x({ cwd: out, file: tarFile }).then(() => {
+ t.not(readlinkSync(resolve(out, 'exploit_sym')), targetSym)
++ t.throws(() => lstatSync(resolve(out, 'secret.txt')), {
++ code: 'ENOENT',
++ })
++ t.end()
++ })
++})
++
++t.test('absolute symlink with .. has prefix stripped', t => {
++ mkdirp.sync(out)
++ writeFileSync(tarFile, getExploitTar())
++ t.teardown(_ => rimraf.sync(dir))
++
++ x({ cwd: out, file: tarFile }).then(() => {
++ t.equal(
++ readlinkSync(resolve(out, 'a/link')),
++ '../a/target',
++ 'symlink target should be normalized',
++ )
+ t.end()
+ })
+ })
+diff --git a/test/unpack.js b/test/unpack.js
+index 198f034..06e9140 100644
+--- a/test/unpack.js
++++ b/test/unpack.js
+@@ -3294,3 +3294,81 @@ t.test('excessively deep subfolder nesting', async t => {
+ check(t, 64)
+ })
+ })
++
++t.test('valid relative symlink with .. should be extracted', t => {
++ const dir = path.resolve(unpackdir, 'relative-symlink')
++ mkdirp.sync(dir)
++
++ const makeRelSymTar = () => {
++ const chunks = []
++
++ const fooDirHeader = Buffer.alloc(512)
++ new Header({
++ path: 'foo/',
++ type: 'Directory',
++ mode: 0o755,
++ }).encode(fooDirHeader, 0)
++ chunks.push(fooDirHeader)
++
++ const fileContent = 'hello world'
++ const fileHeader = Buffer.alloc(512)
++ new Header({
++ path: 'foo/file.txt',
++ type: 'File',
++ mode: 0o644,
++ size: fileContent.length,
++ }).encode(fileHeader, 0)
++ chunks.push(fileHeader)
++
++ const contentBuf = Buffer.alloc(512)
++ contentBuf.write(fileContent)
++ chunks.push(contentBuf)
++
++ const barDirHeader = Buffer.alloc(512)
++ new Header({
++ path: 'bar/',
++ type: 'Directory',
++ mode: 0o755,
++ }).encode(barDirHeader, 0)
++ chunks.push(barDirHeader)
++
++ const symlinkHeader = Buffer.alloc(512)
++ new Header({
++ path: 'bar/link.txt',
++ type: 'SymbolicLink',
++ linkpath: '../foo/file.txt',
++ }).encode(symlinkHeader, 0)
++ chunks.push(symlinkHeader)
++
++ const escapedSymlinkHeader = Buffer.alloc(512)
++ new Header({
++ path: 'bar/badlink.txt',
++ type: 'SymbolicLink',
++ linkpath: '../../oh/no',
++ }).encode(escapedSymlinkHeader, 0)
++ chunks.push(escapedSymlinkHeader)
++
++ chunks.push(Buffer.alloc(1024))
++ return Buffer.concat(chunks)
++ }
++
++ const tarFile = path.resolve(dir, 'test.tar')
++ fs.writeFileSync(tarFile, makeRelSymTar())
++
++ const warnings = []
++
++ const u = new Unpack({
++ cwd: dir,
++ onwarn: (c, m) => warnings.push([c, m]),
++ })
++ u.on('end', () => {
++ t.ok(fs.lstatSync(dir + '/foo/file.txt').isFile())
++ t.ok(fs.lstatSync(dir + '/bar/link.txt').isSymbolicLink())
++ t.equal(fs.readlinkSync(dir + '/bar/link.txt'), '../foo/file.txt')
++ t.equal(fs.readFileSync(dir + '/bar/link.txt', 'utf8'), 'hello world')
++ t.throws(() => fs.lstatSync(dir + '/bar/badlink.txt'))
++ t.match(warnings, [['TAR_ENTRY_ERROR', 'linkpath escapes extraction directory']])
++ t.end()
++ })
++ u.end(fs.readFileSync(tarFile))
++})
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-24842.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-24842.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-24842.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-24842.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,78 @@
+From: isaacs <i at izs.me>
+Date: Tue, 27 Jan 2026 12:47:48 -0800
+Subject: [PATCH] fix: properly sanitize hard links containing ..
+
+Fix: https://github.com/isaacs/node-tar/security/advisories/GHSA-34x7-hfp2-rc4v
+
+The issue here is that *hard* links are resolved relative to the unpack
+cwd, so if they have `..`, they cannot possibly be valid, same as files
+and for the same reason. The loosening of this restriction for symbolic
+links should have been limited by type, allowing this error.
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/f4a7aa9bc3d717c987fdf1480ff7a64e87ffdb46
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-34x7-hfp2-rc4v
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-24842
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-24842
+---
+ lib/unpack.js | 3 ++-
+ test/ghsa-8qq5-rm4j-mr97.js | 18 ++++++++++++++++++
+ 2 files changed, 20 insertions(+), 1 deletion(-)
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index baf8f1b..d3f8f0e 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -249,6 +249,7 @@ class Unpack extends Parser {
+ // return true if the field was successfully sanitized
+ [STRIPABSOLUTEPATH](entry, field) {
+ const p = entry[field]
++ const { type } = entry
+ if (!p || this.preservePaths) return true
+
+ const parts = p.split('/')
+@@ -262,7 +263,7 @@ class Unpack extends Parser {
+ // just rejecting any path with '..' - relative symlinks like
+ // '../sibling/file' are valid if they resolve within the cwd.
+ // For paths, they just simply may not ever use .. at all.
+- if (field === 'path') {
++ if (field === 'path' || type === 'Link') {
+ this.warn('TAR_ENTRY_ERROR', `${field} contains '..'`, {
+ entry,
+ [field]: p,
+diff --git a/test/ghsa-8qq5-rm4j-mr97.js b/test/ghsa-8qq5-rm4j-mr97.js
+index 95b0e03..9b96a40 100644
+--- a/test/ghsa-8qq5-rm4j-mr97.js
++++ b/test/ghsa-8qq5-rm4j-mr97.js
+@@ -28,6 +28,20 @@ const getExploitTar = () => {
+ }).encode(hardHeader, 0)
+ chunks.push(hardHeader)
+
++ const hardSubHeader = Buffer.alloc(1024)
++ new Header({
++ path: 'sub/',
++ type: 'Directory',
++ size: 0,
++ }).encode(hardSubHeader, 0)
++ new Header({
++ path: 'sub/exploit_sub',
++ type: 'Link',
++ size: 0,
++ linkpath: '../secret.txt',
++ }).encode(hardSubHeader, 512)
++ chunks.push(hardSubHeader)
++
+ const symHeader = Buffer.alloc(512)
+ new Header({
+ path: 'exploit_sym',
+@@ -74,6 +88,10 @@ t.test('hardlink escape does not clobber target', t => {
+ x({ cwd: out, file: tarFile }).then(() => {
+ writeFileSync(resolve(out, 'exploit_hard'), 'OVERWRITTEN')
+ t.equal(readFileSync(secretFile, 'utf8'), 'ORIGINAL DATA')
++
++ writeFileSync(resolve(out, 'sub/exploit_sub'), 'OVERWRITTEN SUB')
++ t.equal(readFileSync(secretFile, 'utf8'), 'ORIGINAL DATA')
++
+ t.end()
+ })
+ })
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-1.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-1.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-1.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-1.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,269 @@
+From: isaacs <i at izs.me>
+Date: Thu, 12 Feb 2026 20:50:19 -0800
+Subject: [PATCH] fix: do not write linkpaths through symlinks
+
+Prevent any `Link` or `SymbolicLink` entry from being created if its
+`linkpath` would target a location that is through a symbolic link from
+the current working directory.
+
+This matches the behavior of `bsdtar` for hard links, and is somewhat
+more restrictive in applying the same logic to symbolic links as well.
+
+Unpacking links with targets that extend through symlink folders is
+allowed if `preservePaths` option is enabled, as this disables all
+protective link checking by design, and is only designed for use with
+trusted input.
+
+Fix: https://github.com/isaacs/node-tar/security/advisories/GHSA-83g3-92jg-28cx
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/d18e4e1f846f4ddddc153b0f536a19c050e7499f
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-83g3-92jg-28cx
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-26960
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-26960
+---
+ lib/mkdir.js | 2 ++
+ lib/process-umask.js | 1 +
+ lib/unpack.js | 77 ++++++++++++++++++++++++++++++++++++++++++++++++---
+ test/process-umask.js | 6 ++++
+ test/unpack.js | 71 +++++++++++++++++++++++++++++++++++++++++++++++
+ 5 files changed, 153 insertions(+), 4 deletions(-)
+ create mode 100644 lib/process-umask.js
+ create mode 100644 test/process-umask.js
+
+diff --git a/lib/mkdir.js b/lib/mkdir.js
+index 8ee8de7..6e51f31 100644
+--- a/lib/mkdir.js
++++ b/lib/mkdir.js
+@@ -227,3 +227,5 @@ module.exports.sync = (dir, opt) => {
+
+ return done(created)
+ }
++
++module.exports.SymlinkError = SymlinkError
+diff --git a/lib/process-umask.js b/lib/process-umask.js
+new file mode 100644
+index 0000000..0cddb41
+--- /dev/null
++++ b/lib/process-umask.js
+@@ -0,0 +1 @@
++module.exports = { umask: () => process.umask() }
+diff --git a/lib/unpack.js b/lib/unpack.js
+index d3f8f0e..aa36bfa 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -18,6 +18,8 @@ const stripAbsolutePath = require('./strip-absolute-path.js')
+ const normPath = require('./normalize-windows-path.js')
+ const stripSlash = require('./strip-trailing-slashes.js')
+ const normalize = require('./normalize-unicode.js')
++const { SymlinkError } = require('./mkdir.js')
++const { umask } = require('./process-umask.js');
+
+ const ONENTRY = Symbol('onEntry')
+ const CHECKFS = Symbol('checkFs')
+@@ -30,6 +32,7 @@ const DIRECTORY = Symbol('directory')
+ const LINK = Symbol('link')
+ const SYMLINK = Symbol('symlink')
+ const HARDLINK = Symbol('hardlink')
++const ENSURE_NO_SYMLINK = Symbol('ensureNoSymlink')
+ const UNSUPPORTED = Symbol('unsupported')
+ const CHECKPATH = Symbol('checkPath')
+ const STRIPABSOLUTEPATH = Symbol('stripAbsolutePath')
+@@ -217,7 +220,7 @@ class Unpack extends Parser {
+ this.cwd = normPath(path.resolve(opt.cwd || process.cwd()))
+ this.strip = +opt.strip || 0
+ // if we're not chmodding, then we don't need the process umask
+- this.processUmask = opt.noChmod ? 0 : process.umask()
++ this.processUmask = opt.noChmod ? 0 : umask()
+ this.umask = typeof opt.umask === 'number' ? opt.umask : this.processUmask
+
+ // default mode for dirs created as parents
+@@ -310,6 +313,7 @@ class Unpack extends Parser {
+ return true
+ }
+
++ // no IO, just string checking for absolute indicators
+ [CHECKPATH] (entry) {
+ const p = normPath(entry.path)
+ const parts = p.split('/')
+@@ -591,12 +595,58 @@ class Unpack extends Parser {
+ }
+
+ [SYMLINK] (entry, done) {
+- this[LINK](entry, entry.linkpath, 'symlink', done)
++ const parts = normPath(
++ path.relative(
++ this.cwd,
++ path.resolve(
++ path.dirname(String(entry.absolute)),
++ String(entry.linkpath),
++ ),
++ ),
++ ).split('/')
++ this[ENSURE_NO_SYMLINK](
++ entry,
++ this.cwd,
++ parts,
++ () =>
++ this[LINK](entry, String(entry.linkpath), 'symlink', done),
++ er => {
++ this[ONERROR](er, entry)
++ done()
++ },
++ )
+ }
+
+ [HARDLINK] (entry, done) {
+ const linkpath = normPath(path.resolve(this.cwd, entry.linkpath))
+- this[LINK](entry, linkpath, 'link', done)
++ const parts = normPath(String(entry.linkpath)).split(
++ '/',
++ )
++ this[ENSURE_NO_SYMLINK](
++ entry,
++ this.cwd,
++ parts,
++ () => this[LINK](entry, linkpath, 'link', done),
++ er => {
++ this[ONERROR](er, entry)
++ done()
++ },
++ )
++ }
++
++ [ENSURE_NO_SYMLINK] (entry, cwd, parts, done, onError) {
++ const p = parts.shift()
++ if (this.preservePaths || p === undefined) return done()
++ const t = path.resolve(cwd, p)
++ fs.lstat(t, (er, st) => {
++ if (er) return done()
++ if (st.isSymbolicLink()) {
++ return onError(
++ new SymlinkError(t, path.resolve(t, parts.join('/'))),
++ )
++ }
++ this[ENSURE_NO_SYMLINK](entry, t, parts, done, onError)
++ })
+ }
+
+ [PEND] () {
+@@ -767,8 +817,27 @@ class Unpack extends Parser {
+ }
+ }
+
++ [ENSURE_NO_SYMLINK] (_entry, cwd, parts, done, onError) {
++ if (this.preservePaths || !parts.length) return done()
++ let t = cwd
++ for (const p of parts) {
++ t = path.resolve(t, p)
++ let st
++ try {
++ st = fs.lstatSync(t)
++ } catch (er) {
++ return done()
++ }
++ if (st.isSymbolicLink()) {
++ return onError(
++ new SymlinkError(t, path.resolve(t, parts.join('/'))),
++ )
++ }
++ }
++ done()
++ }
++
+ [LINK] (entry, linkpath, link, done) {
+- // XXX: get the type ('symlink' or 'junction') for windows
+ fs[link](linkpath, entry.absolute, er => {
+ if (er) {
+ this[ONERROR](er, entry)
+diff --git a/test/process-umask.js b/test/process-umask.js
+new file mode 100644
+index 0000000..b7d0745
+--- /dev/null
++++ b/test/process-umask.js
+@@ -0,0 +1,6 @@
++'use strict';
++
++const t = require('tap')
++const { umask } = require('../lib/process-umask.js');
++
++t.equal(umask(), process.umask())
+diff --git a/test/unpack.js b/test/unpack.js
+index 06e9140..2337377 100644
+--- a/test/unpack.js
++++ b/test/unpack.js
+@@ -3372,3 +3372,74 @@ t.test('valid relative symlink with .. should be extracted', t => {
+ })
+ u.end(fs.readFileSync(tarFile))
+ })
++
++t.test('no linking through a symlink', t => {
++ const types = ['Link', 'SymbolicLink']
++ for (const type of types) {
++ t.test(type, t => {
++ const exploit = makeTar([
++ {
++ type: 'SymbolicLink',
++ path: 'a/b/up',
++ linkpath: '../..',
++ mode: 0o755,
++ },
++ {
++ type: 'SymbolicLink',
++ path: 'a/b/escape',
++ linkpath: 'up/..',
++ mode: 0o755,
++ },
++ {
++ type,
++ path: 'exploit',
++ linkpath: 'a/b/escape/exploited-file',
++ mode: 0o755,
++ },
++ '',
++ '',
++ ])
++
++ const dir = path.resolve(unpackdir, 'symlink-linking', type)
++ const exploitedFile = path.resolve(dir, 'exploited-file')
++ const cwd = path.resolve(dir, 'x')
++
++ const setup = () => {
++ mkdirp.sync(cwd)
++ fs.writeFileSync(exploitedFile, 'original content')
++ }
++
++ const cleanup = () => rimraf.sync(dir)
++
++ const check = t => {
++ fs.writeFileSync(path.resolve(cwd, 'exploit'), 'pwned')
++ t.equal(fs.readFileSync(exploitedFile, 'utf8'), 'original content')
++ }
++
++ t.test('sync', t => {
++ setup()
++ t.teardown(cleanup)
++ t.throws(() => {
++ new UnpackSync({ cwd, strict: true }).end(exploit)
++ })
++ check(t)
++ t.end()
++ })
++
++ t.test('async', async t => {
++ setup()
++ t.teardown(cleanup)
++ await t.rejects(new Promise((res, rej) => {
++ new Unpack({ cwd, strict: true })
++ .on('finish', res)
++ .on('error', rej)
++ .end(exploit)
++ }))
++ check(t)
++ })
++
++ t.end()
++ })
++ }
++ t.end()
++})
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-2.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-2.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-2.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-26960-2.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,65 @@
+From: Guillermo de Angel <scumfrog at users.noreply.github.com>
+Date: Fri, 13 Feb 2026 08:32:20 +0100
+Subject: [PATCH] fix(unpack): improve UnpackSync symlink error "into" path
+ accuracy
+
+UnpackSync[ENSURE_NO_SYMLINK] previously constructed SymlinkError's
+"into" path using the full original linkpath parts array, which could
+produce misleading diagnostics.
+
+Build the "into" path from the original `cwd` value and the `parts`
+list.
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/2cb1120bcefe28d7ecc719b41441ade59c52e384
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-83g3-92jg-28cx
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-26960
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-26960
+---
+ lib/unpack.js | 2 +-
+ test/unpack.js | 12 ++++++++++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index aa36bfa..5e8b970 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -642,7 +642,7 @@ class Unpack extends Parser {
+ if (er) return done()
+ if (st.isSymbolicLink()) {
+ return onError(
+- new SymlinkError(t, path.resolve(t, parts.join('/'))),
++ new SymlinkError(t, path.resolve(cwd, parts.join('/'))),
+ )
+ }
+ this[ENSURE_NO_SYMLINK](entry, t, parts, done, onError)
+diff --git a/test/unpack.js b/test/unpack.js
+index 2337377..5c6d3a2 100644
+--- a/test/unpack.js
++++ b/test/unpack.js
+@@ -3421,6 +3421,12 @@ t.test('no linking through a symlink', t => {
+ t.teardown(cleanup)
+ t.throws(() => {
+ new UnpackSync({ cwd, strict: true }).end(exploit)
++ },
++ {
++ name: 'SylinkError',
++ message: /^Cannot extract through symbolic link/,
++ path: /a.b.escape.exploited-file$/,
++ symlink: /a.b.escape$/,
+ })
+ check(t)
+ t.end()
+@@ -3434,6 +3440,12 @@ t.test('no linking through a symlink', t => {
+ .on('finish', res)
+ .on('error', rej)
+ .end(exploit)
++ },
++ {
++ name: 'SylinkError',
++ message: /^Cannot extract through symbolic link/,
++ path: /a.b.escape.exploited-file$/,
++ symlink: /a.b.escape$/,
+ }))
+ check(t)
+ })
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-29786.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-29786.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-29786.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-29786.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,77 @@
+From: isaacs <i at izs.me>
+Date: Wed, 4 Mar 2026 11:41:10 -0800
+Subject: [PATCH] parse root off paths before sanitizing .. parts
+
+Fix: https://github.com/isaacs/node-tar/security/advisories/GHSA-qffp-2rhf-9h96
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/7bc755dd85e623c0279e08eb3784909e6d7e4b9f
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-qffp-2rhf-9h96
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-29786
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-29786
+---
+ lib/unpack.js | 6 +++---
+ test/ghsa-8qq5-rm4j-mr97.js | 15 ++++++++++++++-
+ 2 files changed, 17 insertions(+), 4 deletions(-)
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index 5e8b970..f135b9c 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -255,7 +255,9 @@ class Unpack extends Parser {
+ const { type } = entry
+ if (!p || this.preservePaths) return true
+
+- const parts = p.split('/')
++ // strip off the root
++ const [root, stripped] = stripAbsolutePath(p)
++ const parts = stripped.replace(/\\/g, '/').split('/')
+
+ if (
+ parts.includes('..') ||
+@@ -296,8 +298,6 @@ class Unpack extends Parser {
+ }
+ }
+
+- // strip off the root
+- const [root, stripped] = stripAbsolutePath(p)
+ if (root) {
+ // ok, but triggers warning about stripping root
+ entry[field] = String(stripped)
+diff --git a/test/ghsa-8qq5-rm4j-mr97.js b/test/ghsa-8qq5-rm4j-mr97.js
+index 9b96a40..0c77f9d 100644
+--- a/test/ghsa-8qq5-rm4j-mr97.js
++++ b/test/ghsa-8qq5-rm4j-mr97.js
+@@ -75,11 +75,19 @@ const getExploitTar = () => {
+ }).encode(absWithDotDotHeader, 0)
+ chunks.push(absWithDotDotHeader)
+
++ const winAbsWithDotDotHeader = Buffer.alloc(512)
++ new Header({
++ path: 'a/winrootdotslink',
++ type: 'SymbolicLink',
++ linkpath: 'c:..\\foo\\bar',
++ }).encode(winAbsWithDotDotHeader, 0)
++ chunks.push(winAbsWithDotDotHeader)
++
+ chunks.push(Buffer.alloc(1024))
+ return Buffer.concat(chunks)
+ }
+
+-t.test('hardlink escape does not clobber target', t => {
++t.test('writefile exploits fail', t => {
+ mkdirp.sync(out)
+ writeFileSync(secretFile, 'ORIGINAL DATA')
+ writeFileSync(tarFile, getExploitTar())
+@@ -122,6 +130,11 @@ t.test('absolute symlink with .. has prefix stripped', t => {
+ '../a/target',
+ 'symlink target should be normalized',
+ )
++ t.equal(
++ readlinkSync(resolve(out, 'a/winrootdotslink')),
++ '..\\foo\\bar',
++ 'symlink target should be normalized',
++ )
+ t.end()
+ })
+ })
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-31802.patch node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-31802.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-31802.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/CVE-2026-31802.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,59 @@
+From: isaacs <i at izs.me>
+Date: Sun, 8 Mar 2026 22:52:09 -0700
+Subject: [PATCH] prevent escaping symlinks with drive-relative paths
+
+Fix: https://github.com/isaacs/node-tar/security/advisories/GHSA-9ppj-qmqm-q256
+
+Reviewed-By: Daniel Leidert <dleidert at debian.org>
+Origin: https://github.com/isaacs/node-tar/commit/f48b5fa3b7985ddab96dc0f2125a4ffc9911b6ad
+Bug: https://github.com/isaacs/node-tar/security/advisories/GHSA-9ppj-qmqm-q256
+Bug-Debian-Security: https://security-tracker.debian.org/tracker/CVE-2026-31802
+Bug-Freexian-Security: https://deb.freexian.com/extended-lts/tracker/CVE-2026-31802
+---
+ lib/unpack.js | 2 +-
+ test/ghsa-8qq5-rm4j-mr97.js | 12 ++++++++++++
+ 2 files changed, 13 insertions(+), 1 deletion(-)
+
+diff --git a/lib/unpack.js b/lib/unpack.js
+index f135b9c..a76e02f 100644
+--- a/lib/unpack.js
++++ b/lib/unpack.js
+@@ -281,7 +281,7 @@ class Unpack extends Parser {
+ // tar paths, not a filesystem.
+ const entryDir = path.posix.dirname(entry.path)
+ const resolved = path.posix.normalize(
+- path.posix.join(entryDir, p),
++ path.posix.join(entryDir, parts.join('/'))
+ )
+ // If the resolved path escapes (starts with ..), reject it
+ if (resolved.startsWith('../') || resolved === '..') {
+diff --git a/test/ghsa-8qq5-rm4j-mr97.js b/test/ghsa-8qq5-rm4j-mr97.js
+index 0c77f9d..4aff87d 100644
+--- a/test/ghsa-8qq5-rm4j-mr97.js
++++ b/test/ghsa-8qq5-rm4j-mr97.js
+@@ -83,6 +83,14 @@ const getExploitTar = () => {
+ }).encode(winAbsWithDotDotHeader, 0)
+ chunks.push(winAbsWithDotDotHeader)
+
++ const winAbsWithDotDotEscapeHeader = Buffer.alloc(512)
++ new Header({
++ path: 'a/winrootdotsescapelink',
++ type: 'SymbolicLink',
++ linkpath: 'c:..\\..\\..\\..\\foo\\bar',
++ }).encode(winAbsWithDotDotEscapeHeader, 0)
++ chunks.push(winAbsWithDotDotEscapeHeader)
++
+ chunks.push(Buffer.alloc(1024))
+ return Buffer.concat(chunks)
+ }
+@@ -135,6 +143,10 @@ t.test('absolute symlink with .. has prefix stripped', t => {
+ '..\\foo\\bar',
+ 'symlink target should be normalized',
+ )
++ t.throws(
++ () => lstatSync(resolve(out, 'a/winrootdotsescapelink')),
++ 'escaping symlink is not created',
++ )
+ t.end()
+ })
+ })
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/fix-tests.patch node-tar-6.1.13+~cs7.0.5/debian/patches/fix-tests.patch
--- node-tar-6.1.13+~cs7.0.5/debian/patches/fix-tests.patch 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/fix-tests.patch 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1,20 @@
+From: isaacs <i at izs.me>
+Date: Fri, 12 May 2023 14:43:44 -0700
+Subject: remove parallelism causing test/pack.js to be flaky
+
+---
+ test/pack.js | 1 +
+ 1 file changed, 1 insertion(+)
+
+diff --git a/test/pack.js b/test/pack.js
+index 8906ae3..bae126f 100644
+--- a/test/pack.js
++++ b/test/pack.js
+@@ -1135,6 +1135,7 @@ t.test('prefix and hard links', t => {
+ cwd: dir + '/in',
+ prefix: 'out/x',
+ noDirRecurse: true,
++ jobs: 1,
+ })
+ const out = []
+ p.on('data', d => out.push(d))
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/patches/series node-tar-6.1.13+~cs7.0.5/debian/patches/series
--- node-tar-6.1.13+~cs7.0.5/debian/patches/series 2022-11-10 14:49:38.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/patches/series 2026-04-30 04:21:06.000000000 +0200
@@ -1 +1,10 @@
api-backward-compatibility.patch
+CVE-2024-28863.patch
+fix-tests.patch
+CVE-2026-23745.patch
+CVE-2026-23745-regression-fix.patch
+CVE-2026-24842.patch
+CVE-2026-26960-1.patch
+CVE-2026-26960-2.patch
+CVE-2026-29786.patch
+CVE-2026-31802.patch
diff -Nru node-tar-6.1.13+~cs7.0.5/debian/source/include-binaries node-tar-6.1.13+~cs7.0.5/debian/source/include-binaries
--- node-tar-6.1.13+~cs7.0.5/debian/source/include-binaries 1970-01-01 01:00:00.000000000 +0100
+++ node-tar-6.1.13+~cs7.0.5/debian/source/include-binaries 2026-04-30 04:21:06.000000000 +0200
@@ -0,0 +1 @@
+debian/patches/CVE-2024-28863.patch
More information about the Pkg-javascript-devel
mailing list