@endo/compartment-mapper
Advanced tools
Comparing version 0.9.2 to 1.0.0
{ | ||
"name": "@endo/compartment-mapper", | ||
"version": "0.9.2", | ||
"version": "1.0.0", | ||
"description": "The compartment mapper assembles Node applications in a sandbox", | ||
@@ -25,3 +25,6 @@ "keywords": [ | ||
"exports": { | ||
".": "./index.js", | ||
".": { | ||
"types": "./types.d.ts", | ||
"default": "./index.js" | ||
}, | ||
"./import.js": "./import.js", | ||
@@ -36,4 +39,4 @@ "./archive.js": "./archive.js", | ||
"build": "exit 0", | ||
"prepack": "tsc --build jsconfig.build.json", | ||
"postpack": "git clean -f '*.d.ts*'", | ||
"build:types": "tsc --build tsconfig.build.json", | ||
"clean:types": "git clean -f '*.d.ts*'", | ||
"cover": "c8 ava", | ||
@@ -43,3 +46,3 @@ "lint": "yarn lint:types && yarn lint:js", | ||
"lint:js": "eslint .", | ||
"lint:types": "tsc -p jsconfig.json", | ||
"lint:types": "tsc", | ||
"prettier-fixtures": "prettier --write --with-node-modules './test/fixtures-*/**/*.*js'", | ||
@@ -49,6 +52,6 @@ "test": "ava" | ||
"dependencies": { | ||
"@endo/cjs-module-analyzer": "^0.2.35", | ||
"@endo/static-module-record": "^0.8.2", | ||
"@endo/zip": "^0.2.35", | ||
"ses": "^0.18.8" | ||
"@endo/cjs-module-analyzer": "^1.0.0", | ||
"@endo/static-module-record": "^1.0.0", | ||
"@endo/zip": "^1.0.0", | ||
"ses": "^1.0.0" | ||
}, | ||
@@ -63,3 +66,3 @@ "devDependencies": { | ||
"eslint-plugin-eslint-comments": "^3.1.2", | ||
"eslint-plugin-import": "^2.27.5", | ||
"eslint-plugin-import": "^2.29.0", | ||
"prettier": "^3.0.0", | ||
@@ -101,3 +104,6 @@ "typescript": "~5.2.2" | ||
}, | ||
"gitHead": "9c779d317c4b02133172dbe142c5b2d1727efc49" | ||
"typeCoverage": { | ||
"atLeast": 85.78 | ||
}, | ||
"gitHead": "6aa22009bf8128575c446aebceb0f9a01459d165" | ||
} |
@@ -0,1 +1,2 @@ | ||
// @ts-nocheck | ||
/** quotes strings */ | ||
@@ -8,3 +9,5 @@ const q = JSON.stringify; | ||
exportName => `\ | ||
${exportName}: cell(${q(exportName)}), | ||
${q(exportName)}: cell(${q(exportName)}${ | ||
exportName !== 'default' ? '' : `, {}` | ||
}), | ||
`, | ||
@@ -18,4 +21,5 @@ ), | ||
return ({ imports = {} }) => { | ||
const moduleCells = cells[num]; | ||
const cModule = Object.freeze( | ||
Object.defineProperty({}, 'exports', cells[num].default), | ||
Object.defineProperty({}, 'exports', moduleCells.default), | ||
); | ||
@@ -25,5 +29,17 @@ // TODO: specifier not found handling | ||
functors[num](Object.freeze(requireImpl), cModule.exports, cModule); | ||
Object.keys(cells[num]) | ||
// Update all named cells from module.exports. | ||
Object.keys(moduleCells) | ||
.filter(k => k !== 'default' && k !== '*') | ||
.map(k => cells[num][k].set(cModule.exports[k])); | ||
.map(k => moduleCells[k].set(cModule.exports[k])); | ||
// Add new named cells from module.exports. | ||
Object.keys(cModule.exports) | ||
.filter(k => k !== 'default' && k !== '*') | ||
.filter(k => moduleCells[k] === undefined) | ||
.map(k => (moduleCells[k] = cell(k, cModule.exports[k]))); | ||
// Update the star cell from all cells. | ||
const starExports = Object.create(null); | ||
Object.keys(moduleCells) | ||
.filter(k => k !== '*') | ||
.map(k => Object.defineProperty(starExports, k, moduleCells[k])); | ||
moduleCells['*'].set(Object.freeze(starExports)); | ||
}; | ||
@@ -30,0 +46,0 @@ /* eslint-enable no-undef */ |
@@ -98,2 +98,5 @@ // @ts-check | ||
exit: true, | ||
errorHint: `Blocked in loading. ${q( | ||
moduleSpecifier, | ||
)} was not in the archive and an attempt was made to load it as a builtin`, | ||
}); | ||
@@ -100,0 +103,0 @@ const record = await exitModuleImportHook(moduleSpecifier); |
@@ -223,2 +223,5 @@ // @ts-check | ||
exit: true, | ||
errorHint: `Blocked in loading. ${q( | ||
moduleSpecifier, | ||
)} was not in the compartment map and an attempt was made to load it as a builtin`, | ||
}); | ||
@@ -255,6 +258,4 @@ if (archiveOnly) { | ||
const candidates = [moduleSpecifier]; | ||
if (moduleSpecifier !== '.') { | ||
for (const candidateSuffix of searchSuffixes) { | ||
candidates.push(`${moduleSpecifier}${candidateSuffix}`); | ||
} | ||
for (const candidateSuffix of searchSuffixes) { | ||
candidates.push(`${moduleSpecifier}${candidateSuffix}`); | ||
} | ||
@@ -261,0 +262,0 @@ |
@@ -21,3 +21,3 @@ export function mapParsers(languageForExtension: Record<string, Language>, languageForModuleSpecifier: Record<string, string>, parserForLanguage: Record<string, ParserImplementation>, moduleTransforms?: ModuleTransforms): ParseFn; | ||
export type LinkOptions = import('./types.js').LinkOptions; | ||
export type ERef<T_1> = import('@endo/eventual-send').ERef<T>; | ||
export type ERef<T_1> = any; | ||
//# sourceMappingURL=link.d.ts.map |
@@ -22,3 +22,2 @@ // @ts-check | ||
ATTENUATORS_COMPARTMENT, | ||
diagnoseMissingCompartmentError, | ||
attenuateGlobals, | ||
@@ -236,6 +235,10 @@ makeDeferredAttenuatorsProvider, | ||
if (foreignModuleSpecifier !== undefined) { | ||
// archive goes through foreignModuleSpecifier for local modules too | ||
if (!moduleSpecifier.startsWith('./')) { | ||
// archive goes through foreignModuleSpecifier for local modules too | ||
// This code path seems to only be reached on subsequent imports of the same specifier in the same compartment. | ||
// The check should be redundant and is only left here out of abundance of caution. | ||
enforceModulePolicy(moduleSpecifier, compartmentDescriptor, { | ||
exit: false, | ||
errorHint: | ||
'This check should not be reachable. If you see this error, please file an issue.', | ||
}); | ||
@@ -249,8 +252,3 @@ } | ||
foreignCompartmentName, | ||
)}${diagnoseMissingCompartmentError({ | ||
moduleSpecifier, | ||
compartmentDescriptor, | ||
foreignModuleSpecifier, | ||
foreignCompartmentName, | ||
})}`, | ||
)}}`, | ||
); | ||
@@ -285,16 +283,13 @@ } | ||
foreignCompartmentName, | ||
)}${diagnoseMissingCompartmentError({ | ||
moduleSpecifier, | ||
compartmentDescriptor, | ||
foreignModuleSpecifier, | ||
foreignCompartmentName, | ||
})}`, | ||
)}`, | ||
); | ||
} | ||
// Despite all non-exit modules not allowed by policy being dropped | ||
// while building the graph, this check is necessary because module | ||
// is written back to the compartment map below. | ||
enforceModulePolicy(scopePrefix, compartmentDescriptor, { | ||
exit: false, | ||
errorHint: `Blocked in linking. ${q( | ||
moduleSpecifier, | ||
)} is part of the compartment map and resolves to ${q( | ||
foreignCompartmentName, | ||
)}.`, | ||
}); | ||
@@ -301,0 +296,0 @@ // The following line is weird. |
@@ -569,3 +569,3 @@ // @ts-check | ||
* for selecting relevant exports, e.g., "browser" or "node". | ||
* @param {object} policy | ||
* @param {object|undefined} policy | ||
* @returns {CompartmentMapDescriptor} | ||
@@ -615,7 +615,2 @@ */ | ||
); | ||
// do not include compartments for packages not covered by policy | ||
if (policy && !packagePolicy) { | ||
// eslint-disable-next-line no-continue | ||
continue; | ||
} | ||
@@ -622,0 +617,0 @@ /** |
@@ -135,2 +135,7 @@ // @ts-check | ||
const require = (/** @type {string} */ importSpecifier) => { | ||
if (!has(resolvedImports, importSpecifier)) { | ||
throw new Error( | ||
`Cannot find module "${importSpecifier}" in "${location}"`, | ||
); | ||
} | ||
const namespace = compartment.importNow(resolvedImports[importSpecifier]); | ||
@@ -137,0 +142,0 @@ // If you read this file carefully, you'll see it's not possible for a cjs module to not have the default anymore. |
@@ -46,3 +46,3 @@ // @ts-check | ||
* | ||
* @param {*} policyValue | ||
* @param {any} policyValue | ||
* @returns {boolean} | ||
@@ -126,3 +126,3 @@ */ | ||
allegedPackagePolicy === packagePolicy && !isArray(allegedPackagePolicy), | ||
`${path} must be an object, got ${allegedPackagePolicy}${inUrl}`, | ||
`${path} must be an object, got ${q(allegedPackagePolicy)}${inUrl}`, | ||
); | ||
@@ -129,0 +129,0 @@ const { |
@@ -10,10 +10,4 @@ /** | ||
export function attenuateGlobals(globalThis: object, globals: object, packagePolicy: object, attenuators: DeferredAttenuatorsProvider, pendingJobs: Array<Promise<any>>, name?: string): void; | ||
export function enforceModulePolicy(specifier: string, compartmentDescriptor: object, info?: object): void; | ||
export function enforceModulePolicy(specifier: string, compartmentDescriptor: import('./types.js').CompartmentDescriptor, info?: object): void; | ||
export function attenuateModuleHook(specifier: string, originalModuleRecord: ThirdPartyStaticModuleInterface, policy: object, attenuators: DeferredAttenuatorsProvider): Promise<ThirdPartyStaticModuleInterface>; | ||
export function diagnoseMissingCompartmentError({ moduleSpecifier, compartmentDescriptor, foreignModuleSpecifier, foreignCompartmentName, }: { | ||
moduleSpecifier: string; | ||
compartmentDescriptor: object; | ||
foreignModuleSpecifier: string; | ||
foreignCompartmentName: string; | ||
}): string; | ||
export type PackageNamingKit = import('./types.js').PackageNamingKit; | ||
@@ -20,0 +14,0 @@ export type AttenuationDefinition = import('./types.js').AttenuationDefinition; |
@@ -20,3 +20,3 @@ // @ts-check | ||
const { entries, values, assign, keys, freeze } = Object; | ||
const { create, entries, values, assign, keys, freeze } = Object; | ||
const q = JSON.stringify; | ||
@@ -105,3 +105,3 @@ | ||
* @param {PackageNamingKit} namingKit | ||
* @param {*} packagePolicy | ||
* @param {any} packagePolicy | ||
* @returns {boolean} | ||
@@ -118,25 +118,2 @@ */ | ||
const validateDependencies = (policy, canonicalName) => { | ||
const packages = policy.resources[canonicalName].packages; | ||
if (!packages || isAllowingEverything(packages)) { | ||
return; | ||
} | ||
const packageNames = keys(packages); | ||
const attenuators = detectAttenuators(policy); | ||
// Join attenuators with packageNames into a Set to deduplicate and check if all are listed in policy.resources | ||
const allSpecifiers = new Set([...packageNames, ...attenuators]); | ||
for (const specifier of allSpecifiers) { | ||
if (!(specifier in policy.resources)) { | ||
throw Error( | ||
`Package ${q(specifier)} is allowed for ${q( | ||
canonicalName, | ||
)} to import but its policy is not defined. Please add a policy for ${q( | ||
specifier, | ||
)}`, | ||
); | ||
} | ||
} | ||
}; | ||
/** | ||
@@ -160,16 +137,10 @@ * Returns the policy applicable to the canonicalName of the package | ||
defaultAttenuator: policy.defaultAttenuator, | ||
packages: detectAttenuators(policy).reduce((packages, specifier) => { | ||
packages[specifier] = true; | ||
return packages; | ||
}, {}), | ||
packages: 'any', | ||
}; | ||
} | ||
if (policy.resources && policy.resources[canonicalName]) { | ||
validateDependencies(policy, canonicalName); | ||
if (policy.resources && policy.resources[canonicalName] !== undefined) { | ||
return policy.resources[canonicalName]; | ||
} else { | ||
console.warn( | ||
`No policy for '${canonicalName}', omitting from compartment map.`, | ||
); | ||
return undefined; | ||
// Allow skipping policy entries for packages with no powers. | ||
return create(null); | ||
} | ||
@@ -364,2 +335,8 @@ }; | ||
const diagnoseModulePolicy = errorHint => { | ||
if (!errorHint) { | ||
return ''; | ||
} | ||
return ` (info: ${errorHint})`; | ||
}; | ||
/** | ||
@@ -369,7 +346,11 @@ * Throws if importing of the specifier is not allowed by the policy | ||
* @param {string} specifier | ||
* @param {object} compartmentDescriptor | ||
* @param {import('./types.js').CompartmentDescriptor} compartmentDescriptor | ||
* @param {object} [info] | ||
*/ | ||
export const enforceModulePolicy = (specifier, compartmentDescriptor, info) => { | ||
const { policy, modules } = compartmentDescriptor; | ||
export const enforceModulePolicy = ( | ||
specifier, | ||
compartmentDescriptor, | ||
info = {}, | ||
) => { | ||
const { policy, modules, label } = compartmentDescriptor; | ||
if (!policy) { | ||
@@ -382,5 +363,7 @@ return; | ||
throw Error( | ||
`Importing ${q(specifier)} was not allowed by policy packages:${q( | ||
`Importing ${q(specifier)} in ${q( | ||
label, | ||
)} was not allowed by packages policy ${q( | ||
policy.packages, | ||
)}`, | ||
)}${diagnoseModulePolicy(info.errorHint)}`, | ||
); | ||
@@ -395,3 +378,3 @@ } | ||
policy.builtins, | ||
)}`, | ||
)}${diagnoseModulePolicy(info.errorHint)}`, | ||
); | ||
@@ -470,59 +453,1 @@ } | ||
}; | ||
const padDiagnosis = text => ` (${text})`; | ||
/** | ||
* Provide dignostic information for a missing compartment error | ||
* | ||
* @param {object} args | ||
* @param {string} args.moduleSpecifier | ||
* @param {object} args.compartmentDescriptor | ||
* @param {string} args.foreignModuleSpecifier | ||
* @param {string} args.foreignCompartmentName | ||
* @returns {string} | ||
*/ | ||
export const diagnoseMissingCompartmentError = ({ | ||
moduleSpecifier, | ||
compartmentDescriptor, | ||
foreignModuleSpecifier, | ||
foreignCompartmentName, | ||
}) => { | ||
const { policy, name, scopes } = compartmentDescriptor; | ||
if (policy) { | ||
if (!policy.packages) { | ||
return padDiagnosis( | ||
`There were no allowed packages specified in policy for ${q(name)}`, | ||
); | ||
} | ||
if (name === ATTENUATORS_COMPARTMENT) { | ||
return padDiagnosis( | ||
`Attenuator ${q( | ||
moduleSpecifier, | ||
)} was imported but there is no policy resources entry defined for it.`, | ||
); | ||
} | ||
const scopeNames = entries(scopes) | ||
.filter(([_name, scope]) => scope.compartment === foreignCompartmentName) | ||
.map(([scopeName]) => scopeName); | ||
if (scopeNames.length === 1 && scopeNames[0] === moduleSpecifier) { | ||
return padDiagnosis( | ||
`Package ${q( | ||
moduleSpecifier, | ||
)} is missing. Are you sure there is an entry in policy resources specified for it?`, | ||
); | ||
} else { | ||
return padDiagnosis( | ||
`Package ${q(moduleSpecifier)} resolves to ${q( | ||
foreignModuleSpecifier, | ||
)} in ${q( | ||
foreignCompartmentName, | ||
)} which seems disallowed by policy. There is likely an override defined that causes another package to be imported as ${q( | ||
moduleSpecifier, | ||
)}.`, | ||
); | ||
} | ||
} | ||
// Omit diagnostics when parent package had no policy - it means there was no policy. | ||
return ''; | ||
}; |
@@ -110,3 +110,3 @@ export type FinalStaticModuleType = import('ses').FinalStaticModuleType; | ||
export type CanonicalFn = (location: string) => Promise<string>; | ||
export type HashFn = (bytes: Uint8Array) => string; | ||
export type HashFn = (bytes: string | Uint8Array) => string; | ||
export type Application = { | ||
@@ -113,0 +113,0 @@ import: ExecuteFn; |
@@ -136,3 +136,3 @@ // @ts-check | ||
* @callback HashFn | ||
* @param {Uint8Array} bytes | ||
* @param {string | Uint8Array} bytes | ||
* @returns {string} hash | ||
@@ -139,0 +139,0 @@ */ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
273347
6396
+ Added@endo/cjs-module-analyzer@1.0.9(transitive)
+ Added@endo/env-options@1.1.8(transitive)
+ Added@endo/static-module-record@1.1.2(transitive)
+ Added@endo/zip@1.0.9(transitive)
+ Addedses@1.10.0(transitive)
- Removed@endo/cjs-module-analyzer@0.2.35(transitive)
- Removed@endo/env-options@0.1.4(transitive)
- Removed@endo/static-module-record@0.8.2(transitive)
- Removed@endo/zip@0.2.35(transitive)
- Removedses@0.18.8(transitive)
Updated@endo/zip@^1.0.0
Updatedses@^1.0.0