Sophie

Sophie

distrib > Mageia > 8 > armv7hl > by-pkgid > 6f5fcdec8aea1fd3ce63eeed80376ca7 > files > 3

nodejs-tar-6.0.5-1.1.mga8.src.rpm

Description: normalize paths on Windows systems
Author: isaacs <i@izs.me>
Origin: upstream, https://github.com/npm/node-tar/commit/53602669
Bug: https://github.com/npm/node-tar/security/advisories/GHSA-9r2w-394v-53qc
Forwarded: not-needed
Reviewed-By: Yadd <yadd@debian.org>
Last-Update: 2021-11-11

--- a/lib/mkdir.js
+++ b/lib/mkdir.js
@@ -8,6 +8,7 @@
 const fs = require('fs')
 const path = require('path')
 const chownr = require('chownr')
+const normPath = require('./normalize-windows-path.js')
 
 class SymlinkError extends Error {
   constructor (symlink, path) {
@@ -33,7 +34,11 @@
   }
 }
 
+const cGet = (cache, key) => cache.get(normPath(key))
+const cSet = (cache, key, val) => cache.set(normPath(key), val)
+
 const mkdir = module.exports = (dir, opt, cb) => {
+  dir = normPath(dir)
   // if there's any overlap between mask and mode,
   // then we'll need an explicit chmod
   const umask = opt.umask
@@ -49,13 +54,13 @@
   const preserve = opt.preserve
   const unlink = opt.unlink
   const cache = opt.cache
-  const cwd = opt.cwd
+  const cwd = normPath(opt.cwd)
 
   const done = (er, created) => {
     if (er)
       cb(er)
     else {
-      cache.set(dir, true)
+      cSet(cache, dir, true)
       if (created && doChown)
         chownr(created, uid, gid, er => done(er))
       else if (needChmod)
@@ -65,7 +70,7 @@
     }
   }
 
-  if (cache && cache.get(dir) === true)
+  if (cache && cGet(cache, dir) === true)
     return done()
 
   if (dir === cwd)
@@ -79,7 +84,7 @@
     return mkdirp(dir, {mode}).then(made => done(null, made), done)
 
   const sub = path.relative(cwd, dir)
-  const parts = sub.split(/\/|\\/)
+  const parts = sub.split('/')
   mkdir_(cwd, parts, mode, cache, unlink, cwd, null, done)
 }
 
@@ -88,7 +93,7 @@
     return cb(null, created)
   const p = parts.shift()
   const part = base + '/' + p
-  if (cache.get(part))
+  if (cGet(cache, part))
     return mkdir_(part, parts, mode, cache, unlink, cwd, created, cb)
   fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cwd, created, cb))
 }
@@ -122,6 +127,7 @@
 }
 
 const mkdirSync = module.exports.sync = (dir, opt) => {
+  dir = normPath(dir)
   // if there's any overlap between mask and mode,
   // then we'll need an explicit chmod
   const umask = opt.umask
@@ -137,17 +143,17 @@
   const preserve = opt.preserve
   const unlink = opt.unlink
   const cache = opt.cache
-  const cwd = opt.cwd
+  const cwd = normPath(opt.cwd)
 
   const done = (created) => {
-    cache.set(dir, true)
+    cSet(cache, dir, true)
     if (created && doChown)
       chownr.sync(created, uid, gid)
     if (needChmod)
       fs.chmodSync(dir, mode)
   }
 
-  if (cache && cache.get(dir) === true)
+  if (cache && cGet(cache, dir) === true)
     return done()
 
   if (dir === cwd) {
@@ -169,19 +175,19 @@
     return done(mkdirp.sync(dir, mode))
 
   const sub = path.relative(cwd, dir)
-  const parts = sub.split(/\/|\\/)
+  const parts = sub.split('/')
   let created = null
   for (let p = parts.shift(), part = cwd;
        p && (part += '/' + p);
        p = parts.shift()) {
 
-    if (cache.get(part))
+    if (cGet(cache, part))
       continue
 
     try {
       fs.mkdirSync(part, mode)
       created = created || part
-      cache.set(part, true)
+      cSet(cache, part, true)
     } catch (er) {
       if (er.path && path.dirname(er.path) === cwd &&
           (er.code === 'ENOTDIR' || er.code === 'ENOENT'))
@@ -189,13 +195,13 @@
 
       const st = fs.lstatSync(part)
       if (st.isDirectory()) {
-        cache.set(part, true)
+        cSet(cache, part, true)
         continue
       } else if (unlink) {
         fs.unlinkSync(part)
         fs.mkdirSync(part, mode)
         created = created || part
-        cache.set(part, true)
+        cSet(cache, part, true)
         continue
       } else if (st.isSymbolicLink())
         return new SymlinkError(part, part + '/' + parts.join('/'))
--- /dev/null
+++ b/lib/normalize-windows-path.js
@@ -0,0 +1,8 @@
+// on windows, either \ or / are valid directory separators.
+// on unix, \ is a valid character in filenames.
+// so, on windows, and only on windows, we replace all \ chars with /,
+// so that we can use / as our one and only directory separator char.
+
+const platform = process.env.TESTING_TAR_FAKE_PLATFORM || process.platform
+module.exports = platform !== 'win32' ? p => p
+  : p => p.replace(/\\/g, '/')
--- a/lib/pack.js
+++ b/lib/pack.js
@@ -54,6 +54,7 @@
 const fs = require('fs')
 const path = require('path')
 const warner = require('./warn-mixin.js')
+const normPath = require('./normalize-windows-path.js')
 
 const Pack = warner(class Pack extends MiniPass {
   constructor (opt) {
@@ -66,7 +67,7 @@
     this.preservePaths = !!opt.preservePaths
     this.strict = !!opt.strict
     this.noPax = !!opt.noPax
-    this.prefix = (opt.prefix || '').replace(/(\\|\/)+$/, '')
+    this.prefix = normPath(opt.prefix || '')
     this.linkCache = opt.linkCache || new Map()
     this.statCache = opt.statCache || new Map()
     this.readdirCache = opt.readdirCache || new Map()
@@ -133,7 +134,7 @@
   }
 
   [ADDTARENTRY] (p) {
-    const absolute = path.resolve(this.cwd, p.path)
+    const absolute = normPath(path.resolve(this.cwd, p.path))
     if (this.prefix)
       p.path = this.prefix + '/' + p.path.replace(/^\.(\/+|$)/, '')
 
@@ -152,7 +153,7 @@
   }
 
   [ADDFSENTRY] (p) {
-    const absolute = path.resolve(this.cwd, p)
+    const absolute = normPath(path.resolve(this.cwd, p))
     if (this.prefix)
       p = this.prefix + '/' + p.replace(/^\.(\/+|$)/, '')
 
--- a/lib/path-reservations.js
+++ b/lib/path-reservations.js
@@ -7,6 +7,7 @@
 // while still allowing maximal safe parallelization.
 
 const assert = require('assert')
+const normPath = require('./normalize-windows-path.js')
 
 module.exports = () => {
   // path => [function or Set]
@@ -20,8 +21,9 @@
   // return a set of parent dirs for a given path
   const { join } = require('path')
   const getDirs = path =>
-    join(path).split(/[\\\/]/).slice(0, -1).reduce((set, path) =>
-      set.length ? set.concat(join(set[set.length-1], path)) : [path], [])
+    normPath(join(path)).split('/').slice(0, -1).reduce((set, path) =>
+      set.length ? set.concat(normPath(join(set[set.length - 1], path)))
+      : [path], [])
 
   // functions currently running
   const running = new Set()
--- /dev/null
+++ b/lib/strip-trailing-slashes.js
@@ -0,0 +1,13 @@
+// warning: extremely hot code path.
+// This has been meticulously optimized for use
+// within npm install on large package trees.
+// Do not edit without careful benchmarking.
+module.exports = str => {
+  let i = str.length - 1
+  let slashesStart = -1
+  while (i > -1 && str.charAt(i) === '/') {
+    slashesStart = i
+    i--
+  }
+  return slashesStart === -1 ? str : str.slice(0, slashesStart)
+}
--- a/lib/unpack.js
+++ b/lib/unpack.js
@@ -17,6 +17,7 @@
 const wc = require('./winchars.js')
 const pathReservations = require('./path-reservations.js')
 const stripAbsolutePath = require('./strip-absolute-path.js')
+const normPath = require('./normalize-windows-path.js')
 
 const ONENTRY = Symbol('onEntry')
 const CHECKFS = Symbol('checkFs')
@@ -94,6 +95,17 @@
   : b === b >>> 0 ? b
   : c
 
+const pruneCache = (cache, abs) => {
+  // clear the cache if it's a case-insensitive match, since we can't
+  // know if the current file system is case-sensitive or not.
+  abs = normPath(abs).toLowerCase()
+  for (const path of cache.keys()) {
+    const plower = path.toLowerCase()
+    if (plower === abs || plower.toLowerCase().indexOf(abs + '/') === 0)
+      cache.delete(path)
+  }
+}
+
 class Unpack extends Parser {
   constructor (opt) {
     if (!opt)
@@ -170,7 +182,7 @@
     // links, and removes symlink directories rather than erroring
     this.unlink = !!opt.unlink
 
-    this.cwd = path.resolve(opt.cwd || process.cwd())
+    this.cwd = normPath(path.resolve(opt.cwd || process.cwd()))
     this.strip = +opt.strip || 0
     this.processUmask = process.umask()
     this.umask = typeof opt.umask === 'number' ? opt.umask : this.processUmask
@@ -200,21 +212,21 @@
 
   [CHECKPATH] (entry) {
     if (this.strip) {
-      const parts = entry.path.split(/\/|\\/)
+      const parts = normPath(entry.path).split('/')
       if (parts.length < this.strip)
         return false
       entry.path = parts.slice(this.strip).join('/')
 
       if (entry.type === 'Link') {
-        const linkparts = entry.linkpath.split(/\/|\\/)
+        const linkparts = normPath(entry.linkpath).split('/')
         if (linkparts.length >= this.strip)
           entry.linkpath = linkparts.slice(this.strip).join('/')
       }
     }
 
     if (!this.preservePaths) {
-      const p = entry.path
-      if (p.match(/(^|\/|\\)\.\.(\\|\/|$)/)) {
+      const p = normPath(entry.path)
+      if (p.split('/').includes('..')) {
         this.warn('TAR_ENTRY_ERROR', `path contains '..'`, {
           entry,
           path: p,
@@ -242,9 +254,9 @@
     }
 
     if (path.isAbsolute(entry.path))
-      entry.absolute = entry.path
+      entry.absolute = normPath(entry.path)
     else
-      entry.absolute = path.resolve(this.cwd, entry.path)
+      entry.absolute = normPath(path.resolve(this.cwd, entry.path))
 
     return true
   }
@@ -289,7 +301,7 @@
   }
 
   [MKDIR] (dir, mode, cb) {
-    mkdir(dir, {
+    mkdir(normPath(dir), {
       uid: this.uid,
       gid: this.gid,
       processUid: this.processUid,
@@ -424,7 +436,8 @@
   }
 
   [HARDLINK] (entry, done) {
-    this[LINK](entry, path.resolve(this.cwd, entry.linkpath), 'link', done)
+    const linkpath = normPath(path.resolve(this.cwd, entry.linkpath))
+    this[LINK](entry, linkpath, 'link', done)
   }
 
   [PEND] () {
@@ -465,14 +478,8 @@
     // 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)
-      }
-    }
+    if (entry.type !== 'Directory')
+      pruneCache(this.dirCache, entry.absolute)
 
     this[MKDIR](path.dirname(entry.absolute), this.dmode, er => {
       if (er) {
@@ -524,7 +531,7 @@
   }
 
   [LINK] (entry, linkpath, link, done) {
-    // XXX: get the type ('file' or 'dir') for windows
+    // XXX: get the type ('symlink' or 'junction') for windows
     fs[link](linkpath, entry.absolute, er => {
       if (er)
         return this[ONERROR](er, entry)
@@ -541,14 +548,8 @@
   }
 
   [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)
-      }
-    }
+    if (entry.type !== 'Directory')
+      pruneCache(this.dirCache, entry.absolute)
 
     const er = this[MKDIR](path.dirname(entry.absolute), this.dmode, neverCalled)
     if (er)
@@ -671,7 +672,7 @@
 
   [MKDIR] (dir, mode) {
     try {
-      return mkdir.sync(dir, {
+      return mkdir.sync(normPath(dir), {
         uid: this.uid,
         gid: this.gid,
         processUid: this.processUid,
--- a/lib/write-entry.js
+++ b/lib/write-entry.js
@@ -5,6 +5,8 @@
 const ReadEntry = require('./read-entry.js')
 const fs = require('fs')
 const path = require('path')
+const normPath = require('./normalize-windows-path.js')
+const stripSlash = require('./strip-trailing-slashes.js')
 
 const types = require('./types.js')
 const maxReadSize = 16 * 1024 * 1024
@@ -35,7 +37,7 @@
     super(opt)
     if (typeof p !== 'string')
       throw new TypeError('path is required')
-    this.path = p
+    this.path = normPath(p)
     // suppress atime, ctime, uid, gid, uname, gname
     this.portable = !!opt.portable
     // until node has builtin pwnam functions, this'll have to do
@@ -69,7 +71,7 @@
       p = p.replace(/\\/g, '/')
     }
 
-    this.absolute = opt.absolute || path.resolve(this.cwd, p)
+    this.absolute = normPath(opt.absolute || path.resolve(this.cwd, p))
 
     if (this.path === '')
       this.path = './'
@@ -175,14 +177,14 @@
   }
 
   [ONREADLINK] (linkpath) {
-    this.linkpath = linkpath.replace(/\\/g, '/')
+    this.linkpath = normPath(linkpath)
     this[HEADER]()
     this.end()
   }
 
   [HARDLINK] (linkpath) {
     this.type = 'Link'
-    this.linkpath = path.relative(this.cwd, linkpath).replace(/\\/g, '/')
+    this.linkpath = normPath(path.relative(this.cwd, linkpath))
     this.stat.size = 0
     this[HEADER]()
     this.end()