Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(endomoat): add policy generation [ci skip]
This adds policy generation for Endomoat. It does not yet have support for writable globals.
- Loading branch information
Showing
18 changed files
with
1,967 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
/** | ||
* Provides Lavamoat policy generation facilities via {@link generatePolicy} | ||
* | ||
* @packageDocumentation | ||
*/ | ||
|
||
import { | ||
loadCompartmentForArchive, | ||
makeArchiveCompartmentMap, | ||
} from '@endo/compartment-mapper' | ||
import { pathToFileURL } from 'node:url' | ||
import { importHook } from '../import-hook.js' | ||
import { moduleTransforms } from '../module-transforms.js' | ||
import { defaultReadPower } from '../power.js' | ||
import { PolicyGenerator } from './policy-generator.js' | ||
|
||
const { fromEntries, entries } = Object | ||
|
||
/** | ||
* @typedef {import('type-fest').Merge< | ||
* Omit<import('./policy-generator.js').PolicyGeneratorOptions, 'read'>, | ||
* { | ||
* readPowers?: | ||
* | import('@endo/compartment-mapper').ReadFn | ||
* | import('@endo/compartment-mapper').ReadPowers | ||
* } | ||
* >} GeneratePolicyOptions | ||
*/ | ||
|
||
/** | ||
* Generates a LavaMoat policy from a given entry point using | ||
* `@endo/compartment-mapper` | ||
* | ||
* @param {string | URL} entrypointPath | ||
* @param {GeneratePolicyOptions} opts | ||
* @returns {Promise<import('lavamoat-core').LavaMoatPolicy>} | ||
*/ | ||
export async function generatePolicy( | ||
entrypointPath, | ||
{ readPowers = defaultReadPower, debug = false, policyOverride } = {} | ||
) { | ||
const { compartmentMap, sources, renames } = await loadCompartmentMap( | ||
entrypointPath, | ||
{ | ||
readPowers, | ||
} | ||
) | ||
|
||
const read = typeof readPowers === 'function' ? readPowers : readPowers.read | ||
|
||
return await PolicyGenerator.generatePolicy( | ||
compartmentMap, | ||
sources, | ||
renames, | ||
{ debug, read, policyOverride } | ||
) | ||
} | ||
|
||
/** | ||
* Loads compartment map and associated sources. | ||
* | ||
* @param {string | URL} entrypointPath | ||
* @param {{ | ||
* readPowers?: | ||
* | import('@endo/compartment-mapper').ReadFn | ||
* | import('@endo/compartment-mapper').ReadPowers | ||
* }} opts | ||
*/ | ||
export async function loadCompartmentMap( | ||
entrypointPath, | ||
{ readPowers = defaultReadPower } = {} | ||
) { | ||
const url = | ||
entrypointPath instanceof URL | ||
? `${entrypointPath}` | ||
: `${pathToFileURL(entrypointPath)}` | ||
|
||
const { compartmentMap, sources } = await loadCompartmentForArchive({ | ||
readPowers: readPowers, | ||
moduleLocation: url, | ||
importHook, | ||
moduleTransforms, | ||
dev: true, | ||
}) | ||
|
||
const { archiveCompartmentMap, archiveSources, compartmentRenames } = | ||
makeArchiveCompartmentMap(compartmentMap, sources) | ||
|
||
// `compartmentRenames` is a mapping of filepath to compartment name; | ||
// we need the reverse mapping. | ||
const renames = fromEntries( | ||
entries(compartmentRenames).map(([filepath, id]) => [id, filepath]) | ||
) | ||
|
||
return { | ||
compartmentMap: archiveCompartmentMap, | ||
sources: archiveSources, | ||
renames, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
/** | ||
* Provides {@link LMRCache} | ||
* | ||
* @packageDescription | ||
*/ | ||
|
||
import { LavamoatModuleRecord } from 'lavamoat-core' | ||
|
||
/** | ||
* This class represents a transient cache for | ||
* {@link LavamoatModuleRecord LavamoatModuleRecords}. | ||
* | ||
* It's a thin wrapper around a `Map`; it's used to ensure we don't create | ||
* duplicate `LavamoatModuleRecord` objects for the same specifiers. | ||
*/ | ||
export class LMRCache { | ||
/** @type {Map<string, LavamoatModuleRecord>} */ | ||
#cache | ||
constructor() { | ||
this.#cache = new Map() | ||
} | ||
|
||
/** | ||
* Computes the cache key for a given {@link LavamoatModuleRecord} or its | ||
* options. | ||
* | ||
* @param {import('lavamoat-core').LavamoatModuleRecordOptions | ||
* | LavamoatModuleRecord} opts | ||
* @returns {string} | ||
* @todo Determine if this is appropriately unique | ||
*/ | ||
static keyFor({ specifier, file }) { | ||
return `${specifier}:${file}` | ||
} | ||
|
||
/** | ||
* Gets or creates a new {@link LavamoatModuleRecord} for the given options. | ||
* | ||
* @param {import('lavamoat-core').LavamoatModuleRecordOptions} opts | ||
*/ | ||
get(opts) { | ||
const key = LMRCache.keyFor(opts) | ||
if (this.#cache.has(key)) { | ||
return /** @type {LavamoatModuleRecord} */ (this.#cache.get(key)) | ||
} | ||
const record = new LavamoatModuleRecord(opts) | ||
this.#cache.set(key, record) | ||
return record | ||
} | ||
|
||
/** | ||
* Clears the cache | ||
*/ | ||
clear() { | ||
this.#cache.clear() | ||
} | ||
} |
Oops, something went wrong.