Skip to content

Commit

Permalink
Fix(which): match only executable files (#874)
Browse files Browse the repository at this point in the history
On Unix, this only matches files with the exec bit set. On Windows, this only
matches files which are readable (since Windows has different rules for
execution).

Fixes #657.
  • Loading branch information
termosa authored and nfischer committed Jul 18, 2018
1 parent 6d66a1a commit 8dae55f
Show file tree
Hide file tree
Showing 3 changed files with 48 additions and 5 deletions.
27 changes: 23 additions & 4 deletions src/which.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,33 @@ common.register('which', _which, {
// set on Windows.
var XP_DEFAULT_PATHEXT = '.com;.exe;.bat;.cmd;.vbs;.vbe;.js;.jse;.wsf;.wsh';

// For earlier versions of NodeJS that doesn't have a list of constants (< v6)
var FILE_EXECUTABLE_MODE = 1;

function isWindowsPlatform() {
return process.platform === 'win32';
}

// Cross-platform method for splitting environment `PATH` variables
function splitPath(p) {
return p ? p.split(path.delimiter) : [];
}

// Tests are running all cases for this func but it stays uncovered by codecov due to unknown reason
/* istanbul ignore next */
function isExecutable(pathName) {
try {
// TODO(node-support): replace with fs.constants.X_OK once remove support for node < v6
fs.accessSync(pathName, FILE_EXECUTABLE_MODE);
} catch (err) {
return false;
}
return true;
}

function checkPath(pathName) {
return fs.existsSync(pathName) && !common.statFollowLinks(pathName).isDirectory();
return fs.existsSync(pathName) && !common.statFollowLinks(pathName).isDirectory()
&& (isWindowsPlatform() || isExecutable(pathName));
}

//@
Expand All @@ -37,9 +57,8 @@ function checkPath(pathName) {
function _which(options, cmd) {
if (!cmd) common.error('must specify command');

var isWindows = process.platform === 'win32';
var pathEnv = process.env.path || process.env.Path || process.env.PATH;
var pathArray = splitPath(pathEnv);
var isWindows = isWindowsPlatform();
var pathArray = splitPath(process.env.PATH);

var queryMatches = [];

Expand Down
1 change: 1 addition & 0 deletions test/resources/which/node
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
text file, not an executable
25 changes: 24 additions & 1 deletion test/which.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fs from 'fs';
import path from 'path';

import test from 'ava';

Expand Down Expand Up @@ -69,5 +70,27 @@ test('Searching with -a flag returns an array with first item equals to the regu
t.falsy(shell.error());
t.truthy(resultForWhich);
t.truthy(resultForWhichA);
t.is(resultForWhich.toString(), resultForWhichA[0].toString());
t.is(resultForWhich.toString(), resultForWhichA[0]);
});

test('None executable files does not appear in the result list', t => {
const commandName = 'node'; // Should be an existing command
const extraPath = path.resolve(__dirname, 'resources', 'which');
const matchingFile = path.resolve(extraPath, commandName);
const pathEnv = process.env.PATH;

// make sure that file is exists (will throw error otherwise)
t.truthy(fs.existsSync(matchingFile));

process.env.PATH = extraPath + path.delimiter + process.env.PATH;
const resultForWhich = shell.which(commandName);
const resultForWhichA = shell.which('-a', commandName);
t.falsy(shell.error());
t.truthy(resultForWhich);
t.truthy(resultForWhichA);
t.truthy(resultForWhichA.length);
t.not(resultForWhich.toString(), matchingFile);
t.is(resultForWhichA.indexOf(matchingFile), -1);

process.env.PATH = pathEnv;
});

0 comments on commit 8dae55f

Please sign in to comment.