New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

big-dig

Package Overview
Dependencies
Maintainers
5
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

big-dig - npm Package Compare versions

Comparing version 0.5.0 to 0.6.0

.npmignore

12

package.json
{
"name": "big-dig",
"version": "0.5.0",
"version": "0.6.0",
"description": "Secure, re-connectable channel for bidirectional communication with a remote host.",

@@ -10,9 +10,10 @@ "author": "Nuclide : Remote",

"prepublish": "../scripts/prepublish.sh",
"test": "node ../nuclide-jasmine/bin/jasmine-node-transpiled spec"
"test": "node ../nuclide-jest/bin/jest-node.js"
},
"dependencies": {
"async-to-generator": "1.1.0",
"double-ended-queue": "2.1.0-0",
"event-kit": "2.2.0",
"log4js": "1.1.1",
"nuclide-commons": "0.4.0",
"nuclide-commons": "0.6.0",
"request": "2.79.0",

@@ -22,4 +23,9 @@ "rimraf": "2.6.2",

"ssh2": "0.5.4",
"temp": "0.8.3",
"uuid": "3.0.1",
"ws": "3.2.0"
},
"devDependencies": {
"nuclide-jest": "0.6.0"
}
}

@@ -10,2 +10,8 @@ 'use strict';

var _log4js;
function _load_log4js() {
return _log4js = require('log4js');
}
/**

@@ -16,21 +22,10 @@ * This class is responsible for talking to a Big Dig server, which enables the

*/
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
class BigDigClient {
constructor(webSocketTransport, heartbeat) {
this._webSocketTransport = webSocketTransport;
constructor(reliableSocketTransport) {
this._logger = (0, (_log4js || _load_log4js()).getLogger)();
this._transport = reliableSocketTransport;
this._tagToSubject = new Map();
const observable = webSocketTransport.onMessage();
const observable = reliableSocketTransport.onMessage();
observable.subscribe({

@@ -46,37 +41,28 @@ // Must use arrow function so that `this` is bound correctly.

} else {
// eslint-disable-next-line no-console
console.warn(`No one listening for tag "${tag}".`);
this._logger.warn(`No one listening for tag "${tag}".`);
}
},
error(err) {
// eslint-disable-next-line no-console
console.error('Error received in ConnectionWrapper', err);
this._logger.error('Error received in ConnectionWrapper', err);
},
complete() {
// eslint-disable-next-line no-console
console.error('ConnectionWrapper completed()?');
this._logger.error('ConnectionWrapper completed()?');
}
});
this._heartbeat = heartbeat;
this._heartbeat.onConnectionRestored(() => {
// eslint-disable-next-line no-console
console.warn('TODO(T25533063): Implement reconnect logic');
});
}
isClosed() {
return this._webSocketTransport.isClosed();
return this._transport.isClosed();
}
onClose(callback) {
return this._webSocketTransport.onClose(callback);
return this._transport.onClose(callback);
}
close() {
this._webSocketTransport.close();
this._transport.close();
}
sendMessage(tag, body) {
this._webSocketTransport.send(`${tag}\0${body}`);
this._transport.send(`${tag}\0${body}`);
}

@@ -94,13 +80,19 @@

getHeartbeat() {
return this._heartbeat;
return this._transport.getHeartbeat();
}
getAddress() {
return this._webSocketTransport.getAddress();
return this._transport.getAddress();
}
dispose() {
// TODO(mbolin)
}
}
exports.BigDigClient = BigDigClient;
exports.BigDigClient = BigDigClient; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/

@@ -9,10 +9,2 @@ 'use strict';

var _ws;
function _load_ws() {
return _ws = _interopRequireDefault(require('ws'));
}
var _https = _interopRequireDefault(require('https'));
var _BigDigServer;

@@ -24,8 +16,2 @@

var _WebSocketTransport;
function _load_WebSocketTransport() {
return _WebSocketTransport = require('./WebSocketTransport');
}
var _BigDigClient;

@@ -37,6 +23,6 @@

var _XhrConnectionHeartbeat;
var _ReliableSocket;
function _load_XhrConnectionHeartbeat() {
return _XhrConnectionHeartbeat = require('./XhrConnectionHeartbeat');
function _load_ReliableSocket() {
return _ReliableSocket = require('../socket/ReliableSocket');
}

@@ -49,30 +35,14 @@

*/
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
exports.default = (() => {
var _ref = (0, _asyncToGenerator.default)(function* (config) {
const options = {
ca: config.certificateAuthorityCertificate,
cert: config.clientCertificate,
key: config.clientKey
};
const socket = new (_ws || _load_ws()).default(`wss://${config.host}:${config.port}/v1`, options);
yield new Promise(function (resolve, reject) {
socket.once('open', resolve);
socket.once('error', reject);
});
const agent = new _https.default.Agent(options);
const webSocketTransport = new (_WebSocketTransport || _load_WebSocketTransport()).WebSocketTransport('test', agent, socket);
const heartbeat = new (_XhrConnectionHeartbeat || _load_XhrConnectionHeartbeat()).XhrConnectionHeartbeat(`https://${config.host}:${config.port}`, (_BigDigServer || _load_BigDigServer()).HEARTBEAT_CHANNEL, options);
return new (_BigDigClient || _load_BigDigClient()).BigDigClient(webSocketTransport, heartbeat);
const reliableSocket = createReliableSocket(config);
const client = new (_BigDigClient || _load_BigDigClient()).BigDigClient(reliableSocket);
try {
// Make sure we're able to make the initial connection
yield reliableSocket.testConnection();
return client;
} catch (error) {
client.close();
throw error;
}
});

@@ -85,2 +55,31 @@

return createBigDigClient;
})();
})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
function createReliableSocket(config) {
const options = {
ca: config.certificateAuthorityCertificate,
cert: config.clientCertificate,
key: config.clientKey,
family: config.family
};
const serverUri = `https://${config.host}:${config.port}/v1`;
const reliableSocket = new (_ReliableSocket || _load_ReliableSocket()).ReliableSocket(serverUri, (_BigDigServer || _load_BigDigServer()).HEARTBEAT_CHANNEL, options);
if (!config.ignoreIntransientErrors) {
reliableSocket.onIntransientError(error => reliableSocket.close());
}
return reliableSocket;
}

@@ -44,9 +44,13 @@ 'use strict';

return new Promise((resolve, reject) => {
_dns.default.lookup(host, family, (error, address) => {
_dns.default.lookup(host, family, (error, address, resultFamily) => {
if (error) {
reject(error);
} else if (address != null) {
resolve(address);
if (!(resultFamily === 4 || resultFamily === 6)) {
throw new Error('Invariant violation: "resultFamily === 4 || resultFamily === 6"');
}
resolve({ address, family: resultFamily });
} else {
reject(Error('One of error or address must be set.'));
reject(new Error('One of error or address must be set.'));
}

@@ -53,0 +57,0 @@ });

@@ -82,7 +82,6 @@ 'use strict';

const hash = _crypto.default.createHash('sha1');
for (const file of files) {
if (file.filename === manifestFilename || file.stats.isDirectory()) {
// Exclude the manifest file and directories from the manifest.
continue;
}
const sortedFiles = files
// Exclude the manifest file and directories from the manifest.
.filter(file => file.filename !== manifestFilename && !file.stats.isDirectory()).sort((a, b) => a.filename.localeCompare(b.filename));
for (const file of sortedFiles) {
const filename = _path.default.relative(basePath, file.filename);

@@ -89,0 +88,0 @@ const { mode, uid, gid, size, mtime } = file.stats;

@@ -12,2 +12,8 @@ 'use strict';

var _log4js;
function _load_log4js() {
return _log4js = require('log4js');
}
var _net = _interopRequireDefault(require('net'));

@@ -72,14 +78,15 @@

// Sync word and regex pattern for parsing command stdout.
const READY_TIMEOUT_MS = 120 * 1000; /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
const READY_TIMEOUT_MS = 120 * 1000;
const SFTP_TIMEOUT_MS = 20 * 1000;

@@ -90,2 +97,4 @@

const logger = (0, (_log4js || _load_log4js()).getLogger)('SshHandshake');
const SupportedMethods = Object.freeze({

@@ -108,3 +117,3 @@ SSL_AGENT: 'SSL_AGENT',

UNSUPPORTED_AUTH_METHOD: 'UNSUPPORTED_AUTH_METHOD',
USER_CANCELLED: 'USER_CANCELLED',
USER_CANCELED: 'USER_CANCELLED',
SERVER_SETUP_FAILED: 'SERVER_SETUP_FAILED'

@@ -159,3 +168,3 @@ });

this.innerError = innerError;
this.isCancellation = errorType === SshHandshake.ErrorType.USER_CANCELLED;
this.isCancellation = errorType === SshHandshake.ErrorType.USER_CANCELED;
}

@@ -183,3 +192,3 @@ }

constructor(delegate, connection) {
this._cancelled = false;
this._canceled = false;
this._delegate = delegate;

@@ -248,3 +257,10 @@ this._connection = new (_SshClient || _load_SshClient()).SshClient(connection ? connection : new (_ssh || _load_ssh()).Client(), this._onKeyboardInteractive.bind(this));

} catch (error) {
throw new SshHandshakeError(`Failed to read private key at ${expandedPath}.`, SshHandshake.ErrorType.CANT_READ_PRIVATE_KEY, error);
logger.warn(`Failed to read private key at ${expandedPath}, falling back to password auth`);
return {
host: address,
port: config.sshPort,
username: config.username,
tryKeyboard: true,
readyTimeout: READY_TIMEOUT_MS
};
}

@@ -279,2 +295,5 @@

return (0, _asyncToGenerator.default)(function* () {
if (_this2._canceled) {
throw new SshHandshakeError('Connection has been cancelled by the user', SshHandshake.ErrorType.USER_CANCELED);
}
try {

@@ -288,2 +307,5 @@ yield _this2._connection.connect(config);

return new SshAuthError(error, { needsPrivateKeyPassword: false });
} else if (error.level !== undefined) {
const errorType = error.level && SshConnectionErrorLevelMap.get(error.level) || SshHandshake.ErrorType.UNKNOWN;
throw new SshHandshakeError(error.message, errorType, error);
} else {

@@ -335,2 +357,3 @@ throw error;

while (authError != null && attempts < PASSWORD_RETRIES) {
const retry = attempts > 0;
const retryText = attempts > 0 ? ' again' : '';

@@ -345,3 +368,3 @@ const prompt = `Authentication failed. Try entering your password${retryText}: `;

echo: false,
retry: true
retry
});

@@ -369,4 +392,4 @@ // eslint-disable-next-line no-await-in-loop

_wrapError(error) {
if (this._cancelled) {
return new SshHandshakeError('Cancelled by user', SshHandshake.ErrorType.USER_CANCELLED, error);
if (this._canceled) {
return new SshHandshakeError('Cancelled by user', SshHandshake.ErrorType.USER_CANCELED, error);
} else if (error instanceof SshHandshakeError) {

@@ -390,11 +413,13 @@ return error;

_this4._config = config;
_this4._cancelled = false;
_this4._canceled = false;
_this4._willConnect();
let address;
let lookup;
try {
address = yield (0, (_lookupPreferIpV || _load_lookupPreferIpV()).default)(config.host);
lookup = yield (0, (_lookupPreferIpV || _load_lookupPreferIpV()).default)(config.host);
} catch (error) {
throw new SshHandshakeError('Failed to resolve DNS.', SshHandshake.ErrorType.HOST_NOT_FOUND, error);
}
const { address, family } = lookup;
_this4._remoteFamily = family;

@@ -424,3 +449,3 @@ const connectConfig = yield _this4._getConnectConfig(address, config);

return (0, _asyncToGenerator.default)(function* () {
_this5._cancelled = true;
_this5._canceled = true;
yield _this5._connection.end();

@@ -546,2 +571,5 @@ })();

} catch (error) {
if (error instanceof SshHandshakeError) {
throw error;
}
throw new SshHandshakeError('Unknown error while acquiring server start information', SshHandshake.ErrorType.UNKNOWN, error);

@@ -631,6 +659,7 @@ }

jsonOutputFile: remoteTempFile,
timeout: '60s', // Currently unused and not configurable.
expiration: '7d',
timeout: 60000,
expiration: '14d',
serverParams: _this10._config.remoteServerCustomParams,
port: _this10._config.remoteServerPort
exclusive: _this10._config.exclusive,
ports: _this10._config.remoteServerPorts
};

@@ -644,3 +673,3 @@

if (code !== 0) {
throw new SshHandshakeError('Remote shell execution failed', SshHandshake.ErrorType.UNKNOWN, new Error(stdout));
throw new SshHandshakeError('Remote shell execution failed', SshHandshake.ErrorType.SERVER_START_FAILED, new Error(stdout));
}

@@ -671,9 +700,13 @@

if (_this11._isSecure()) {
// flowlint-next-line sketchy-null-string:off
if (!_this11._remoteHost) {
throw new Error('Invariant violation: "this._remoteHost"');
if (!(_this11._remoteHost != null)) {
throw new Error('Invariant violation: "this._remoteHost != null"');
}
if (!(_this11._remoteFamily != null)) {
throw new Error('Invariant violation: "this._remoteFamily != null"');
}
return _this11._didConnect({
host: _this11._remoteHost,
family: _this11._remoteFamily,
port: _this11._remotePort,

@@ -692,10 +725,14 @@ certificateAuthorityCertificate: _this11._certificateAuthorityCertificate,

const localPort = _this11._getLocalPort();
// flowlint-next-line sketchy-null-number:off
if (!localPort) {
throw new Error('Invariant violation: "localPort"');
if (!(localPort != null)) {
throw new Error('Invariant violation: "localPort != null"');
}
if (!(_this11._remoteFamily != null)) {
throw new Error('Invariant violation: "this._remoteFamily != null"');
}
return _this11._didConnect({
host: 'localhost',
family: _this11._remoteFamily,
port: localPort

@@ -702,0 +739,0 @@ });

@@ -22,2 +22,14 @@ 'use strict';

var _log4js;
function _load_log4js() {
return _log4js = require('log4js');
}
var _promise;
function _load_promise() {
return _promise = require('nuclide-commons/promise');
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -41,2 +53,5 @@

const CERT_NOT_YET_VALID_DELAY = 3000;
const CERT_NOT_YET_VALID_RETRIES = 3;
class XhrConnectionHeartbeat {

@@ -72,4 +87,20 @@

return (0, _asyncToGenerator.default)(function* () {
const { body } = yield (0, (_asyncRequest || _load_asyncRequest()).default)(_this._options);
return body;
let retries = CERT_NOT_YET_VALID_RETRIES;
while (true) {
try {
// eslint-disable-next-line no-await-in-loop
const { body } = yield (0, (_asyncRequest || _load_asyncRequest()).default)(_this._options);
return body;
} catch (err) {
if (retries-- > 0 && err.code === 'CERT_NOT_YET_VALID') {
(0, (_log4js || _load_log4js()).getLogger)('XhrConnectionHeartbeat').warn(`Certificate not yet valid, retrying after ${CERT_NOT_YET_VALID_DELAY}ms...`);
// eslint-disable-next-line no-await-in-loop
yield (0, (_promise || _load_promise()).sleep)(CERT_NOT_YET_VALID_DELAY);
} else {
throw err;
}
}
}
// eslint-disable-next-line no-unreachable
throw Error('unreachable');
})();

@@ -76,0 +107,0 @@ }

@@ -13,4 +13,4 @@ 'use strict';

if (!username) {
throw new Error('Invariant violation: "username"');
if (!(username != null)) {
throw new Error('Invariant violation: "username != null"');
}

@@ -17,0 +17,0 @@

@@ -24,2 +24,14 @@ 'use strict';

var _WebSocketTransport;
function _load_WebSocketTransport() {
return _WebSocketTransport = require('../socket/WebSocketTransport');
}
var _QueuedAckTransport;
function _load_QueuedAckTransport() {
return _QueuedAckTransport = require('../socket/QueuedAckTransport');
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }

@@ -50,2 +62,3 @@

this._httpsServer.on('request', this._onHttpsRequest.bind(this));
this._clientIdToTransport = new Map();
this._webSocketServer = webSocketServer;

@@ -70,3 +83,3 @@ this._webSocketServer.on('connection', this._onWebSocketConnection.bind(this));

const { pathname } = _url.default.parse(request.url);
if (pathname === `/${HEARTBEAT_CHANNEL}`) {
if (request.method === 'POST' && pathname === `/v1/${HEARTBEAT_CHANNEL}`) {
response.write((0, (_getVersion || _load_getVersion()).getVersion)());

@@ -76,9 +89,11 @@ response.end();

}
this._logger.info(`Ignored HTTPS request for ${request.url}`);
this._logger.info(`Ignored HTTPS ${request.method} request for ${request.url}`);
}
_onWebSocketConnection(ws, req) {
// Note that in ws@3.0.0, the upgradeReq property of ws has been removed:
// it is passed as the second argument to this callback instead.
const { pathname } = _url.default.parse(req.url);
const clientId = req.headers.client_id;
this._logger.info(`connection negotiation via path ${String(pathname)}`);
this._logger.info(`received client_id in header ${clientId}`);
if (pathname !== '/v1') {

@@ -89,33 +104,51 @@ this._logger.info(`Ignored WSS connection for ${String(pathname)}`);

// Every subscriber must be notified of the new connection because it may
// want to broadcast messages to it.
const tagToTransport = new Map();
for (const [tag, subscriber] of this._tagToSubscriber) {
const transport = new InternalTransport(tag, ws);
this._logger.info(`Created new InternalTransport for ${tag}`);
tagToTransport.set(tag, transport);
subscriber.onConnection(transport);
}
const cachedTransport = this._clientIdToTransport.get(clientId);
const wsTransport = new (_WebSocketTransport || _load_WebSocketTransport()).WebSocketTransport(clientId, ws);
// Is message a string or could it be a Buffer?
ws.on('message', message => {
// The message must start with a header identifying its route.
const index = message.indexOf('\0');
const tag = message.substring(0, index);
const body = message.substring(index + 1);
if (cachedTransport == null) {
this._logger.info(`on connection the clientId is ${clientId}`);
const transport = tagToTransport.get(tag);
if (transport != null) {
transport.broadcastMessage(body);
} else {
this._logger.info(`No route for ${tag}.`);
const qaTransport = new (_QueuedAckTransport || _load_QueuedAckTransport()).QueuedAckTransport(clientId, wsTransport);
this._clientIdToTransport.set(clientId, qaTransport);
// Every subscriber must be notified of the new connection because it may
// want to broadcast messages to it.
const tagToTransport = new Map();
for (const [tag, subscriber] of this._tagToSubscriber) {
const transport = new InternalTransport(tag, qaTransport);
this._logger.info(`Created new InternalTransport for ${tag}`);
tagToTransport.set(tag, transport);
subscriber.onConnection(transport);
}
});
// TODO(mbolin): When ws disconnects, do we explicitly have to clear out
// tagToTransport? It seems like it should get garbage-collected
// automatically, assuming this._webSocketServer no longer has a reference
// to ws. But we should probably call InternalTransport.close() on all of
// the entries in tagToTransport?
// subsequent messages will be BigDig messages
// TODO: could the message be a Buffer?
qaTransport.onMessage().subscribe(message => {
this._handleBigDigMessage(tagToTransport, message);
});
// TODO: Either garbage collect inactive transports, or implement
// an explicit "close" action in the big-dig protocol.
} else {
if (!(clientId === cachedTransport.id)) {
throw new Error('Invariant violation: "clientId === cachedTransport.id"');
}
cachedTransport.reconnect(wsTransport);
}
}
_handleBigDigMessage(tagToTransport, message) {
// The message must start with a header identifying its route.
const index = message.indexOf('\0');
const tag = message.substring(0, index);
const body = message.substring(index + 1);
const transport = tagToTransport.get(tag);
if (transport != null) {
transport.broadcastMessage(body);
} else {
this._logger.info(`No route for ${tag}.`);
}
}
}

@@ -135,13 +168,7 @@

this._tag = tag;
this._ws = ws;
this._transport = ws;
}
send(message) {
this._ws.send(`${this._tag}\0${message}`, err => {
if (err != null) {
// This may happen if the client disconnects.
// TODO: use the reliable transport from Nuclide when that's ready.
(0, (_log4js || _load_log4js()).getLogger)().warn('Error sending websocket message', err);
}
});
this._transport.send(`${this._tag}\0${message}`);
}

@@ -148,0 +175,0 @@

@@ -13,27 +13,33 @@ 'use strict';

*/
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
let generateCertificates = exports.generateCertificates = (() => {
var _ref = (0, _asyncToGenerator.default)(function* (clientCommonName, serverCommonName, openSSLConfigPath, sharedCertsDir, expirationDays) {
const paths = yield generateKeyPairPaths(sharedCertsDir);
const env = generateEnvironmentForOpenSSLCalls(serverCommonName);
yield generateCA(paths.caKey, paths.caCert, expirationDays, env);
yield Promise.all([generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.serverKey, paths.serverCsr, paths.serverCert, openSSLConfigPath, serverCommonName, 1, env), generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.clientKey, paths.clientCsr, paths.clientCert, openSSLConfigPath, clientCommonName, 2, env)]);
return paths;
var _ref = (0, _asyncToGenerator.default)(function* (clientCommonName, serverCommonName, openSSLConfigPath, expirationDays) {
// Set the process umask to 0077 to ensure that certificates have 0700 permissions.
// The spawned OpenSSL processes will inherit the umask.
const oldUmask = process.umask();
process.umask(0o77);
try {
const paths = yield generateKeyPairPaths();
const env = generateEnvironmentForOpenSSLCalls(serverCommonName);
yield generateCA(paths.caKey, paths.caCert, expirationDays, env);
yield Promise.all([generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.serverKey, paths.serverCsr, paths.serverCert, openSSLConfigPath, serverCommonName, 1, env), generateKeyAndCertificate(paths.caKey, paths.caCert, expirationDays, paths.clientKey, paths.clientCsr, paths.clientCert, openSSLConfigPath, clientCommonName, 2, env)]);
return paths;
} finally {
process.umask(oldUmask);
}
});
return function generateCertificates(_x, _x2, _x3, _x4, _x5) {
return function generateCertificates(_x, _x2, _x3, _x4) {
return _ref.apply(this, arguments);
};
})();
})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/

@@ -48,3 +54,3 @@ let generateCA = (() => {

return function generateCA(_x6, _x7, _x8, _x9) {
return function generateCA(_x5, _x6, _x7, _x8) {
return _ref2.apply(this, arguments);

@@ -63,3 +69,3 @@ };

return function generateKeyAndCertificate(_x10, _x11, _x12, _x13, _x14, _x15, _x16, _x17, _x18, _x19) {
return function generateKeyAndCertificate(_x9, _x10, _x11, _x12, _x13, _x14, _x15, _x16, _x17, _x18) {
return _ref3.apply(this, arguments);

@@ -70,3 +76,3 @@ };

/**
* Creates a new directory under `sharedCertsDir` where all of the certificate data for one instance
* Creates a new temporary directory where all of the certificate data for one instance
* of the server should be written.

@@ -77,5 +83,5 @@ */

let generateKeyPairPaths = (() => {
var _ref4 = (0, _asyncToGenerator.default)(function* (sharedCertsDir) {
const certsDir = yield (_fs || _load_fs()).default.mkdtemp(sharedCertsDir);
const pathPrefix = (_nuclideUri || _load_nuclideUri()).default.join(certsDir, 'nuclide');
var _ref4 = (0, _asyncToGenerator.default)(function* () {
const certsDir = yield (_fs || _load_fs()).default.mkdtemp((_nuclideUri || _load_nuclideUri()).default.join(_os.default.tmpdir(), '.big-dig-certs'));
const pathPrefix = (_nuclideUri || _load_nuclideUri()).default.join(certsDir, 'big-dig');
return {

@@ -94,3 +100,3 @@ certsDir,

return function generateKeyPairPaths(_x20) {
return function generateKeyPairPaths() {
return _ref4.apply(this, arguments);

@@ -102,2 +108,4 @@ };

var _os = _interopRequireDefault(require('os'));
var _nuclideUri;

@@ -125,2 +133,7 @@

const env = Object.assign({}, process.env);
if (process.platform === 'darwin') {
// High Sierra comes with LibreSSL by default, which is not supported.
// Often, OpenSSL may be installed by Homebrew.
env.PATH = '/usr/local/opt/openssl/bin:' + env.PATH;
}
// Usually, we don't have to make the common name a SAN,

@@ -127,0 +140,0 @@ // but our openssl.cnf requires a value via $OPENSSL_SAN.

@@ -20,4 +20,4 @@ 'use strict';

const params = JSON.parse(process.argv[process.argv.length - 1]);
const { cname, expiration, jsonOutputFile } = params;
let { port } = params;
const { cname, expiration, exclusive, jsonOutputFile } = params;
let { ports, timeout } = params;
if (typeof cname !== 'string') {

@@ -31,14 +31,16 @@ throw Error(`cname must be specified as string but was: '${cname}'`);

// port arg validation
if (port == null) {
port = DEFAULT_PORT;
if (ports == null) {
ports = DEFAULT_PORTS;
}
if (typeof port !== 'number') {
throw Error(`port must be specified as number but was: '${port}'`);
if (typeof ports !== 'string') {
throw Error(`ports must be specified as string but was: '${ports}'`);
}
// eslint-disable-next-line no-bitwise
if ((port | 0) !== port) {
throw Error(`port must be an integer but was: '${port}'`);
// This will throw an exception if the ports string is invalid.
(0, (_ports || _load_ports()).parsePorts)(ports);
if (timeout == null) {
timeout = DEFAULT_TIMEOUT;
}
if (port < 0) {
throw Error(`port must be >=0 but was ${port}`);
if (typeof timeout !== 'number') {
throw Error(`timeout must be specified as number but was: '${timeout}'`);
}

@@ -59,2 +61,6 @@

if (exclusive != null && (typeof exclusive !== 'string' || !exclusive.match(/^[\w\d][\w\d-]*$/))) {
throw Error(`exclusive must be a valid identifier: '${exclusive}'`);
}
const clientCommonName = 'nuclide';

@@ -64,3 +70,14 @@ const serverCommonName = cname || `${(0, (_username || _load_username()).getUsername)()}.nuclide.${_os.default.hostname()}`;

yield (0, (_main || _load_main()).generateCertificatesAndStartServer)(clientCommonName, serverCommonName, openSSLConfigPath, port, expirationDays, jsonOutputFile, absolutePathToServerMain, params.serverParams);
yield (0, (_main || _load_main()).generateCertificatesAndStartServer)({
clientCommonName,
serverCommonName,
openSSLConfigPath,
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams: params.serverParams
});
});

@@ -99,4 +116,22 @@

var _ports;
function _load_ports() {
return _ports = require('../common/ports');
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
(_log4js || _load_log4js()).default.configure({

@@ -109,14 +144,5 @@ appenders: [{

}]
}); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
});
const DEFAULT_PORT = 0;
const DEFAULT_PORTS = '0';
const DEFAULT_TIMEOUT = 60000;

@@ -5,21 +5,10 @@ 'use strict';

/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
let handleLaunchParams = (() => {
var _ref = (0, _asyncToGenerator.default)(function* (params) {
if (params.exclusive != null) {
yield enforceExclusive(params.exclusive);
}
let main = (() => {
var _ref = (0, _asyncToGenerator.default)(function* () {
const params = JSON.parse(process.argv[2]);
// TODO(mbolin): Do basic runtime validation on params.
const port = yield (0, (_NuclideServer || _load_NuclideServer()).launchServer)({
port: params.port,
ports: params.ports,
webServer: {

@@ -30,7 +19,10 @@ key: params.key,

},
absolutePathToServerMain: params.launcher,
absolutePathToServerMain: params.absolutePathToServerMain,
serverParams: params.serverParams
});
// $FlowIgnore
if (!(process.send != null)) {
throw new Error('Invariant violation: "process.send != null"');
}
process.send({ port }, function () {

@@ -43,5 +35,11 @@ if (!process.disconnect) {

});
// Exit once the certificates expire, as no clients will be able to connect at this point.
setTimeout(function () {
(_log4js || _load_log4js()).default.getLogger().info(`Certificates expired after ${params.expirationDays} days, shutting down.`);
process.exit(2);
}, params.expirationDays * 24 * 60 * 60 * 1000);
});
return function main() {
return function handleLaunchParams(_x) {
return _ref.apply(this, arguments);

@@ -51,2 +49,77 @@ };

// When an 'exclusive' parameter is provided, we'll ensure that only one server
// with a given "exclusive" tag is alive at any given time (per user).
// We do this by storing a .bigdig.exclusive.pid file in sharedCertsDir:
// if the file already exists, we'll try to kill the PID in that file.
let enforceExclusive = (() => {
var _ref2 = (0, _asyncToGenerator.default)(function* (exclusive) {
const bigDigPath = (_nuclideUri || _load_nuclideUri()).default.join(_os.default.homedir(), '.big-dig');
try {
yield (_fs2 || _load_fs()).default.mkdir(bigDigPath);
} catch (err) {
if (err.code !== 'EEXIST') {
throw err;
}
}
const pidFile = (_nuclideUri || _load_nuclideUri()).default.join(bigDigPath, `.big-dig.${exclusive}.pid`);
while (true) {
try {
const c = _fs.default.constants;
// O_CREAT / O_EXCL atomically creates the PID file.
// Ideally we'd use fcntl/flock to hold onto the PID file until exit,
// but sadly there's no easy flock API in Node.
const handle = _fs.default.openSync(pidFile,
// eslint-disable-next-line no-bitwise
c.O_WRONLY | c.O_CREAT | c.O_EXCL,
// Readable only for the current user.
0o600);
(_log4js || _load_log4js()).default.getLogger().info(`Writing pid=${process.pid} to ${pidFile}`);
// $FlowFixMe: writeFileSync takes handles too.
_fs.default.writeFileSync(handle, process.pid);
_fs.default.closeSync(handle);
break;
} catch (error) {
if (error.code === 'EEXIST') {
// Note: the read, kill, and unlink steps could all throw.
// However, an exception at any of those steps probably indicates a race,
// in which case we should probably bail out anyway.
const pidContents = _fs.default.readFileSync(pidFile, 'utf8');
const pid = parseInt(pidContents, 10);
if (pid > 0) {
(_log4js || _load_log4js()).default.getLogger().info(`Killing existing server with pid=${pid}`);
// Node doesn't have any flock() style primitives, so we can't be certain
// that this pid still corresponds to the process.
// As a quick sanity check, we'll inspect the pstree to see that it's consistent.
// eslint-disable-next-line no-await-in-loop
const processTree = yield (0, (_process || _load_process()).psTree)();
const processInfo = processTree.find(function (proc) {
return proc.pid === pid;
});
if (processInfo != null && processInfo.commandWithArgs.includes('launchServer')) {
process.kill(pid);
}
}
_fs.default.unlinkSync(pidFile);
} else {
throw error;
}
}
}
// Attempt to clean up the pid file on graceful exits.
process.on('exit', function () {
_fs.default.unlinkSync(pidFile);
});
});
return function enforceExclusive(_x2) {
return _ref2.apply(this, arguments);
};
})();
var _fs = _interopRequireDefault(require('fs'));
var _log4js;

@@ -64,4 +137,16 @@

var _process;
function _load_process() {
return _process = require('nuclide-commons/process');
}
var _os = _interopRequireDefault(require('os'));
var _fs2;
function _load_fs() {
return _fs2 = _interopRequireDefault(require('../common/fs'));
}
var _NuclideServer;

@@ -75,2 +160,28 @@

function main() {
// launchServer should only be spawned from ./main.js.
if (process.send == null) {
// eslint-disable-next-line no-console
console.error('Error: launchServer should only be spawned via parseArgsAndRunMain.');
process.exit(1);
}
process.on('message', params => {
handleLaunchParams(params).catch(error => {
(_log4js || _load_log4js()).default.getLogger().fatal('launchServer failed:', error);
(_log4js || _load_log4js()).default.shutdown(() => process.exit(1));
});
});
} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
(_log4js || _load_log4js()).default.configure({

@@ -85,7 +196,4 @@ appenders: [{

main();
process.on('unhandledRejection', error => {
(_log4js || _load_log4js()).default.getLogger().fatal('Unhandled rejection:', error);
(_log4js || _load_log4js()).default.shutdown(() => process.exit(1));
(_log4js || _load_log4js()).default.getLogger().error('Unhandled rejection:', error);
});

@@ -95,3 +203,5 @@

(_log4js || _load_log4js()).default.getLogger().fatal('Uncaught exception:', error);
(_log4js || _load_log4js()).default.shutdown(() => process.exit(1));
});
(_log4js || _load_log4js()).default.shutdown(() => process.abort());
});
main();

@@ -10,30 +10,31 @@ 'use strict';

/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
let generateCertificatesAndStartServer = exports.generateCertificatesAndStartServer = (() => {
var _ref = (0, _asyncToGenerator.default)(function* (clientCommonName, serverCommonName, openSSLConfigPath, port, expirationDays, jsonOutputFile, absolutePathToServerMain, serverParams) {
var _ref = (0, _asyncToGenerator.default)(function* ({
clientCommonName,
serverCommonName,
openSSLConfigPath,
ports,
timeout,
expirationDays,
exclusive,
jsonOutputFile,
absolutePathToServerMain,
serverParams
}) {
const logger = (0, (_log4js || _load_log4js()).getLogger)();
logger.info('in generateCertificatesAndStartServer()');
// flowlint-next-line sketchy-null-string:off
const homeDir = process.env.HOME || process.env.USERPROFILE;
// flowlint-next-line sketchy-null-string:off
if (!homeDir) {
throw new Error('Invariant violation: "homeDir"');
}
const sharedCertsDir = (_nuclideUri || _load_nuclideUri()).default.join(homeDir, '.certs');
try {
yield (_fs || _load_fs()).default.mkdir(sharedCertsDir);
} catch (error) {
if (error.code !== 'EEXIST') {
throw error;
}
}
// HACK: kill existing servers on the given port.
try {
_child_process.default.execFileSync('pkill', ['-f', `launchServer-entry.js.*"port":${port}`]);
} catch (e) {}
const paths = yield (0, (_certificates || _load_certificates()).generateCertificates)(clientCommonName, serverCommonName, openSSLConfigPath, sharedCertsDir, expirationDays);
const paths = yield (0, (_certificates || _load_certificates()).generateCertificates)(clientCommonName, serverCommonName, openSSLConfigPath, expirationDays);
logger.info('generateCertificates() succeeded!');

@@ -46,16 +47,30 @@

ca: ca.toString(),
port,
launcher: absolutePathToServerMain,
ports,
expirationDays,
exclusive,
absolutePathToServerMain,
serverParams
};
// Redirect child stderr to a file so that we can read it.
// (If we just pipe it, there's no safe way of disconnecting it after.)
(_temp || _load_temp()).default.track();
const stderrLog = (_temp || _load_temp()).default.openSync('big-dig-stderr');
const launcherScript = require.resolve('./launchServer-entry.js');
logger.info(`About to spawn ${launcherScript} to launch Big Dig server.`);
const child = _child_process.default.spawn(process.execPath, [launcherScript, JSON.stringify(params)], {
const child = _child_process.default.spawn(process.execPath, [
// Increase stack trace limit for better debug logs.
// For reference, Atom/Electron does not have a stack trace limit.
'--stack-trace-limit=50',
// Increase the maximum heap size if we have enough memory.
...(_os.default.totalmem() > 8 * 1024 * 1024 * 1024 ? ['--max-old-space-size=4096'] : []), launcherScript], {
detached: true,
stdio: ['ignore', 'ignore', 'ignore', 'ipc']
stdio: ['ignore', 'ignore', stderrLog.fd, 'ipc']
});
logger.info(`spawn called for ${launcherScript}`);
// Send launch parameters over IPC to avoid making them visible in `ps`.
child.send(params);
const childPort = yield new Promise(function (resolve, reject) {
const childPort = yield (0, (_promise || _load_promise()).timeoutPromise)(new Promise(function (resolve, reject) {
const onMessage = function ({ port: result }) {

@@ -67,6 +82,20 @@ resolve(result);

child.on('error', reject);
child.on('exit', function (code) {
logger.info(`${launcherScript} exited with code ${code}`);
reject(Error(`child exited early with code ${code}`));
});
child.on('exit', (() => {
var _ref2 = (0, _asyncToGenerator.default)(function* (code) {
const stderr = yield (_fs || _load_fs()).default.readFileAsString(stderrLog.path).catch(function () {
return '';
});
reject(Error(`Child exited early with code ${code}.\nstderr: ${stderr}`));
});
return function (_x2) {
return _ref2.apply(this, arguments);
};
})());
}), timeout).catch(function (err) {
// Make sure we clean up hung children.
if (err instanceof (_promise || _load_promise()).TimedOutError) {
child.kill('SIGKILL');
}
return Promise.reject(err);
});

@@ -92,19 +121,13 @@

return function generateCertificatesAndStartServer(_x, _x2, _x3, _x4, _x5, _x6, _x7, _x8) {
return function generateCertificatesAndStartServer(_x) {
return _ref.apply(this, arguments);
};
})(); /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
})();
var _child_process = _interopRequireDefault(require('child_process'));
var _promise;
function _load_promise() {
return _promise = require('nuclide-commons/promise');
}
var _fs;

@@ -116,8 +139,4 @@

var _nuclideUri;
var _child_process = _interopRequireDefault(require('child_process'));
function _load_nuclideUri() {
return _nuclideUri = _interopRequireDefault(require('nuclide-commons/nuclideUri'));
}
var _log4js;

@@ -129,2 +148,10 @@

var _os = _interopRequireDefault(require('os'));
var _temp;
function _load_temp() {
return _temp = _interopRequireDefault(require('temp'));
}
var _certificates;

@@ -131,0 +158,0 @@

@@ -6,4 +6,78 @@ 'use strict';

});
exports.launchServer = launchServer;
exports.launchServer = undefined;
var _asyncToGenerator = _interopRequireDefault(require('async-to-generator'));
/**
* Launch a NuclideServer with the specified parameters.
*
* One common this may fail is if the specified port is in use. The caller is responsible for
* checking for this failure and retrying on a different port.
*
* The best way to avoid this is by specifying a port of 0, though that may not be an option if the
* host machine does not allow HTTP traffic to be served on an arbitrary port.
* Note that if options.port=0 is specified to choose an ephemeral port, then the caller should
* check server.address().port to see what the actual port is.
*/
// The absolutePathToServerMain must export a single function of this type.
/**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
let launchServer = exports.launchServer = (() => {
var _ref = (0, _asyncToGenerator.default)(function* (options) {
const webServer = _https.default.createServer(options.webServer);
let found = false;
for (const port of (0, (_ports || _load_ports()).parsePorts)(options.ports)) {
// eslint-disable-next-line no-await-in-loop
if (yield tryListen(webServer, port)) {
found = true;
break;
}
}
if (!found) {
throw Error(`All ports in range "${options.ports}" are already in use`);
}
const webSocketServer = new (_ws || _load_ws()).default.Server({
server: webServer,
perMessageDeflate: true
});
// Let unhandled WS server errors go through to the global exception handler.
// $FlowIgnore
const launcher = require(options.absolutePathToServerMain);
const bigDigServer = new (_BigDigServer || _load_BigDigServer()).default(webServer, webSocketServer);
yield launcher({
server: bigDigServer,
serverParams: options.serverParams
});
return webServer.address().port;
});
return function launchServer(_x) {
return _ref.apply(this, arguments);
};
})();
/**
* Attempts to have the https server listen to the specified port.
* Returns true if successful or false if the port is already in use.
* Any other errors result in a rejection.
*/
var _BigDigServer;

@@ -23,69 +97,26 @@

function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _ports;
/**
* Launch a NuclideServer with the specified parameters.
*
* One common this may fail is if the specified port is in use. The caller is responsible for
* checking for this failure and retrying on a different port.
*
* The best way to avoid this is by specifying a port of 0, though that may not be an option if the
* host machine does not allow HTTP traffic to be served on an arbitrary port.
* Note that if options.port=0 is specified to choose an ephemeral port, then the caller should
* check server.address().port to see what the actual port is.
*/
function _load_ports() {
return _ports = require('../common/ports');
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// The absolutePathToServerMain must export a single function of this type.
function launchServer(options) {
const webServer = _https.default.createServer(options.webServer);
function tryListen(server, port) {
return new Promise((resolve, reject) => {
// TODO(mbolin): Once the webServer is up and running and this Promise is resolved,
// rejecting the Promise will be a noop. We need better error handling here.
const onError = error => {
function onError(error) {
if (error.errno === 'EADDRINUSE') {
// eslint-disable-next-line
console.error(`ERROR: Port ${options.port} is already in use.`);
process.exit(1);
return resolve(false);
}
// Note that `error` could be an EADDRINUSE error.
webServer.removeAllListeners();
reject(error);
};
// TODO(mbolin): If we want the new WebSocketServer to get the 'connection' event,
// then we need to get it wired up before the webServer is connected.
webServer.on('listening', () => {
const webSocketServer = new (_ws || _load_ws()).default.Server({
server: webServer,
perMessageDeflate: true
});
webSocketServer.on('error', onError);
}
// $FlowIgnore
const launcher = require(options.absolutePathToServerMain);
const bigDigServer = new (_BigDigServer || _load_BigDigServer()).default(webServer, webSocketServer);
launcher({
server: bigDigServer,
serverParams: options.serverParams
}).then(() => {
// Now the NuclideServer should have attached its own error handler.
webServer.removeListener('error', onError);
resolve(webServer.address().port);
});
server.once('error', onError);
server.listen(port, () => {
// Let errors after the initial listen fall through to the global exception handler.
server.removeListener('error', onError);
resolve(true);
});
webServer.on('error', onError);
webServer.listen(options.port);
});
} /**
* Copyright (c) 2017-present, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*
*
* @format
*/
}

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