From de0ea45c2def6447f31098faa1495aac92638c1b Mon Sep 17 00:00:00 2001 From: Kris Kowal Date: Tue, 11 Aug 2020 17:26:16 -0700 Subject: [PATCH] feat(endo): Support exec shell environment --- packages/endo/bin/endo.js | 9 +++++---- packages/endo/mitm/.gitignore | 1 + packages/endo/package.json | 3 ++- packages/endo/src/cli.js | 20 ++++++++++++++++---- packages/endo/src/lockdown.cjs | 17 +++++++++++++++++ packages/endo/src/lockdown.js | 2 ++ packages/endo/src/postinstall.js | 17 +++++++++++++++++ 7 files changed, 60 insertions(+), 9 deletions(-) create mode 100644 packages/endo/mitm/.gitignore create mode 100644 packages/endo/src/lockdown.cjs create mode 100644 packages/endo/src/postinstall.js diff --git a/packages/endo/bin/endo.js b/packages/endo/bin/endo.js index da980e130d..48af66f5a7 100755 --- a/packages/endo/bin/endo.js +++ b/packages/endo/bin/endo.js @@ -1,5 +1,6 @@ #!/usr/bin/env node -import fs from "fs"; -import { main } from "../src/cli.js"; - -main(process, { fs: fs.promises }); +(async () => { + const fs = await import("fs"); + const { main } = await import("../src/cli.js"); + main(process, { fs: fs.promises }); +})(); diff --git a/packages/endo/mitm/.gitignore b/packages/endo/mitm/.gitignore new file mode 100644 index 0000000000..64f5a0a681 --- /dev/null +++ b/packages/endo/mitm/.gitignore @@ -0,0 +1 @@ +node diff --git a/packages/endo/package.json b/packages/endo/package.json index f40889199d..06aab6ea2f 100644 --- a/packages/endo/package.json +++ b/packages/endo/package.json @@ -15,7 +15,7 @@ "browser": "./dist/endo.umd.js" }, "bin": { - "endo": "./bin/endo" + "endo": "./bin/endo.js" }, "scripts": { "build": "rollup --config rollup.config.js", @@ -23,6 +23,7 @@ "depcheck": "depcheck", "lint": "eslint '**/*.js'", "lint-fix": "eslint --fix '**/*.js'", + "postinstall": "node src/postinstall.js", "prepublish": "yarn clean && yarn build", "test": "yarn build && tap --no-esm --no-coverage --reporter spec 'test/**/*.test.js'" }, diff --git a/packages/endo/src/cli.js b/packages/endo/src/cli.js index 868546f3a2..014f5bed57 100644 --- a/packages/endo/src/cli.js +++ b/packages/endo/src/cli.js @@ -1,9 +1,12 @@ /* eslint no-shadow: [0] */ import "./lockdown.js"; +import subprocess from "child_process"; import { writeArchive } from "./main.js"; import { search } from "./search.js"; import { compartmentMapForNodeModules } from "./compartmap.js"; +const mitmPath = new URL("../mitm", import.meta.url).pathname; + function usage(message) { console.error(message); return 1; @@ -36,7 +39,7 @@ async function parameter(args, handle, usage) { return handle(arg, rest); } -async function run(args, { cwd, read, write, stdout }) { +async function run(args, { cwd, read, write, stdout, env }) { async function compartmap(args) { async function handleEntry(applicationPath, args) { if (args.length) { @@ -72,12 +75,20 @@ async function run(args, { cwd, read, write, stdout }) { return parameter(args, handleArchive, noArchiveUsage); } - return subcommand(args, { compartmap, archive }); + async function exec([arg, ...args]) { + const child = subprocess.spawn(arg, args, { + env: { ...env, PATH: `${mitmPath}:${env.PATH}` }, + stdio: "inherit" + }); + return new Promise(resolve => child.on("exit", resolve)); + } + + return subcommand(args, { compartmap, archive, exec }); } export async function main(process, modules) { const { fs } = modules; - const { cwd, stdout } = process; + const { cwd, stdout, env } = process; // Filesystem errors often don't have stacks: @@ -102,7 +113,8 @@ export async function main(process, modules) { read, write, cwd, - stdout + stdout, + env }); } catch (error) { process.exitCode = usage(error.stack || error.message); diff --git a/packages/endo/src/lockdown.cjs b/packages/endo/src/lockdown.cjs new file mode 100644 index 0000000000..e63084603c --- /dev/null +++ b/packages/endo/src/lockdown.cjs @@ -0,0 +1,17 @@ +/* global lockdown */ + +// This is a CommonJS module, to be used like `node -r endo/src/lockdown.cjs`. +// The `endo exec` command stages a man-in-the-middle `node` shell script that +// in turn injects this SES lockdown parameter in all Node.js commands in the +// resulting shell environment. +// The taming behavior may be overridden with environment variables +// like `ERROR_TAMING=unsafe endo exec node ...` + +require("ses"); +lockdown({ + dateTaming: process.env.ENDO_DATE_TAMING || 'safe', + errorTaming: process.env.ENDO_ERROR_TAMING || 'safe', + mathTaming: process.env.ENDO_MATH_TAMING || 'safe', + regExpTaming: process.env.ENDO_REGEXP_TAMING || 'safe', + localeTaming: process.env.ENDO_LOCAlE_TAMING || 'safe', +}); diff --git a/packages/endo/src/lockdown.js b/packages/endo/src/lockdown.js index 7095b6e19a..cb1acb85b9 100644 --- a/packages/endo/src/lockdown.js +++ b/packages/endo/src/lockdown.js @@ -1,6 +1,8 @@ /* global lockdown */ import "ses"; +// This is used by Endo to lockdown its own start compartment. + lockdown({ errorTaming: "unsafe" }); diff --git a/packages/endo/src/postinstall.js b/packages/endo/src/postinstall.js new file mode 100644 index 0000000000..f1c6c38722 --- /dev/null +++ b/packages/endo/src/postinstall.js @@ -0,0 +1,17 @@ +// This postinstall hook creates mitm/node. +// This in turn interposes SES lockdown for all descendent processes, provided +// that the mitm directory is appears before Node.js's bin directory on the +// environment PATH. + +import fs from "fs"; + +const node = process.argv[0]; +const lockdown = new URL("../src/lockdown.cjs", import.meta.url).pathname; +const mitm = new URL("../mitm/node", import.meta.url).pathname; + +const script = `#!/bin/bash +set -ueo pipefail +${node} -r ${lockdown} "$@"`; + +fs.writeFileSync(mitm, script, "utf-8"); +fs.chmodSync(mitm, 0o755);