Socket
Socket
Sign inDemoInstall

node-pty

Package Overview
Dependencies
Maintainers
2
Versions
164
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

node-pty - npm Package Compare versions

Comparing version 0.11.0-beta3 to 0.11.0-beta30

src/unix/comms.h

14

lib/terminal.js

@@ -22,2 +22,8 @@ "use strict";

function Terminal(opt) {
this._pid = 0;
this._fd = 0;
this._cols = 0;
this._rows = 0;
this._readable = false;
this._writable = false;
this._onData = new eventEmitter2_1.EventEmitter2();

@@ -27,2 +33,6 @@ this._onExit = new eventEmitter2_1.EventEmitter2();

this._internalee = new events_1.EventEmitter();
// setup flow control handling
this.handleFlowControl = !!(opt === null || opt === void 0 ? void 0 : opt.handleFlowControl);
this._flowControlPause = (opt === null || opt === void 0 ? void 0 : opt.flowControlPause) || FLOW_CONTROL_PAUSE;
this._flowControlResume = (opt === null || opt === void 0 ? void 0 : opt.flowControlResume) || FLOW_CONTROL_RESUME;
if (!opt) {

@@ -41,6 +51,2 @@ return;

this._checkType('encoding', opt.encoding ? opt.encoding : undefined, 'string');
// setup flow control handling
this.handleFlowControl = !!(opt.handleFlowControl);
this._flowControlPause = opt.flowControlPause || FLOW_CONTROL_PAUSE;
this._flowControlResume = opt.flowControlResume || FLOW_CONTROL_RESUME;
}

@@ -47,0 +53,0 @@ Object.defineProperty(Terminal.prototype, "onData", {

@@ -23,7 +23,10 @@ "use strict";

var net = require("net");
var path = require("path");
var terminal_1 = require("./terminal");
var utils_1 = require("./utils");
var pty;
var helperPath;
try {
pty = require('../build/Release/pty.node');
helperPath = '../build/Release/spawn-helper';
}

@@ -33,2 +36,3 @@ catch (outerError) {

pty = require('../build/Debug/pty.node');
helperPath = '../build/Debug/spawn-helper';
}

@@ -41,2 +45,5 @@ catch (innerError) {

}
helperPath = path.resolve(__dirname, helperPath);
helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
var DEFAULT_FILE = 'sh';

@@ -48,3 +55,6 @@ var DEFAULT_NAME = 'xterm';

function UnixTerminal(file, args, opt) {
var _a, _b;
var _this = _super.call(this, opt) || this;
_this._boundClose = false;
_this._emittedClose = false;
if (typeof args === 'string') {

@@ -60,4 +70,5 @@ throw new Error('args as a string is not supported on unix.');

_this._rows = opt.rows || terminal_1.DEFAULT_ROWS;
var uid = opt.uid || -1;
var gid = opt.gid || -1;
var uid = (_a = opt.uid) !== null && _a !== void 0 ? _a : -1;
var gid = (_b = opt.gid) !== null && _b !== void 0 ? _b : -1;
var closeFDs = opt.closeFDs || false;
var env = utils_1.assign({}, opt.env);

@@ -100,3 +111,3 @@ if (opt.env === process.env) {

// fork
var term = pty.fork(file, args, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, (encoding === 'utf8'), onexit);
var term = pty.fork(file, args, parsedEnv, cwd, _this._cols, _this._rows, uid, gid, (encoding === 'utf8'), closeFDs, onexit, helperPath);
_this._socket = new PipeSocket(term.fd);

@@ -166,2 +177,13 @@ if (encoding !== null) {

};
Object.defineProperty(UnixTerminal.prototype, "fd", {
/* Accessors */
get: function () { return this._fd; },
enumerable: false,
configurable: true
});
Object.defineProperty(UnixTerminal.prototype, "ptsName", {
get: function () { return this._pty; },
enumerable: false,
configurable: true
});
/**

@@ -195,3 +217,3 @@ * openpty

self._socket = self._master;
self._pid = null;
self._pid = -1;
self._fd = term.master;

@@ -198,0 +220,0 @@ self._pty = term.pty;

@@ -11,2 +11,5 @@ "use strict";

var path = require("path");
var tty = require("tty");
var fs = require("fs");
var os_1 = require("os");
var testUtils_test_1 = require("./testUtils.test");

@@ -29,4 +32,5 @@ var FIXTURES_PATH = path.normalize(path.join(__dirname, '..', 'fixtures', 'utf8-character.txt'));

if (regExp) {
assert.ok(regExp.test(term._pty), '"' + term._pty + '" should match ' + regExp.toString());
assert.ok(regExp.test(term.ptsName), '"' + term.ptsName + '" should match ' + regExp.toString());
}
assert.ok(tty.isatty(term.fd));
});

@@ -38,4 +42,4 @@ });

term.on('data', function (data) {
assert.equal(typeof data, 'string');
assert.equal(data, '\u00E6');
assert.strictEqual(typeof data, 'string');
assert.strictEqual(data, '\u00E6');
done();

@@ -49,6 +53,6 @@ });

term.on('data', function (data) {
assert.equal(typeof data, 'object');
assert.strictEqual(typeof data, 'object');
assert.ok(data instanceof Buffer);
assert.equal(0xC3, data[0]);
assert.equal(0xA6, data[1]);
assert.strictEqual(0xC3, data[0]);
assert.strictEqual(0xA6, data[1]);
done();

@@ -59,12 +63,12 @@ });

var text = 'test æ!';
var term = new unixTerminal_1.UnixTerminal(null, ['-c', 'echo "' + text + '"'], {
var term = new unixTerminal_1.UnixTerminal(undefined, ['-c', 'echo "' + text + '"'], {
encoding: 'base64'
});
var buffer = '';
term.on('data', function (data) {
assert.equal(typeof data, 'string');
term.onData(function (data) {
assert.strictEqual(typeof data, 'string');
buffer += data;
});
term.on('exit', function () {
assert.equal(Buffer.alloc(8, buffer, 'base64').toString().replace('\r', '').replace('\n', ''), text);
term.onExit(function () {
assert.strictEqual(Buffer.alloc(8, buffer, 'base64').toString().replace('\r', '').replace('\n', ''), text);
done();

@@ -103,2 +107,13 @@ });

});
describe('close', function () {
var term = new unixTerminal_1.UnixTerminal('node');
it('should exit when terminal is destroyed programmatically', function (done) {
term.on('exit', function (code, signal) {
assert.strictEqual(code, 0);
assert.strictEqual(signal, os_1.constants.signals.SIGHUP);
done();
});
term.destroy();
});
});
describe('signals in parent and child', function () {

@@ -126,4 +141,4 @@ it('SIGINT - custom in parent and child', function (done) {

// handlers in parent and child should have been triggered
assert.equal(buffer.indexOf('SIGINT in child') !== -1, true);
assert.equal(buffer.indexOf('SIGINT in parent') !== -1, true);
assert.strictEqual(buffer.indexOf('SIGINT in child') !== -1, true);
assert.strictEqual(buffer.indexOf('SIGINT in parent') !== -1, true);
done();

@@ -154,4 +169,4 @@ });

// handlers in parent and child should have been triggered
assert.equal(buffer.indexOf('should not be printed') !== -1, false);
assert.equal(buffer.indexOf('SIGINT in parent') !== -1, true);
assert.strictEqual(buffer.indexOf('should not be printed') !== -1, false);
assert.strictEqual(buffer.indexOf('SIGINT in parent') !== -1, true);
done();

@@ -174,3 +189,3 @@ });

// no timeout in buffer
assert.equal(buffer, '');
assert.strictEqual(buffer, '');
done();

@@ -202,4 +217,4 @@ });

// should have called both handlers and only once
assert.equal(pHandlerCalled, 1);
assert.equal(buffer, 'SIGUSR1 in child\r\n');
assert.strictEqual(pHandlerCalled, 1);
assert.strictEqual(buffer, 'SIGUSR1 in child\r\n');
done();

@@ -209,4 +224,98 @@ });

});
describe('spawn', function () {
it('should handle exec() errors', function (done) {
try {
new unixTerminal_1.UnixTerminal('/bin/bogus.exe', []);
done(new Error('should have failed'));
}
catch (_a) {
done();
}
});
it('should handle chdir() errors', function (done) {
try {
new unixTerminal_1.UnixTerminal('/bin/echo', [], { cwd: '/nowhere' });
done(new Error('should have failed'));
}
catch (e) {
assert.strictEqual(e.toString(), 'Error: chdir() failed: No such file or directory');
done();
}
});
it('should handle setuid() errors', function (done) {
try {
new unixTerminal_1.UnixTerminal('/bin/echo', [], { uid: 999999 });
done(new Error('should have failed'));
}
catch (e) {
assert.strictEqual(e.toString(), 'Error: setuid() failed: Operation not permitted');
done();
}
});
if (process.platform === 'linux') {
it('should not close on exec when closeFDs is not defined', function (done) {
var data = "\n var pty = require('./lib/index');\n var ptyProcess = pty.spawn('node', ['-e', 'setTimeout(() => console.log(\"hello from terminal\"), 300);']);\n ptyProcess.on('data', function (data) {\n console.log(data);\n });\n setTimeout(() => null, 500);\n console.log('ready', ptyProcess.pid);\n ";
var buffer = [];
var readFd = fs.openSync(FIXTURES_PATH, 'r');
var p = cp.spawn('node', ['-e', data], {
stdio: ['ignore', 'pipe', 'pipe', readFd]
});
var sub = '';
p.stdout.on('data', function (data) {
if (!data.toString().indexOf('ready')) {
sub = data.toString().split(' ')[1].slice(0, -1);
try {
fs.statSync("/proc/" + sub + "/fd/" + readFd);
}
catch (_) {
done('not reachable');
}
setTimeout(function () {
process.kill(parseInt(sub), 'SIGINT'); // SIGINT to child
p.kill('SIGINT'); // SIGINT to parent
}, 200);
}
else {
buffer.push(data.toString().replace(/^\s+|\s+$/g, ''));
}
});
p.on('close', function () {
done();
});
});
it('should close on exec when closeFDs is true', function (done) {
var data = "\n var pty = require('./lib/index');\n var ptyProcess = pty.spawn('node', ['-e', 'setTimeout(() => console.log(\"hello from terminal\"), 300);'], {\n closeFDs: true\n });\n ptyProcess.on('data', function (data) {\n console.log(data);\n });\n setTimeout(() => null, 500);\n console.log('ready', ptyProcess.pid);\n ";
var buffer = [];
var readFd = fs.openSync(FIXTURES_PATH, 'r');
var p = cp.spawn('node', ['-e', data], {
stdio: ['ignore', 'pipe', 'pipe', readFd]
});
var sub = '';
p.stdout.on('data', function (data) {
if (!data.toString().indexOf('ready')) {
sub = data.toString().split(' ')[1].slice(0, -1);
try {
fs.statSync("/proc/" + sub + "/fd/" + readFd);
done('not reachable');
}
catch (error) {
assert.notStrictEqual(error.message.indexOf('ENOENT'), -1);
}
setTimeout(function () {
process.kill(parseInt(sub), 'SIGINT'); // SIGINT to child
p.kill('SIGINT'); // SIGINT to parent
}, 200);
}
else {
buffer.push(data.toString().replace(/^\s+|\s+$/g, ''));
}
});
p.on('close', function () {
done();
});
});
}
});
});
}
//# sourceMappingURL=unixTerminal.test.js.map

@@ -72,3 +72,4 @@ "use strict";

var workerData = { conoutPipeName: _conoutPipeName };
this._worker = new worker_threads_1.Worker(path_1.join(__dirname, 'worker/conoutSocketWorker.js'), { workerData: workerData });
var scriptPath = __dirname.replace('node_modules.asar', 'node_modules.asar.unpacked');
this._worker = new worker_threads_1.Worker(path_1.join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData: workerData });
this._worker.on('message', function (message) {

@@ -75,0 +76,0 @@ switch (message) {

@@ -32,2 +32,5 @@ "use strict";

this._useConpty = _useConpty;
this._pid = 0;
this._innerPid = 0;
this._innerPidHandle = 0;
if (this._useConpty === undefined || this._useConpty === true) {

@@ -34,0 +37,0 @@ this._useConpty = this._getWindowsBuildNumber() >= 18309;

@@ -131,3 +131,3 @@ "use strict";

}
this._defer(function () {
this._deferNoArgs(function () {
_this._agent.resize(cols, rows);

@@ -140,3 +140,3 @@ _this._cols = cols;

var _this = this;
this._defer(function () {
this._deferNoArgs(function () {
_this.kill();

@@ -147,3 +147,3 @@ });

var _this = this;
this._defer(function () {
this._deferNoArgs(function () {
if (signal) {

@@ -156,2 +156,14 @@ throw new Error('Signals not supported on windows.');

};
WindowsTerminal.prototype._deferNoArgs = function (deferredFn) {
var _this = this;
// If the terminal is ready, execute.
if (this._isReady) {
deferredFn.call(this);
return;
}
// Queue until terminal is ready.
this._deferreds.push({
run: function () { return deferredFn.call(_this); }
});
};
WindowsTerminal.prototype._defer = function (deferredFn, arg) {

@@ -158,0 +170,0 @@ var _this = this;

@@ -81,3 +81,2 @@ "use strict";

assert.fail("Bad process state, expected: " + size + ", actual: " + list.length);
resolve();
}

@@ -105,6 +104,6 @@ });

pollForProcessTreeSize(term.pid, 4, 500, 5000).then(function (list) {
assert.equal(list[0].name, 'cmd.exe');
assert.equal(list[1].name, 'powershell.exe');
assert.equal(list[2].name, 'notepad.exe');
assert.equal(list[3].name, 'node.exe');
assert.strictEqual(list[0].name.toLowerCase(), 'cmd.exe');
assert.strictEqual(list[1].name.toLowerCase(), 'powershell.exe');
assert.strictEqual(list[2].name.toLowerCase(), 'notepad.exe');
assert.strictEqual(list[3].name.toLowerCase(), 'node.exe');
term.kill();

@@ -111,0 +110,0 @@ var desiredState = {};

@@ -17,4 +17,7 @@ "use strict";

server.listen(conout_1.getWorkerPipeName(conoutPipeName));
if (!worker_threads_1.parentPort) {
throw new Error('worker_threads parentPort is null');
}
worker_threads_1.parentPort.postMessage(1 /* READY */);
});
//# sourceMappingURL=conoutSocketWorker.js.map

@@ -7,3 +7,3 @@ {

},
"version": "0.11.0-beta3",
"version": "0.11.0-beta30",
"license": "MIT",

@@ -40,3 +40,2 @@ "main": "./lib/index.js",

"lint": "eslint -c .eslintrc.js --ext .ts src/",
"install": "node scripts/install.js",
"postinstall": "node scripts/post-install.js",

@@ -49,3 +48,3 @@ "test": "cross-env NODE_ENV=test mocha -R spec --exit lib/*.test.js",

"dependencies": {
"nan": "^2.14.0"
"nan": "^2.17.0"
},

@@ -52,0 +51,0 @@ "devDependencies": {

# node-pty
[![Build Status](https://dev.azure.com/vscode/node-pty/_apis/build/status/Microsoft.node-pty)](https://dev.azure.com/vscode/node-pty/_build/latest?definitionId=11)
[![Build Status](https://dev.azure.com/vscode/node-pty/_apis/build/status/Microsoft.node-pty?branchName=main)](https://dev.azure.com/vscode/node-pty/_build/latest?definitionId=11&branchName=main)

@@ -34,3 +34,3 @@ `forkpty(3)` bindings for node.js. This allows you to fork processes with pseudoterminal file descriptors. It returns a terminal object which allows reads and writes.

ptyProcess.on('data', function(data) {
ptyProcess.onData((data) => {
process.stdout.write(data);

@@ -67,2 +67,6 @@ });

- [ENiGMA½ BBS Software](https://github.com/NuSkooler/enigma-bbs): A modern BBS software with a nostalgic flair!
- [Tinkerun](https://github.com/tinkerun/tinkerun): A new way of running Tinker.
- [Tess](https://tessapp.dev): Hackable, simple and rapid terminal for the new era of technology 👍
- [NxShell](https://nxshell.github.io/): An easy to use new terminal for Windows/Linux/MacOS platform.
- [OpenSumi](https://github.com/opensumi/core): A framework helps you quickly build Cloud or Desktop IDE products.

@@ -82,3 +86,3 @@ Do you use node-pty in your application as well? Please open a [Pull Request](https://github.com/Tyriar/node-pty/pulls) to include it here. We would love to have it in our list.

Node.JS 12+ or Electron 8+ is required to use `node-pty`.
Node.JS 16 or Electron 19 is required to use `node-pty`. What version of node is supported is currently mostly bound to [whatever version Visual Studio Code is using](https://github.com/microsoft/node-pty/issues/557#issuecomment-1332193541).

@@ -85,0 +89,0 @@ ### Linux (apt)

@@ -12,2 +12,3 @@ var fs = require('fs');

path.join(RELEASE_DIR, 'pty.pdb'),
path.join(RELEASE_DIR, 'spawn-helper'),
path.join(RELEASE_DIR, 'winpty-agent.exe'),

@@ -14,0 +15,0 @@ path.join(RELEASE_DIR, 'winpty-agent.pdb'),

@@ -18,3 +18,3 @@ /**

const consoleProcessList = getConsoleProcessList(shellPid);
process.send({ consoleProcessList });
process.send!({ consoleProcessList });
process.exit(0);

@@ -103,3 +103,3 @@ /**

env?: IProcessEnv;
encoding?: string;
encoding?: string | null;
handleFlowControl?: boolean;

@@ -113,2 +113,3 @@ flowControlPause?: string;

gid?: number;
closeFDs?: boolean;
}

@@ -124,3 +125,3 @@

rows?: number;
encoding?: string;
encoding?: string | null;
}

@@ -21,3 +21,3 @@ /**

interface IUnixNative {
fork(file: string, args: string[], parsedEnv: string[], cwd: string, cols: number, rows: number, uid: number, gid: number, useUtf8: boolean, onExitCallback: (code: number, signal: number) => void): IUnixProcess;
fork(file: string, args: string[], parsedEnv: string[], cwd: string, cols: number, rows: number, uid: number, gid: number, closeFDs: boolean, useUtf8: boolean, onExitCallback: (code: number, signal: number) => void, helperPath: string): IUnixProcess;
open(cols: number, rows: number): IUnixOpenProcess;

@@ -24,0 +24,0 @@ process(fd: number, pty: string): string;

@@ -9,3 +9,3 @@ /**

import { EventEmitter } from 'events';
import { ITerminal, IPtyForkOptions } from './interfaces';
import { ITerminal, IPtyForkOptions, IProcessEnv } from './interfaces';
import { EventEmitter2, IEvent } from './eventEmitter2';

@@ -26,14 +26,14 @@ import { IExitEvent } from './types';

export abstract class Terminal implements ITerminal {
protected _socket: Socket;
protected _pid: number;
protected _fd: number;
protected _socket!: Socket; // HACK: This is unsafe
protected _pid: number = 0;
protected _fd: number = 0;
protected _pty: any;
protected _file: string;
protected _name: string;
protected _cols: number;
protected _rows: number;
protected _file!: string; // HACK: This is unsafe
protected _name!: string; // HACK: This is unsafe
protected _cols: number = 0;
protected _rows: number = 0;
protected _readable: boolean;
protected _writable: boolean;
protected _readable: boolean = false;
protected _writable: boolean = false;

@@ -58,2 +58,7 @@ protected _internalee: EventEmitter;

// setup flow control handling
this.handleFlowControl = !!(opt?.handleFlowControl);
this._flowControlPause = opt?.flowControlPause || FLOW_CONTROL_PAUSE;
this._flowControlResume = opt?.flowControlResume || FLOW_CONTROL_RESUME;
if (!opt) {

@@ -73,7 +78,2 @@ return;

this._checkType('encoding', opt.encoding ? opt.encoding : undefined, 'string');
// setup flow control handling
this.handleFlowControl = !!(opt.handleFlowControl);
this._flowControlPause = opt.flowControlPause || FLOW_CONTROL_PAUSE;
this._flowControlResume = opt.flowControlResume || FLOW_CONTROL_RESUME;
}

@@ -164,5 +164,5 @@

if (eventName === 'close') {
return this._internalee.emit.apply(this._internalee, arguments);
return this._internalee.emit.apply(this._internalee, arguments as any);
}
return this._socket.emit.apply(this._socket, arguments);
return this._socket.emit.apply(this._socket, arguments as any);
}

@@ -191,4 +191,4 @@

public abstract get process(): string;
public abstract get master(): Socket;
public abstract get slave(): Socket;
public abstract get master(): Socket| undefined;
public abstract get slave(): Socket | undefined;

@@ -203,3 +203,3 @@ protected _close(): void {

protected _parseEnv(env: {[key: string]: string}): string[] {
protected _parseEnv(env: IProcessEnv): string[] {
const keys = Object.keys(env || {});

@@ -206,0 +206,0 @@ const pairs = [];

@@ -11,5 +11,3 @@ {

],
"alwaysStrict": true,
"noImplicitAny": true,
"noImplicitThis": true
"strict": true
},

@@ -16,0 +14,0 @@ "exclude": [

@@ -10,2 +10,5 @@ /**

import * as path from 'path';
import * as tty from 'tty';
import * as fs from 'fs';
import { constants } from 'os';
import { pollUntil } from './testUtils.test';

@@ -20,3 +23,3 @@

const term = new UnixTerminal('/bin/bash', [], {});
let regExp: RegExp;
let regExp: RegExp | undefined;
if (process.platform === 'linux') {

@@ -31,4 +34,5 @@ // https://linux.die.net/man/4/pts

if (regExp) {
assert.ok(regExp.test((<any>term)._pty), '"' + (<any>term)._pty + '" should match ' + regExp.toString());
assert.ok(regExp.test(term.ptsName), '"' + term.ptsName + '" should match ' + regExp.toString());
}
assert.ok(tty.isatty(term.fd));
});

@@ -41,4 +45,4 @@ });

term.on('data', (data) => {
assert.equal(typeof data, 'string');
assert.equal(data, '\u00E6');
assert.strictEqual(typeof data, 'string');
assert.strictEqual(data, '\u00E6');
done();

@@ -52,6 +56,6 @@ });

term.on('data', (data) => {
assert.equal(typeof data, 'object');
assert.strictEqual(typeof data, 'object');
assert.ok(data instanceof Buffer);
assert.equal(0xC3, data[0]);
assert.equal(0xA6, data[1]);
assert.strictEqual(0xC3, data[0]);
assert.strictEqual(0xA6, data[1]);
done();

@@ -62,12 +66,12 @@ });

const text = 'test æ!';
const term = new UnixTerminal(null, ['-c', 'echo "' + text + '"'], {
const term = new UnixTerminal(undefined, ['-c', 'echo "' + text + '"'], {
encoding: 'base64'
});
let buffer = '';
term.on('data', (data) => {
assert.equal(typeof data, 'string');
term.onData((data) => {
assert.strictEqual(typeof data, 'string');
buffer += data;
});
term.on('exit', () => {
assert.equal(Buffer.alloc(8, buffer, 'base64').toString().replace('\r', '').replace('\n', ''), text);
term.onExit(() => {
assert.strictEqual(Buffer.alloc(8, buffer, 'base64').toString().replace('\r', '').replace('\n', ''), text);
done();

@@ -83,4 +87,4 @@ });

if (term) {
term.slave.destroy();
term.master.destroy();
term.slave!.destroy();
term.master!.destroy();
}

@@ -93,3 +97,3 @@ });

let slavebuf = '';
term.slave.on('data', (data) => {
term.slave!.on('data', (data) => {
slavebuf += data;

@@ -99,3 +103,3 @@ });

let masterbuf = '';
term.master.on('data', (data) => {
term.master!.on('data', (data) => {
masterbuf += data;

@@ -112,6 +116,17 @@ });

term.slave.write('slave\n');
term.master.write('master\n');
term.slave!.write('slave\n');
term.master!.write('master\n');
});
});
describe('close', () => {
const term = new UnixTerminal('node');
it('should exit when terminal is destroyed programmatically', (done) => {
term.on('exit', (code, signal) => {
assert.strictEqual(code, 0);
assert.strictEqual(signal, constants.signals.SIGHUP);
done();
});
term.destroy();
});
});
describe('signals in parent and child', () => {

@@ -153,4 +168,4 @@ it('SIGINT - custom in parent and child', done => {

// handlers in parent and child should have been triggered
assert.equal(buffer.indexOf('SIGINT in child') !== -1, true);
assert.equal(buffer.indexOf('SIGINT in parent') !== -1, true);
assert.strictEqual(buffer.indexOf('SIGINT in child') !== -1, true);
assert.strictEqual(buffer.indexOf('SIGINT in parent') !== -1, true);
done();

@@ -195,4 +210,4 @@ });

// handlers in parent and child should have been triggered
assert.equal(buffer.indexOf('should not be printed') !== -1, false);
assert.equal(buffer.indexOf('SIGINT in parent') !== -1, true);
assert.strictEqual(buffer.indexOf('should not be printed') !== -1, false);
assert.strictEqual(buffer.indexOf('SIGINT in parent') !== -1, true);
done();

@@ -216,3 +231,3 @@ });

// no timeout in buffer
assert.equal(buffer, '');
assert.strictEqual(buffer, '');
done();

@@ -249,4 +264,4 @@ });

// should have called both handlers and only once
assert.equal(pHandlerCalled, 1);
assert.equal(buffer, 'SIGUSR1 in child\r\n');
assert.strictEqual(pHandlerCalled, 1);
assert.strictEqual(buffer, 'SIGUSR1 in child\r\n');
done();

@@ -256,3 +271,108 @@ });

});
describe('spawn', () => {
it('should handle exec() errors', (done) => {
try {
new UnixTerminal('/bin/bogus.exe', []);
done(new Error('should have failed'));
} catch {
done();
}
});
it('should handle chdir() errors', (done) => {
try {
new UnixTerminal('/bin/echo', [], { cwd: '/nowhere' });
done(new Error('should have failed'));
} catch (e) {
assert.strictEqual((e as any).toString(), 'Error: chdir() failed: No such file or directory');
done();
}
});
it('should handle setuid() errors', (done) => {
try {
new UnixTerminal('/bin/echo', [], { uid: 999999 });
done(new Error('should have failed'));
} catch (e) {
assert.strictEqual((e as any).toString(), 'Error: setuid() failed: Operation not permitted');
done();
}
});
if (process.platform === 'linux') {
it('should not close on exec when closeFDs is not defined', (done) => {
const data = `
var pty = require('./lib/index');
var ptyProcess = pty.spawn('node', ['-e', 'setTimeout(() => console.log("hello from terminal"), 300);']);
ptyProcess.on('data', function (data) {
console.log(data);
});
setTimeout(() => null, 500);
console.log('ready', ptyProcess.pid);
`;
const buffer: string[] = [];
const readFd = fs.openSync(FIXTURES_PATH, 'r');
const p = cp.spawn('node', ['-e', data], {
stdio: ['ignore', 'pipe', 'pipe', readFd]
});
let sub = '';
p.stdout!.on('data', (data) => {
if (!data.toString().indexOf('ready')) {
sub = data.toString().split(' ')[1].slice(0, -1);
try {
fs.statSync(`/proc/${sub}/fd/${readFd}`);
} catch (_) {
done('not reachable');
}
setTimeout(() => {
process.kill(parseInt(sub), 'SIGINT'); // SIGINT to child
p.kill('SIGINT'); // SIGINT to parent
}, 200);
} else {
buffer.push(data.toString().replace(/^\s+|\s+$/g, ''));
}
});
p.on('close', () => {
done();
});
});
it('should close on exec when closeFDs is true', (done) => {
const data = `
var pty = require('./lib/index');
var ptyProcess = pty.spawn('node', ['-e', 'setTimeout(() => console.log("hello from terminal"), 300);'], {
closeFDs: true
});
ptyProcess.on('data', function (data) {
console.log(data);
});
setTimeout(() => null, 500);
console.log('ready', ptyProcess.pid);
`;
const buffer: string[] = [];
const readFd = fs.openSync(FIXTURES_PATH, 'r');
const p = cp.spawn('node', ['-e', data], {
stdio: ['ignore', 'pipe', 'pipe', readFd]
});
let sub = '';
p.stdout!.on('data', (data) => {
if (!data.toString().indexOf('ready')) {
sub = data.toString().split(' ')[1].slice(0, -1);
try {
fs.statSync(`/proc/${sub}/fd/${readFd}`);
done('not reachable');
} catch (error) {
assert.notStrictEqual(error.message.indexOf('ENOENT'), -1);
}
setTimeout(() => {
process.kill(parseInt(sub), 'SIGINT'); // SIGINT to child
p.kill('SIGINT'); // SIGINT to parent
}, 200);
} else {
buffer.push(data.toString().replace(/^\s+|\s+$/g, ''));
}
});
p.on('close', () => {
done();
});
});
}
});
});
}

@@ -7,2 +7,3 @@ /**

import * as net from 'net';
import * as path from 'path';
import { Terminal, DEFAULT_COLS, DEFAULT_ROWS } from './terminal';

@@ -14,7 +15,10 @@ import { IProcessEnv, IPtyForkOptions, IPtyOpenOptions } from './interfaces';

let pty: IUnixNative;
let helperPath: string;
try {
pty = require('../build/Release/pty.node');
helperPath = '../build/Release/spawn-helper';
} catch (outerError) {
try {
pty = require('../build/Debug/pty.node');
helperPath = '../build/Debug/spawn-helper';
} catch (innerError) {

@@ -27,2 +31,6 @@ console.error('innerError', innerError);

helperPath = path.resolve(__dirname, helperPath);
helperPath = helperPath.replace('app.asar', 'app.asar.unpacked');
helperPath = helperPath.replace('node_modules.asar', 'node_modules.asar.unpacked');
const DEFAULT_FILE = 'sh';

@@ -42,9 +50,9 @@ const DEFAULT_NAME = 'xterm';

private _boundClose: boolean;
private _emittedClose: boolean;
private _master: net.Socket;
private _slave: net.Socket;
private _boundClose: boolean = false;
private _emittedClose: boolean = false;
private _master: net.Socket | undefined;
private _slave: net.Socket | undefined;
public get master(): net.Socket { return this._master; }
public get slave(): net.Socket { return this._slave; }
public get master(): net.Socket | undefined { return this._master; }
public get slave(): net.Socket | undefined { return this._slave; }

@@ -66,4 +74,5 @@ constructor(file?: string, args?: ArgvOrCommandLine, opt?: IPtyForkOptions) {

this._rows = opt.rows || DEFAULT_ROWS;
const uid = opt.uid || -1;
const gid = opt.gid || -1;
const uid = opt.uid ?? -1;
const gid = opt.gid ?? -1;
const closeFDs = opt.closeFDs || false;
const env: IProcessEnv = assign({}, opt.env);

@@ -94,3 +103,3 @@

// destroyed when this occurs.
let timeout = setTimeout(() => {
let timeout: NodeJS.Timeout | null = setTimeout(() => {
timeout = null;

@@ -112,3 +121,3 @@ // Destroying the socket now will cause the close event to fire

// fork
const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), onexit);
const term = pty.fork(file, args, parsedEnv, cwd, this._cols, this._rows, uid, gid, (encoding === 'utf8'), closeFDs, onexit, helperPath);

@@ -179,2 +188,6 @@ this._socket = new PipeSocket(term.fd);

/* Accessors */
get fd(): number { return this._fd; }
get ptsName(): string { return this._pty; }
/**

@@ -215,3 +228,3 @@ * openpty

self._socket = self._master;
self._pid = null;
self._pid = -1;
self._fd = term.master;

@@ -218,0 +231,0 @@ self._pty = term.pty;

@@ -33,3 +33,3 @@ /**

private _worker: Worker;
private _drainTimeout: NodeJS.Timeout;
private _drainTimeout: NodeJS.Timeout | undefined;
private _isDisposed: boolean = false;

@@ -44,3 +44,4 @@

const workerData: IWorkerData = { conoutPipeName: _conoutPipeName };
this._worker = new Worker(join(__dirname, 'worker/conoutSocketWorker.js'), { workerData });
const scriptPath = __dirname.replace('node_modules.asar', 'node_modules.asar.unpacked');
this._worker = new Worker(join(scriptPath, 'worker/conoutSocketWorker.js'), { workerData });
this._worker.on('message', (message: ConoutWorkerMessage) => {

@@ -47,0 +48,0 @@ switch (message) {

@@ -32,6 +32,6 @@ /**

private _outSocket: Socket;
private _pid: number;
private _innerPid: number;
private _innerPidHandle: number;
private _closeTimeout: NodeJS.Timer;
private _pid: number = 0;
private _innerPid: number = 0;
private _innerPidHandle: number = 0;
private _closeTimeout: NodeJS.Timer | undefined;
private _exitCode: number | undefined;

@@ -206,3 +206,3 @@ private _conoutSocketWorker: ConoutConnection;

public get exitCode(): number {
public get exitCode(): number | undefined {
if (this._useConpty) {

@@ -209,0 +209,0 @@ return this._exitCode;

@@ -68,3 +68,3 @@ /**

while (openList.length) {
const current = openList.shift();
const current = openList.shift()!;
ps.filter(p => p.ppid === current.pid).map(p => {

@@ -85,3 +85,2 @@ return { name: p.name, pid: p.pid };

assert.fail(`Bad process state, expected: ${size}, actual: ${list.length}`);
resolve();
}

@@ -110,6 +109,6 @@ });

pollForProcessTreeSize(term.pid, 4, 500, 5000).then(list => {
assert.equal(list[0].name, 'cmd.exe');
assert.equal(list[1].name, 'powershell.exe');
assert.equal(list[2].name, 'notepad.exe');
assert.equal(list[3].name, 'node.exe');
assert.strictEqual(list[0].name.toLowerCase(), 'cmd.exe');
assert.strictEqual(list[1].name.toLowerCase(), 'powershell.exe');
assert.strictEqual(list[2].name.toLowerCase(), 'notepad.exe');
assert.strictEqual(list[3].name.toLowerCase(), 'node.exe');
term.kill();

@@ -116,0 +115,0 @@ const desiredState: IProcessState = {};

@@ -150,3 +150,3 @@ /**

}
this._defer(() => {
this._deferNoArgs(() => {
this._agent.resize(cols, rows);

@@ -159,3 +159,3 @@ this._cols = cols;

public destroy(): void {
this._defer(() => {
this._deferNoArgs(() => {
this.kill();

@@ -166,3 +166,3 @@ });

public kill(signal?: string): void {
this._defer(() => {
this._deferNoArgs(() => {
if (signal) {

@@ -176,5 +176,18 @@ throw new Error('Signals not supported on windows.');

private _defer<A extends any>(deferredFn: (arg?: A) => void, arg?: A): void {
private _deferNoArgs<A>(deferredFn: () => void): void {
// If the terminal is ready, execute.
if (this._isReady) {
deferredFn.call(this);
return;
}
// Queue until terminal is ready.
this._deferreds.push({
run: () => deferredFn.call(this)
});
}
private _defer<A>(deferredFn: (arg: A) => void, arg: A): void {
// If the terminal is ready, execute.
if (this._isReady) {
deferredFn.call(this, arg);

@@ -181,0 +194,0 @@ return;

@@ -17,3 +17,7 @@ /**

server.listen(getWorkerPipeName(conoutPipeName));
if (!parentPort) {
throw new Error('worker_threads parentPort is null');
}
parentPort.postMessage(ConoutWorkerMessage.READY);
});

@@ -81,7 +81,13 @@ /**

/**
* Security warning: use this option with great caution, as opened file descriptors
* with higher privileges might leak to the child program.
* Security warning: use this option with great caution unless `closeFDs` is also set,
* as opened file descriptors with higher privileges might leak to the child program.
*/
uid?: number;
gid?: number;
/**
* Close all open file after spawning the child process (UNIX only).
* Carries a performance penalty on Linux.
*/
closeFDs?: boolean;
}

@@ -169,3 +175,3 @@

* Resizes the dimensions of the pty.
* @param columns THe number of columns to use.
* @param columns The number of columns to use.
* @param rows The number of rows to use.

@@ -172,0 +178,0 @@ */

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc