Skip to content

Commit

Permalink
feat(endomoat): add policy generation [ci skip]
Browse files Browse the repository at this point in the history
This adds policy generation for Endomoat.

It does not yet have support for writable globals.
  • Loading branch information
boneskull committed Apr 29, 2024
1 parent c087da6 commit d5ade78
Show file tree
Hide file tree
Showing 28 changed files with 2,523 additions and 28 deletions.
13 changes: 2 additions & 11 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions packages/endomoat/package.json
Expand Up @@ -42,6 +42,7 @@
"@endo/compartment-mapper": "1.1.4",
"@endo/evasive-transform": "1.0.4",
"@types/node": "18.19.28",
"json-stable-stringify": "1.1.1",
"lavamoat-core": "^15.3.0",
"ses": "1.4.1",
"type-fest": "4.14.0",
Expand All @@ -50,6 +51,7 @@
"devDependencies": {
"@endo/eslint-plugin": "2.1.0",
"@jessie.js/eslint-plugin": "0.4.0",
"@types/json-stable-stringify": "1.0.36",
"memfs": "4.8.1"
},
"publishConfig": {
Expand Down
73 changes: 71 additions & 2 deletions packages/endomoat/src/cli.js
Expand Up @@ -17,9 +17,16 @@ import assert from 'node:assert'
import path from 'node:path'
import yargs from 'yargs'
import { hideBin } from 'yargs/helpers'
import { constants, loadPolicies, run } from './index.js'
import {
constants,
generateAndWritePolicy,
loadPolicies,
run,
} from './index.js'
import { readJsonFile } from './util.js'

const BEHAVIOR_GROUP = 'Behavior Options:'

/**
* Main entry point to CLI
*/
Expand Down Expand Up @@ -169,8 +176,70 @@ async function main(args = hideBin(process.argv)) {
await run(argv.entrypoint, policy)
}
)
.demandCommand(1)
.command(
['gen <entrypoint>', 'generate <entrypoint>'],
'Generate policy files; overwrites existing policies',
(yargs) =>
yargs
.options({
run: {
describe: 'Run the application after policy generated',
type: 'boolean',
group: BEHAVIOR_GROUP,
},
debug: {
type: 'boolean',
describe: 'Additionally write a debug policy',
group: BEHAVIOR_GROUP,
},
})
.positional('entrypoint', {
describe: 'Path to the application entry point',
type: 'string',
normalize: true,
coerce: path.resolve,
})
.demandOption('entrypoint')
/**
* Resolve entrypoint from `cwd`
*/
.middleware((argv) => {
argv.entrypoint = path.resolve(argv.cwd, argv.entrypoint)
}, true)
/**
* This should not fail. If it does, there is a bug.
*/
.check((argv) => {
assert(
path.isAbsolute(argv.entrypoint),
'entrypoint must be an absolute path'
)
return true
}),
async ({
entrypoint,
debug,
run: shouldRun,
policy: policyPath,
'policy-debug': policyDebugPath,
}) => {
const policy = await generateAndWritePolicy(entrypoint, {
debug,
policyPath,
policyDebugPath,
})

if (debug) {
console.error(`Wrote debug policy to ${policyDebugPath}`)
}
console.error(`Wrote policy to ${policyPath}`)

if (shouldRun) {
await run(entrypoint, policy)
}
}
)
.demandCommand(1)
.parse()
}

Expand Down
11 changes: 10 additions & 1 deletion packages/endomoat/src/constants.js
Expand Up @@ -3,7 +3,6 @@
*
* _Type a string more than once? Make it a constant!_
*/

import path from 'node:path'

/**
Expand Down Expand Up @@ -56,3 +55,13 @@ export const RSRC_POLICY_BUILTINS = 'builtins'
* Name of the `globals` property of a `LavaMoatPackagePolicy`
*/
export const RSRC_POLICY_GLOBALS = 'globals'

/**
* `builtin` module type for a `LavamoatModuleRecord`
*/
export const LMR_TYPE_BUILTIN = 'builtin'

/**
* `js` module type for a `LavamoatModuleRecord`
*/
export const LMR_TYPE_SOURCE = 'js'
48 changes: 43 additions & 5 deletions packages/endomoat/src/index.js
Expand Up @@ -20,23 +20,61 @@ import { pathToFileURL } from 'node:url'
import { importHook } from './import-hook.js'
import { moduleTransforms } from './module-transforms.js'
import { toEndoPolicy } from './policy-converter.js'
import { defaultReadPowers } from './power.js'
import { generatePolicy } from './policy-gen/index.js'
import { isPolicy } from './policy.js'
import { makeReadPowers } from './power.js'

export * as constants from './constants.js'
export { generateAndWritePolicy, generatePolicy } from './policy-gen/index.js'
export { loadPolicies } from './policy.js'
export { toEndoPolicy }

/**
* Runs a program in Endomoat with the provided policy
* Runs Endomoat with provided policy
*
* @param {string | URL} entrypointPath
* @overload
* @param {string | URL} entryFile
* @param {import('lavamoat-core').LavaMoatPolicy} policy
* @param {import('./types.js').RunOptions} [opts]
* @returns {Promise<unknown>}
*/
export async function run(entrypointPath, policy, opts = {}) {

/**
* Runs Endomoat with an auto-generated policy, optionally writing to disk
*
* @overload
* @param {string | URL} entryFile
* @param {import('./types.js').GenerateAndRunOptions} [opts]
* @returns {Promise<unknown>}
*/

/**
* Runs a program in Endomoat
*
* @param {string | URL} entrypointPath
* @param {import('lavamoat-core').LavaMoatPolicy
* | import('./types.js').GenerateAndRunOptions} [policyOrOpts]
* @param {import('./types.js').RunOptions} [opts]
* @returns {Promise<unknown>}
*/
export async function run(entrypointPath, policyOrOpts = {}, opts = {}) {
await Promise.resolve()
/** @type {import('lavamoat-core').LavaMoatPolicy} */
let policy
/** @type {import('./types.js').RunOptions} */
let runOpts

if (isPolicy(policyOrOpts)) {
policy = policyOrOpts
runOpts = opts
} else {
const generateOpts = policyOrOpts
runOpts = { readPowers: generateOpts.readPowers }
policy = await generatePolicy(entrypointPath, generateOpts)
}

const endoPolicy = toEndoPolicy(policy)
const { readPowers = defaultReadPowers } = opts
const readPowers = makeReadPowers(runOpts.readPowers)

const url =
entrypointPath instanceof URL
Expand Down

0 comments on commit d5ade78

Please sign in to comment.