Merge branch 'main' into ICU-1335-close-all-sessions-on-exit

This commit is contained in:
Susmitha Girumala authored and GitHub committed 2021-05-17 14:52:39 -04:00
commit a1b0481db9
11 files changed
+86 -63

No files matched your search

+1 -1
View File
@@ -1,7 +1,7 @@
const path = require('path');
const { spawnSync } = require('../helpers/spawn-promise.js');
const cliPath = async () => path.resolve(__dirname, '..', 'cli', 'boundary');
const cliPath = async () => path.resolve(__dirname, '..', '..', 'cli', 'boundary');
module.exports = {
// Check boundary cli existence
@@ -1,4 +1,4 @@
const origin = require('../origin/index.js');
const runtimeSettings = require('../services/runtime-settings.js');
const isDev = require('electron-is-dev');
const csp = {
@@ -28,9 +28,9 @@ const generateCSPHeader = () => {
const policy = Object.assign({}, csp);
// If an origin is specified, add it to the connect-src directive
if (origin.origin) {
if (runtimeSettings.origin) {
policy['connect-src'] = policy['connect-src'].slice();
policy['connect-src'].push(origin.origin);
policy['connect-src'].push(runtimeSettings.origin);
}
return Object.keys(policy)
@@ -0,0 +1,26 @@
const { net } = require('electron');
const requestTimeoutSeconds = 10;
// Simple promise wrapper around Electron's net.request feature.
// https://www.electronjs.org/docs/api/client-request
const netRequestPromise = (url) =>
new Promise((resolve, reject) => {
try {
const request = net.request(url);
// We don't worry about canceling the request timeout timer once a
// request ends because: request.abort() has no effect after a request
// ends and the promise will already have been completed, so a rejection
// has no effect either.
const requestTimeout = setTimeout(() => {
request.abort();
reject(new Error('Request timeout'));
}, requestTimeoutSeconds * 1000);
request.on('error', reject);
request.on('response', resolve);
request.end();
} catch (e) {
reject(e);
}
});
module.exports = netRequestPromise;
+2 -2
View File
@@ -15,7 +15,7 @@ const {
} = require('electron');
require('./ipc/handlers.js');
const origin = require('./origin/index.js');
const runtimeSettings = require('./services/runtime-settings.js');
const { generateCSPHeader } = require('./config/content-security-policy.js');
const sessionManager = require('./session/manager.js');
@@ -132,7 +132,7 @@ app.on('ready', async () => {
// If the user-specified origin changes, reload the page so that
// the CSP can be refreshed with the this source allowed
origin.onOriginChange(() => mainWindow.loadURL(emberAppURL));
runtimeSettings.onOriginChange(() => mainWindow.loadURL(emberAppURL));
// If you want to open up dev tools programmatically, call
// mainWindow.openDevTools();
+9 -7
View File
@@ -2,28 +2,30 @@ const { shell } = require('electron');
const isDev = require('electron-is-dev');
const handle = require('./ipc-handler.js');
const boundaryCli = require('../cli/index.js');
const origin = require('../origin/index.js');
const sessionManager = require('../session/manager.js');
const sessionManager = require('../services/session-manager.js');
const runtimeSettings = require('../services/runtime-settings.js');
const sanitizer = require('../utils/sanitizer.js');
/**
* Returns the current runtime origin, which is used by the main thread to
* rewrite the CSP to allow requests.
*/
handle('getOrigin', () => origin.origin);
handle('getOrigin', () => runtimeSettings.origin);
/**
* Sets the origin to be used in the content security policy and triggers
* a main window reload.
*/
handle('setOrigin', async (requestOrigin) => {
await origin.validateOrigin(requestOrigin);
origin.origin = requestOrigin;
const origin = sanitizer.urlValidate(requestOrigin);
await runtimeSettings.validateOrigin(origin);
runtimeSettings.origin = origin;
});
/**
* Resets the origin.
*/
handle('resetOrigin', async () => origin.resetOrigin());
handle('resetOrigin', async () => runtimeSettings.resetOrigin());
/**
* Opens the specified URL in an external browser. Only secure HTTPs URLs are
@@ -55,7 +57,7 @@ handle('cliExists', () => boundaryCli.exists());
* Establishes a boundary session and returns session details.
*/
handle('connect', ({ target_id, token, host_id }) =>
sessionManager.start(origin.origin, target_id, token, host_id)
sessionManager.start(runtimeSettings.origin, target_id, token, host_id)
);
/**
@@ -1,34 +1,7 @@
const { net } = require('electron');
const requestTimeoutSeconds = 10;
// Simple promise wrapper around Electron's net.request feature.
// https://www.electronjs.org/docs/api/client-request
const netRequestPromise = (url) =>
new Promise((resolve, reject) => {
try {
const request = net.request(url);
// We don't worry about canceling the request timeout timer once a
// request ends because: request.abort() has no effect after a request
// ends and the promise will already have been completed, so a rejection
// has no effect either.
const requestTimeout = setTimeout(() => {
request.abort();
reject(new Error('Request timeout'));
}, requestTimeoutSeconds * 1000);
request.on('error', reject);
request.on('response', resolve);
request.end();
} catch (e) {
reject(e);
}
});
// Provides a singleton class instance to enable a consistent view of
// runtime settings across the application.
const netRequestPromise = require('../helpers/net-request-promise.js');
// Runtime settings are any configuration, especially user-specified,
// set up at runtime. For example, the Boundary origin is a runtime setting.
class RuntimeSettings {
// Internal private origin is exposed via getter/setter below.
#origin = undefined;
@@ -84,6 +57,4 @@ class RuntimeSettings {
}
}
const runtimeSettings = new RuntimeSettings();
module.exports = runtimeSettings;
module.exports = RuntimeSettings;
@@ -1,5 +1,5 @@
const { spawnAsyncJSONPromise } = require('../helpers/spawn-promise.js');
const base62 = require('../utils/base62.js');
const sanitizer = require('../utils/sanitizer.js');
class Session {
#id;
@@ -77,9 +77,9 @@ class Session {
*/
cliCommand() {
const sanitized = {
target_id: base62.escapeAndValidate(this.#targetId),
token: base62.escapeAndValidate(this.#token),
addr: encodeURI(this.#addr),
target_id: sanitizer.base62EscapeAndValidate(this.#targetId),
token: sanitizer.base62EscapeAndValidate(this.#token),
addr: sanitizer.urlValidate(this.#addr),
};
const command = [
@@ -91,7 +91,7 @@ class Session {
];
if (this.#hostId) {
sanitized.host_id = base62.escapeAndValidate(this.#hostId);
sanitized.host_id = sanitizer.base62EscapeAndValidate(this.#hostId);
command.push(`-host-id=${sanitized.host_id}`);
}
return command;
@@ -0,0 +1,8 @@
const RuntimeSettings = require('../models/runtime-settings.js');
// Provides a singleton class instance to enable a consistent view of
// runtime settings across the application.
const runtimeSettings = new RuntimeSettings();
module.exports = runtimeSettings;
@@ -1,14 +0,0 @@
module.exports = {
/**
* Super paranoid shell quote/escape and validation. Input must be base62.
* @param {string} str
*/
escapeAndValidate: (str) => {
const candidate = str.toString();
if (candidate.match(/^[A-Za-z0-9_]*$/)) return candidate;
throw new Error(`
Could not invoke command:
input contained unsafe characters.
`);
},
};
@@ -0,0 +1,30 @@
const { URL } = require('url');
module.exports = {
/**
* Super paranoid shell quote/escape and validation. Input must be base62.
* @param {string} str
*/
base62EscapeAndValidate: (str) => {
const candidate = str.toString();
if (candidate.match(/^[A-Za-z0-9_]*$/)) return candidate;
throw new Error(`
Could not invoke command:
input contained unsafe characters.
`);
},
/**
* Return a quoted string suitable for using in shell commands.
* @param {string} str
* @returns {string}
*/
urlValidate: (str) => {
try {
const url = new URL(str);
return `${url.protocol}://${url.host}`;
} catch (e) {
// Parse error
throw new Error(`URL ${str} could not be validated.`);
}
},
};