Skip to content

Commit

Permalink
fix: strip absolute paths more comprehensively
Browse files Browse the repository at this point in the history
  • Loading branch information
isaacs committed Jul 23, 2021
1 parent 1b94260 commit 1f036ca
Show file tree
Hide file tree
Showing 6 changed files with 57 additions and 20 deletions.
14 changes: 14 additions & 0 deletions 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]
}
10 changes: 5 additions & 5 deletions lib/unpack.js
Expand Up @@ -14,6 +14,7 @@ const path = require('path')
const mkdir = require('./mkdir.js')
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')
Expand Down Expand Up @@ -224,11 +225,10 @@ class Unpack extends Parser {

// 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,
})
Expand Down
23 changes: 13 additions & 10 deletions lib/write-entry.js
Expand Up @@ -23,6 +23,7 @@ const CLOSE = Symbol('close')
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')

Expand Down Expand Up @@ -52,12 +53,12 @@ const WriteEntry = warner(class WriteEntry extends MiniPass {
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'
Expand Down Expand Up @@ -351,10 +352,12 @@ const WriteEntryTar = warner(class WriteEntryTar extends MiniPass {
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
Expand Down
14 changes: 14 additions & 0 deletions 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)
7 changes: 5 additions & 2 deletions test/unpack.js
Expand Up @@ -776,14 +776,17 @@ t.test('absolute paths', t => {
})

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)
t.notOk(path.isAbsolute(relative))

const data = makeTar([
{
path: absolute,
path: extraAbsolute,
type: 'File',
size: 1,
atime: new Date('1979-07-01T19:10:00.000Z'),
Expand All @@ -798,7 +801,7 @@ t.test('absolute paths', t => {
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')
Expand Down
9 changes: 6 additions & 3 deletions test/write-entry.js
Expand Up @@ -385,7 +385,10 @@ t.test('nonexistent file', t => {
})

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, {
Expand All @@ -398,13 +401,13 @@ t.test('absolute path', t => {
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,
Expand Down

0 comments on commit 1f036ca

Please sign in to comment.