Skip to content
This repository was archived by the owner on Aug 11, 2022. It is now read-only.

Commit 0d4a0b1

Browse files
Thomas Hallockiarna
authored andcommitted
link: fail if package link target is the same as package link source
Credit: @antialias Reviewed-By: @iarna PR-URL: #11442
1 parent fdd6b28 commit 0d4a0b1

2 files changed

Lines changed: 64 additions & 0 deletions

File tree

lib/utils/link.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,24 @@ function linkIfExists (from, to, gently, cb) {
2525
})
2626
}
2727

28+
function resolveIfSymlink (maybeSymlinkPath, cb) {
29+
fs.lstat(maybeSymlinkPath, function (err, stat) {
30+
if (err) return cb.apply(this, arguments)
31+
if (!stat.isSymbolicLink()) return cb(null, maybeSymlinkPath)
32+
fs.readlink(maybeSymlinkPath, cb)
33+
})
34+
}
35+
36+
function ensureFromIsNotSource (from, to, cb) {
37+
resolveIfSymlink(from, function (err, fromDestination) {
38+
if (err) return cb.apply(this, arguments)
39+
if (path.resolve(path.dirname(from), fromDestination) === path.resolve(to)) {
40+
return cb(new Error('Link target resolves to the same directory as link source: ' + to))
41+
}
42+
cb.apply(this, arguments)
43+
})
44+
}
45+
2846
function link (from, to, gently, abs, cb) {
2947
if (typeof cb !== 'function') {
3048
cb = abs
@@ -48,6 +66,7 @@ function link (from, to, gently, abs, cb) {
4866

4967
chain(
5068
[
69+
[ensureFromIsNotSource, from, to],
5170
[fs, 'stat', from],
5271
[rm, to, gently],
5372
[mkdir, path.dirname(to)],

test/tap/link.js

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@ var osenv = require('osenv')
33
var path = require('path')
44
var rimraf = require('rimraf')
55
var test = require('tap').test
6+
var lstatSync = require('fs').lstatSync
67
var writeFileSync = require('fs').writeFileSync
78

89
var common = require('../common-tap.js')
910

1011
var link = path.join(__dirname, 'link')
1112
var linkScoped = path.join(__dirname, 'link-scoped')
1213
var linkInstall = path.join(__dirname, 'link-install')
14+
var linkInside = path.join(linkInstall, 'node_modules', 'inside')
1315
var linkRoot = path.join(__dirname, 'link-root')
1416

1517
var config = 'prefix = ' + linkRoot
@@ -57,6 +59,18 @@ var installJSON = {
5759
license: 'ISC'
5860
}
5961

62+
var insideInstallJSON = {
63+
name: 'inside',
64+
version: '1.0.0',
65+
description: '',
66+
main: 'index.js',
67+
scripts: {
68+
test: 'echo \"Error: no test specified\" && exit 1'
69+
},
70+
author: '',
71+
license: 'ISC'
72+
}
73+
6074
test('setup', function (t) {
6175
setup()
6276
common.npm(['ls', '-g', '--depth=0'], OPTS, function (err, c, out) {
@@ -81,6 +95,20 @@ test('create global link', function (t) {
8195
})
8296
})
8397

98+
test('create global inside link', function (t) {
99+
process.chdir(linkInside)
100+
common.npm(['link'], OPTS, function (err, c, out) {
101+
t.ifError(err, 'link has no error')
102+
common.npm(['ls', '-g'], OPTS, function (err, c, out, stderr) {
103+
t.ifError(err)
104+
t.equal(c, 0)
105+
t.equal(stderr, '', 'got expected stderr')
106+
t.has(out, /inside@1.0.0/, 'creates global inside link ok')
107+
t.end()
108+
})
109+
})
110+
})
111+
84112
test('create scoped global link', function (t) {
85113
process.chdir(linkScoped)
86114
common.npm(['link'], OPTS, function (err, c, out) {
@@ -108,6 +136,17 @@ test('link-install the package', function (t) {
108136
})
109137
})
110138

139+
test('link-inside-install fails', function (t) {
140+
process.chdir(linkInstall)
141+
t.notOk(lstatSync(linkInside).isSymbolicLink(), 'directory for linked package is not a symlink to begin with')
142+
common.npm(['link', 'inside'], OPTS, function (err, code) {
143+
t.ifError(err, 'npm removed the linked package without error')
144+
t.notEqual(code, 0, 'link operation failed')
145+
t.notOk(lstatSync(linkInside).isSymbolicLink(), 'directory for linked package has not turned into a symlink')
146+
t.end()
147+
})
148+
})
149+
111150
test('link-install the scoped package', function (t) {
112151
process.chdir(linkInstall)
113152
common.npm(['link', linkScoped], OPTS, function (err) {
@@ -141,6 +180,7 @@ function cleanup () {
141180
rimraf.sync(link)
142181
rimraf.sync(linkScoped)
143182
rimraf.sync(linkInstall)
183+
rimraf.sync(linkInside)
144184
}
145185

146186
function setup () {
@@ -161,5 +201,10 @@ function setup () {
161201
path.join(linkInstall, 'package.json'),
162202
JSON.stringify(installJSON, null, 2)
163203
)
204+
mkdirp.sync(linkInside)
205+
writeFileSync(
206+
path.join(linkInside, 'package.json'),
207+
JSON.stringify(insideInstallJSON, null, 2)
208+
)
164209
writeFileSync(configPath, config)
165210
}

0 commit comments

Comments
 (0)