diff --git a/packages/core/src/schema/lavamoat-policy.v0-0-1.schema.ts b/packages/core/src/schema/lavamoat-policy.v0-0-1.schema.ts index f58c36f35c..3e01718294 100644 --- a/packages/core/src/schema/lavamoat-policy.v0-0-1.schema.ts +++ b/packages/core/src/schema/lavamoat-policy.v0-0-1.schema.ts @@ -107,7 +107,7 @@ export interface BuiltinPolicy { * `true` to allow and `false` to deny */ export interface PackagePolicy { - [k: string]: boolean + [k: string]: PackagePolicyValue } /** * Custom run-time module resolutions by direct dependency @@ -123,3 +123,7 @@ export interface Resolutions { [k: string]: string } } + +export type PackagePolicyValue = DynamicPkgPolicy | boolean + +export type DynamicPkgPolicy = 'dynamic' diff --git a/packages/endomoat/src/constants.js b/packages/endomoat/src/constants.js index 0a6fde9da1..4d9d475939 100644 --- a/packages/endomoat/src/constants.js +++ b/packages/endomoat/src/constants.js @@ -36,11 +36,15 @@ export const POLICY_ITEM_WRITE = 'write' */ export const POLICY_ITEM_WILDCARD = 'any' +export const POLICY_ITEM_DYNAMIC = 'dynamic' + /** * Designator for the root policy item in a LavaMoat policy */ export const LAVAMOAT_PKG_POLICY_ROOT = '$root$' +export const LAVAMOAT_PKG_POLICY_VALUE_DYNAMIC = 'dynamic' + /** * Name of the `packages` property of a `LavaMoatPackagePolicy` */ diff --git a/packages/endomoat/src/policy-converter.js b/packages/endomoat/src/policy-converter.js index ea5a28db7d..6f7880f18a 100644 --- a/packages/endomoat/src/policy-converter.js +++ b/packages/endomoat/src/policy-converter.js @@ -2,6 +2,7 @@ import { mergePolicy } from 'lavamoat-core' import { LAVAMOAT_PKG_POLICY_ROOT, LAVAMOAT_PKG_POLICY_VALUE_DYNAMIC, + POLICY_ITEM_DYNAMIC, POLICY_ITEM_ROOT, POLICY_ITEM_WILDCARD, RSRC_POLICY_BUILTINS, @@ -43,6 +44,11 @@ function toEndoRsrcPkgsPolicyBuiltins(item) { 'Expected a FullAttenuationDefinition; got a boolean' ) } + if (itemForBuiltin === 'dynamic') { + throw new TypeError( + 'Expected a FullAttenuationDefinition; got "dynamic"' + ) + } if (isArray(itemForBuiltin)) { throw new TypeError( 'Expected a FullAttenuationDefinition; got an array' @@ -82,7 +88,10 @@ function toEndoRsrcPkgsPolicyPkgs(item) { if (key === LAVAMOAT_PKG_POLICY_ROOT) { throw new TypeError('Unexpected root package policy') } else { - policyItem[key] = value + policyItem[key] = + value === LAVAMOAT_PKG_POLICY_VALUE_DYNAMIC + ? POLICY_ITEM_DYNAMIC + : Boolean(value) } } return policyItem diff --git a/packages/endomoat/test/fixture/dynamic/node_modules/dummy/index.js b/packages/endomoat/test/fixture/dynamic/node_modules/dummy/index.js index a35c1acaf5..b9be0704dd 100644 --- a/packages/endomoat/test/fixture/dynamic/node_modules/dummy/index.js +++ b/packages/endomoat/test/fixture/dynamic/node_modules/dummy/index.js @@ -1 +1 @@ -module.exports = "world" +module.exports = require('muddy') diff --git a/packages/endomoat/test/fixture/dynamic/node_modules/dummy/package.json b/packages/endomoat/test/fixture/dynamic/node_modules/dummy/package.json index 82b3f64c49..dd6d79f1cb 100644 --- a/packages/endomoat/test/fixture/dynamic/node_modules/dummy/package.json +++ b/packages/endomoat/test/fixture/dynamic/node_modules/dummy/package.json @@ -1,4 +1,5 @@ { "name": "dummy", - "version": "0.0.0" + "version": "0.0.0", + "dependencies": {"muddy": "0.0.0"} } diff --git a/packages/endomoat/test/fixture/json/dynamic.json b/packages/endomoat/test/fixture/json/dynamic.json index 92770e05ba..a4f54dc687 100644 --- a/packages/endomoat/test/fixture/json/dynamic.json +++ b/packages/endomoat/test/fixture/json/dynamic.json @@ -2,9 +2,11 @@ "/package.json": "{\n \"name\": \"hello\",\n \"version\": \"0.0.0\",\n \"type\": \"module\",\n \"description\": \"this code actually runs\",\n \"private\": true,\n \"main\": \"index.js\",\n \"scripts\": {\n \"start\": \"npx snapshot-fs . ../json/dynamic.json\"\n },\n \"dependencies\": {\n \"dummy\": \"0.0.0\",\n \"dynamic-require\": \"0.0.0\"\n }\n}\n", "/index.js": "import { hello as otherHello } from 'dynamic-require'\nexport const hello = 'hello ' + otherHello\n", "/README.md": "This fixture is not used directly; it's only here in order to create a snapshot:\n\n```bash\nnpm run start\n```\n", + "/node_modules/muddy/package.json": "{\n \"name\": \"muddy\",\n \"version\": \"0.0.0\"\n}\n", + "/node_modules/muddy/index.js": "module.exports = \"world\"\n", "/node_modules/dynamic-require/world.js": "module.exports = 'world'\n", "/node_modules/dynamic-require/package.json": "{\n \"name\": \"dynamic-require\",\n \"version\": \"1.0.0\",\n \"license\": \"ISC\",\n \"private\": true,\n \"main\": \"index.js\",\n \"dependencies\": {\n\n }\n}\n", "/node_modules/dynamic-require/index.js": "function dynamic (value) {\n return require(value)\n}\n\nexports.hello = dynamic('dummy')\n", - "/node_modules/dummy/package.json": "{\n \"name\": \"dummy\",\n \"version\": \"0.0.0\"\n}\n", - "/node_modules/dummy/index.js": "module.exports = \"world\"\n" + "/node_modules/dummy/package.json": "{\n \"name\": \"dummy\",\n \"version\": \"0.0.0\",\n \"dependencies\": {\"muddy\": \"0.0.0\"}\n}\n", + "/node_modules/dummy/index.js": "module.exports = require('muddy')\n" } diff --git a/packages/endomoat/test/index.spec.js b/packages/endomoat/test/index.spec.js index e6926104b8..e3960a2cd4 100644 --- a/packages/endomoat/test/index.spec.js +++ b/packages/endomoat/test/index.spec.js @@ -20,8 +20,20 @@ test.failing('dynamic imports - run a pure-JS app', async (t) => { ) const entryFile = '/index.js' + // dummy needs to require other things that are not dependencies of anything else + const result = await run(entryFile, { readPowers, + policyOverride: { + resources: { + 'dynamic-require': { + // if we do this we need to recognize it in endo + packages: { + dummy: 'dynamic', + }, + }, + }, + }, }) t.deepEqual({ .../** @type {object} */ (result) }, { hello: 'hello world' }) })