Skip to content

Commit 7effe01

Browse files
committed
Fix command injection vulnerability
@ronomon/opened was vulnerable to a command injection vulnerability that would allow a remote attacker to execute commands on the system if the library was used with untrusted input. The root cause of the problem was line 87 in index.js which took potential untrusted input as part of a string executed as a command by `child_process.exec()`. While the arguments were escaped by @ronomon/opened, an attacker could still bypass this sanitization because `child_process.exec()` will also interpret the string as a shell command. This fix moves to `execFile` to spawn the binary with separate arguments that will not also be interpreted as shell commands. Thanks to Fábio Freitas, a security analyst at Checkmarx's CxSCA group, for discovering and disclosing the vulnerability, providing clear steps to reproduce and suggestions for mitigation.
1 parent 33c2dfb commit 7effe01

File tree

2 files changed

+18
-7
lines changed

2 files changed

+18
-7
lines changed

index.js

+3-7
Original file line numberDiff line numberDiff line change
@@ -79,17 +79,13 @@ Unix.files = function(paths, end) {
7979
var files = {};
8080
var queue = new Queue(1); // Concurrency yields no improvement with lsof.
8181
queue.onData = function(paths, end) {
82-
var escapedPaths = paths.map(
83-
function(path) {
84-
return '"' + path.replace(/"/g, '\\"') + '"';
85-
}
86-
);
87-
var command = 'lsof -F n -- ' + escapedPaths.join(' ');
82+
var command = 'lsof';
83+
var args = ['-F', 'n', '--'].concat(paths);
8884
var options = {
8985
encoding: 'utf-8',
9086
maxBuffer: 2 * 1024 * 1024
9187
};
92-
Node.child.exec(command, options,
88+
Node.child.execFile(command, args, options,
9389
function(error, stdout, stderr) {
9490
// lsof returns an error and a status code of 1 if a file is not open:
9591
if (error && error.code === 1 && stderr.length === 0) error = undefined;

test-injection.js

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
const assert = require('assert');
2+
const fs = require('fs');
3+
4+
const Opened = require('./index.js');
5+
6+
const paths = ['$(touch command_line_injection)'];
7+
8+
Opened.files(paths,
9+
function(error, hashTable) {
10+
assert(!!error);
11+
assert(fs.existsSync('command_line_injection') === false);
12+
console.log('PASS');
13+
}
14+
);
15+

0 commit comments

Comments
 (0)