+38
-19
@@ -6,2 +6,3 @@ 'use strict' | ||
| const path = require('path') | ||
| const chownr = require('chownr') | ||
@@ -27,2 +28,8 @@ class SymlinkError extends Error { | ||
| const uid = opt.uid | ||
| const gid = opt.gid | ||
| const doChown = typeof uid === 'number' && | ||
| typeof gid === 'number' && | ||
| ( uid !== opt.processUid || gid !== opt.processGid ) | ||
| const preserve = opt.preserve | ||
@@ -33,3 +40,3 @@ const unlink = opt.unlink | ||
| const done = er => { | ||
| const done = (er, created) => { | ||
| if (er) | ||
@@ -39,3 +46,5 @@ cb(er) | ||
| cache.set(dir, true) | ||
| if (needChmod) | ||
| if (created && doChown) | ||
| chownr(created, uid, gid, er => done(er)) | ||
| else if (needChmod) | ||
| fs.chmod(dir, mode, cb) | ||
@@ -55,16 +64,16 @@ else | ||
| const parts = sub.split(/\/|\\/) | ||
| mkdir_(cwd, parts, mode, cache, unlink, done) | ||
| mkdir_(cwd, parts, mode, cache, unlink, null, done) | ||
| } | ||
| const mkdir_ = (base, parts, mode, cache, unlink, cb) => { | ||
| const mkdir_ = (base, parts, mode, cache, unlink, created, cb) => { | ||
| if (!parts.length) | ||
| return cb() | ||
| return cb(null, created) | ||
| const p = parts.shift() | ||
| const part = base + '/' + p | ||
| if (cache.get(part)) | ||
| return mkdir_(part, parts, mode, cache, unlink, cb) | ||
| fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cb)) | ||
| return mkdir_(part, parts, mode, cache, unlink, created, cb) | ||
| fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, created, cb)) | ||
| } | ||
| const onmkdir = (part, parts, mode, cache, unlink, cb) => er => { | ||
| const onmkdir = (part, parts, mode, cache, unlink, created, cb) => er => { | ||
| if (er) { | ||
@@ -75,3 +84,3 @@ fs.lstat(part, (statEr, st) => { | ||
| else if (st.isDirectory()) | ||
| mkdir_(part, parts, mode, cache, unlink, cb) | ||
| mkdir_(part, parts, mode, cache, unlink, created, cb) | ||
| else if (unlink) | ||
@@ -81,3 +90,3 @@ fs.unlink(part, er => { | ||
| return cb(er) | ||
| fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, cb)) | ||
| fs.mkdir(part, mode, onmkdir(part, parts, mode, cache, unlink, created, cb)) | ||
| }) | ||
@@ -89,4 +98,6 @@ else if (st.isSymbolicLink()) | ||
| }) | ||
| } else | ||
| mkdir_(part, parts, mode, cache, unlink, cb) | ||
| } else { | ||
| created = created || part | ||
| mkdir_(part, parts, mode, cache, unlink, created, cb) | ||
| } | ||
| } | ||
@@ -101,2 +112,8 @@ | ||
| const uid = opt.uid | ||
| const gid = opt.gid | ||
| const doChown = typeof uid === 'number' && | ||
| typeof gid === 'number' && | ||
| ( uid !== opt.processUid || gid !== opt.processGid ) | ||
| const preserve = opt.preserve | ||
@@ -107,4 +124,6 @@ const unlink = opt.unlink | ||
| const done = er => { | ||
| const done = (created) => { | ||
| cache.set(dir, true) | ||
| if (created && doChown) | ||
| chownr.sync(created, uid, gid) | ||
| if (needChmod) | ||
@@ -118,10 +137,8 @@ fs.chmodSync(dir, mode) | ||
| if (preserve) { | ||
| mkdirp.sync(dir, mode) | ||
| cache.set(dir, true) | ||
| return | ||
| } | ||
| if (preserve) | ||
| return done(mkdirp.sync(dir, mode)) | ||
| const sub = path.relative(cwd, dir) | ||
| const parts = sub.split(/\/|\\/) | ||
| let created = null | ||
| for (let p = parts.shift(), part = cwd; | ||
@@ -136,2 +153,3 @@ p && (part += '/' + p); | ||
| fs.mkdirSync(part, mode) | ||
| created = created || part | ||
| cache.set(part, true) | ||
@@ -146,2 +164,3 @@ } catch (er) { | ||
| fs.mkdirSync(part, mode) | ||
| created = created || part | ||
| cache.set(part, true) | ||
@@ -154,3 +173,3 @@ continue | ||
| return done() | ||
| return done(created) | ||
| } |
+51
-7
@@ -32,2 +32,4 @@ 'use strict' | ||
| const DOCHOWN = Symbol('doChown') | ||
| const UID = Symbol('uid') | ||
| const GID = Symbol('gid') | ||
@@ -54,3 +56,20 @@ class Unpack extends Parser { | ||
| if (opt.preserveOwner === undefined) | ||
| if (typeof opt.uid === 'number' || typeof opt.gid === 'number') { | ||
| // need both or neither | ||
| if (typeof opt.uid !== 'number' || typeof opt.gid !== 'number') | ||
| throw new TypeError('cannot set owner without number uid and gid') | ||
| if (opt.preserveOwner) | ||
| throw new TypeError( | ||
| 'cannot preserve owner in archive and also set owner explicitly') | ||
| this.uid = opt.uid | ||
| this.gid = opt.gid | ||
| this.setOwner = true | ||
| } else { | ||
| this.uid = null | ||
| this.gid = null | ||
| this.setOwner = false | ||
| } | ||
| // default true for root | ||
| if (opt.preserveOwner === undefined && typeof opt.uid !== 'number') | ||
| this.preserveOwner = process.getuid && process.getuid() === 0 | ||
@@ -60,5 +79,5 @@ else | ||
| this.processUid = this.preserveOwner && process.getuid ? | ||
| this.processUid = (this.preserveOwner || this.setOwner) && process.getuid ? | ||
| process.getuid() : null | ||
| this.processGid = this.preserveOwner && process.getgid ? | ||
| this.processGid = (this.preserveOwner || this.setOwner) && process.getgid ? | ||
| process.getgid() : null | ||
@@ -179,2 +198,6 @@ | ||
| mkdir(dir, { | ||
| uid: this.uid, | ||
| gid: this.gid, | ||
| processUid: this.processUid, | ||
| processGid: this.processGid, | ||
| umask: this.processUmask, | ||
@@ -190,7 +213,24 @@ preserve: this.preservePaths, | ||
| [DOCHOWN] (entry) { | ||
| // in preserve owner mode, chown if the entry doesn't match process | ||
| // in set owner mode, chown if setting doesn't match process | ||
| return this.preserveOwner && | ||
| ( typeof entry.uid === 'number' && entry.uid !== this.processUid || | ||
| typeof entry.gid === 'number' && entry.gid !== this.processGid ) | ||
| || | ||
| ( typeof this.uid === 'number' && this.uid !== this.processUid || | ||
| typeof this.gid === 'number' && this.gid !== this.processGid ) | ||
| } | ||
| [UID] (entry) { | ||
| return typeof this.uid === 'number' ? this.uid | ||
| : typeof entry.uid === 'number' ? entry.uid | ||
| : this.processUid | ||
| } | ||
| [GID] (entry) { | ||
| return typeof this.gid === 'number' ? this.gid | ||
| : typeof entry.gid === 'number' ? entry.gid | ||
| : this.processGid | ||
| } | ||
| [FILE] (entry) { | ||
@@ -216,3 +256,3 @@ const mode = entry.mode & 0o7777 || this.fmode | ||
| queue.push(cb => | ||
| fs.chown(entry.absolute, entry.uid, entry.gid || this.processGid, cb)) | ||
| fs.chown(entry.absolute, this[UID](entry), this[GID](entry), cb)) | ||
| processQueue() | ||
@@ -245,3 +285,3 @@ }) | ||
| queue.push(cb => | ||
| fs.chown(entry.absolute, entry.uid, entry.gid || this.processGid, cb)) | ||
| fs.chown(entry.absolute, this[UID](entry), this[GID](entry), cb)) | ||
@@ -385,3 +425,3 @@ processQueue() | ||
| try { | ||
| fs.fchownSync(fd, entry.uid, entry.gid || this.processGid) | ||
| fs.fchownSync(fd, this[UID](entry), this[GID](entry)) | ||
| } catch (er) {} | ||
@@ -406,3 +446,3 @@ } | ||
| try { | ||
| fs.chownSync(entry.absolute, entry.uid, entry.gid || this.processGid) | ||
| fs.chownSync(entry.absolute, this[UID](entry), this[GID](entry)) | ||
| } catch (er) {} | ||
@@ -416,2 +456,6 @@ } | ||
| return mkdir.sync(dir, { | ||
| uid: this.uid, | ||
| gid: this.gid, | ||
| processUid: this.processUid, | ||
| processGid: this.processGid, | ||
| umask: this.processUmask, | ||
@@ -418,0 +462,0 @@ preserve: this.preservePaths, |
+2
-1
@@ -5,3 +5,3 @@ { | ||
| "description": "tar for node", | ||
| "version": "3.1.15", | ||
| "version": "3.2.0", | ||
| "repository": { | ||
@@ -20,2 +20,3 @@ "type": "git", | ||
| "dependencies": { | ||
| "chownr": "^1.0.1", | ||
| "minipass": "^2.0.2", | ||
@@ -22,0 +23,0 @@ "minizlib": "^1.0.3", |
+20
-0
@@ -298,2 +298,12 @@ # node-tar | ||
| [Alias: `p`] | ||
| - `uid` Set to a number to force ownership of all extracted files and | ||
| folders, and all implicitly created directories, to be owned by the | ||
| specified user id, regardless of the `uid` field in the archive. | ||
| Cannot be used along with `preserveOwner`. Requires also setting a | ||
| `gid` option. | ||
| - `gid` Set to a number to force ownership of all extracted files and | ||
| folders, and all implicitly created directories, to be owned by the | ||
| specified group id, regardless of the `gid` field in the archive. | ||
| Cannot be used along with `preserveOwner`. Requires also setting a | ||
| `uid` option. | ||
@@ -558,2 +568,12 @@ The following options are mostly internal, but can be modified in some | ||
| windows-compatible values while being unpacked. | ||
| - `uid` Set to a number to force ownership of all extracted files and | ||
| folders, and all implicitly created directories, to be owned by the | ||
| specified user id, regardless of the `uid` field in the archive. | ||
| Cannot be used along with `preserveOwner`. Requires also setting a | ||
| `gid` option. | ||
| - `gid` Set to a number to force ownership of all extracted files and | ||
| folders, and all implicitly created directories, to be owned by the | ||
| specified group id, regardless of the `gid` field in the archive. | ||
| Cannot be used along with `preserveOwner`. Requires also setting a | ||
| `uid` option. | ||
@@ -560,0 +580,0 @@ ### class tar.Unpack.Sync |
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
121908
2.9%2758
2.15%875
2.34%5
25%+ Added
+ Added