Skip to content

Commit 69c56cb

Browse files
committed
fix: strip absolute paths more comprehensively
1 parent 114efef commit 69c56cb

File tree

6 files changed

+57
-20
lines changed

6 files changed

+57
-20
lines changed

lib/strip-absolute-path.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
// unix absolute paths are also absolute on win32, so we use this for both
2+
const { isAbsolute, parse } = require('path').win32
3+
4+
// returns [root, stripped]
5+
module.exports = path => {
6+
let r = ''
7+
while (isAbsolute(path)) {
8+
// windows will think that //x/y/z has a "root" of //x/y/
9+
const root = path.charAt(0) === '/' ? '/' : parse(path).root
10+
path = path.substr(root.length)
11+
r += root
12+
}
13+
return [r, path]
14+
}

lib/unpack.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ const mkdir = require('./mkdir.js')
1616
const mkdirSync = mkdir.sync
1717
const wc = require('./winchars.js')
1818
const pathReservations = require('./path-reservations.js')
19+
const stripAbsolutePath = require('./strip-absolute-path.js')
1920

2021
const ONENTRY = Symbol('onEntry')
2122
const CHECKFS = Symbol('checkFs')
@@ -223,11 +224,10 @@ class Unpack extends Parser {
223224

224225
// absolutes on posix are also absolutes on win32
225226
// so we only need to test this one to get both
226-
if (path.win32.isAbsolute(p)) {
227-
const parsed = path.win32.parse(p)
228-
entry.path = p.substr(parsed.root.length)
229-
const r = parsed.root
230-
this.warn('TAR_ENTRY_INFO', `stripping ${r} from absolute path`, {
227+
const [root, stripped] = stripAbsolutePath(p)
228+
if (root) {
229+
entry.path = stripped
230+
this.warn('TAR_ENTRY_INFO', `stripping ${root} from absolute path`, {
231231
entry,
232232
path: p,
233233
})

lib/write-entry.js

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ const CLOSE = Symbol('close')
2525
const MODE = Symbol('mode')
2626
const warner = require('./warn-mixin.js')
2727
const winchars = require('./winchars.js')
28+
const stripAbsolutePath = require('./strip-absolute-path.js')
2829

2930
const modeFix = require('./mode-fix.js')
3031

@@ -54,12 +55,12 @@ const WriteEntry = warner(class WriteEntry extends MiniPass {
5455
this.on('warn', opt.onwarn)
5556

5657
let pathWarn = false
57-
if (!this.preservePaths && path.win32.isAbsolute(p)) {
58-
// absolutes on posix are also absolutes on win32
59-
// so we only need to test this one to get both
60-
const parsed = path.win32.parse(p)
61-
this.path = p.substr(parsed.root.length)
62-
pathWarn = parsed.root
58+
if (!this.preservePaths) {
59+
const [root, stripped] = stripAbsolutePath(this.path)
60+
if (root) {
61+
this.path = stripped
62+
pathWarn = root
63+
}
6364
}
6465

6566
this.win32 = !!opt.win32 || process.platform === 'win32'
@@ -353,10 +354,12 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass {
353354
this.on('warn', opt.onwarn)
354355

355356
let pathWarn = false
356-
if (path.isAbsolute(this.path) && !this.preservePaths) {
357-
const parsed = path.parse(this.path)
358-
pathWarn = parsed.root
359-
this.path = this.path.substr(parsed.root.length)
357+
if (!this.preservePaths) {
358+
const [root, stripped] = stripAbsolutePath(this.path)
359+
if (root) {
360+
this.path = stripped
361+
pathWarn = root
362+
}
360363
}
361364

362365
this.remain = readEntry.size

test/strip-absolute-path.js

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
const t = require('tap')
2+
const stripAbsolutePath = require('../lib/strip-absolute-path.js')
3+
4+
const cases = {
5+
'/': ['/', ''],
6+
'////': ['////', ''],
7+
'c:///a/b/c': ['c:///', 'a/b/c'],
8+
'\\\\foo\\bar\\baz': ['\\\\foo\\bar\\', 'baz'],
9+
'//foo//bar//baz': ['//', 'foo//bar//baz'],
10+
'c:\\c:\\c:\\c:\\\\d:\\e/f/g': ['c:\\c:\\c:\\c:\\\\d:\\', 'e/f/g'],
11+
}
12+
13+
for (const [input, [root, stripped]] of Object.entries(cases))
14+
t.strictSame(stripAbsolutePath(input), [root, stripped], input)

test/unpack.js

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -780,14 +780,17 @@ t.test('absolute paths', t => {
780780
})
781781

782782
const absolute = path.resolve(dir, 'd/i/r/absolute')
783+
const root = path.parse(absolute).root
784+
const extraAbsolute = root + root + root + absolute
785+
t.ok(path.isAbsolute(extraAbsolute))
783786
t.ok(path.isAbsolute(absolute))
784787
const parsed = path.parse(absolute)
785788
const relative = absolute.substr(parsed.root.length)
786789
t.notOk(path.isAbsolute(relative))
787790

788791
const data = makeTar([
789792
{
790-
path: absolute,
793+
path: extraAbsolute,
791794
type: 'File',
792795
size: 1,
793796
atime: new Date('1979-07-01T19:10:00.000Z'),
@@ -802,7 +805,7 @@ t.test('absolute paths', t => {
802805
t.test('warn and correct', t => {
803806
const check = t => {
804807
t.match(warnings, [[
805-
'stripping / from absolute path',
808+
`stripping ${root}${root}${root}${root} from absolute path`,
806809
{ path: absolute, code: 'TAR_ENTRY_INFO' },
807810
]])
808811
t.ok(fs.lstatSync(path.resolve(dir, relative)).isFile(), 'is file')

test/write-entry.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -377,7 +377,10 @@ t.test('nonexistent file', t => {
377377
})
378378

379379
t.test('absolute path', t => {
380-
const f = path.resolve(files, '512-bytes.txt')
380+
const absolute = path.resolve(files, '512-bytes.txt')
381+
const { root } = path.parse(absolute)
382+
const f = root + root + root + absolute
383+
const warn = root + root + root + root
381384
t.test('preservePaths=false strict=false', t => {
382385
const warnings = []
383386
const ws = new WriteEntry(f, {
@@ -390,13 +393,13 @@ t.test('absolute path', t => {
390393
out = Buffer.concat(out)
391394
t.equal(out.length, 1024)
392395
t.match(warnings, [[
393-
'TAR_ENTRY_INFO', /stripping .* from absolute path/, { path: f }
396+
'TAR_ENTRY_INFO', `stripping ${warn} from absolute path`, { path: f },
394397
]])
395398

396399
t.match(ws.header, {
397400
cksumValid: true,
398401
needPax: false,
399-
path: f.replace(/^(\/|[a-z]:\\\\)/, ''),
402+
path: f.replace(/^(\/|[a-z]:\\\\){4}/, ''),
400403
mode: 0o644,
401404
size: 512,
402405
linkpath: null,

0 commit comments

Comments
 (0)