Skip to content

Commit

Permalink
feat(compartment-mapper): allow skipping powerless packages in policy…
Browse files Browse the repository at this point in the history
… resources WIP
  • Loading branch information
naugtur committed Nov 23, 2023
1 parent 2cd6a72 commit 071dcb4
Show file tree
Hide file tree
Showing 9 changed files with 65 additions and 248 deletions.
7 changes: 2 additions & 5 deletions packages/compartment-mapper/src/archive.js
Original file line number Diff line number Diff line change
Expand Up @@ -145,16 +145,13 @@ const translateCompartmentMap = (compartments, sources, compartmentRenames) => {
if (compartment.modules) {
for (const name of keys(compartmentModules).sort()) {
const module = compartmentModules[name];
if (
module.compartment !== undefined &&
compartmentRenames[module.compartment] !== undefined
) {
if (module.compartment !== undefined) {
modules[name] = {
...module,
compartment: compartmentRenames[module.compartment],
};
} else {
// If compartment is not defined, the module must not be part of the compartment map.
modules[name] = module;
}
}
}
Expand Down
1 change: 1 addition & 0 deletions packages/compartment-mapper/src/import-archive.js
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ const makeArchiveImportHookMaker = (
// module is a "builtin" module and the policy needs to be enforced.
enforceModulePolicy(moduleSpecifier, compartmentDescriptor, {
exit: true,
errorHint: 'The module was not in the archive and an attempt was made to load it as a builtin',
});
const record = await exitModuleImportHook(moduleSpecifier);
if (record) {
Expand Down
1 change: 1 addition & 0 deletions packages/compartment-mapper/src/import-hook.js
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,7 @@ export const makeImportHookMaker = (
// hook returns something. Otherwise, we need to fall back to the 'cannot find' error below.
enforceModulePolicy(moduleSpecifier, compartmentDescriptor, {
exit: true,
errorHint: 'The module was not in the compartment map and an attempt was made to load it as a builtin',
});
if (archiveOnly) {
// Return a place-holder.
Expand Down
26 changes: 10 additions & 16 deletions packages/compartment-mapper/src/link.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ import { parseExtension } from './extension.js';
import {
enforceModulePolicy,
ATTENUATORS_COMPARTMENT,
diagnoseMissingCompartmentError,
attenuateGlobals,
makeDeferredAttenuatorsProvider,
} from './policy.js';
Expand Down Expand Up @@ -236,6 +235,7 @@ const makeModuleMapHook = (
// archive goes through foreignModuleSpecifier for local modules too
enforceModulePolicy(moduleSpecifier, compartmentDescriptor, {
exit: false,
errorHint: `TODO`, // TODO: add a testcase for this and provide a helpful hint
});
}

Expand All @@ -244,12 +244,7 @@ const makeModuleMapHook = (
throw Error(
`Cannot import from missing compartment ${q(
foreignCompartmentName,
)}${diagnoseMissingCompartmentError({
moduleSpecifier,
compartmentDescriptor,
foreignModuleSpecifier,
foreignCompartmentName,
})}`,
)}}`,
);
}
return foreignCompartment.module(foreignModuleSpecifier);
Expand Down Expand Up @@ -279,20 +274,19 @@ const makeModuleMapHook = (
throw Error(
`Cannot import from missing compartment ${q(
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: ` There is an alias defined that causes another package to be imported as ${q(
moduleSpecifier,
)}. Package ${q(moduleSpecifier)} resolves to ${q(
foreignModuleSpecifier,
)} in ${q(
foreignCompartmentName,
)}.`,
});
// The following line is weird.
// Information is flowing backward.
Expand Down
5 changes: 0 additions & 5 deletions packages/compartment-mapper/src/node-modules.js
Original file line number Diff line number Diff line change
Expand Up @@ -611,11 +611,6 @@ const translateGraph = (
},
policy,
);
// do not include compartments for packages not covered by policy
if (policy && !packagePolicy) {
// eslint-disable-next-line no-continue
continue;
}

/**
* @param {string} dependencyName
Expand Down
106 changes: 15 additions & 91 deletions packages/compartment-mapper/src/policy.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,29 +113,6 @@ export const dependencyAllowedByPolicy = (namingKit, packagePolicy) => {
return !!policyLookupHelper(packagePolicy, 'packages', canonicalName);
};

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,
)}`,
);
}
}
};

/**
* Returns the policy applicable to the canonicalName of the package
*
Expand All @@ -157,14 +134,11 @@ export const getPolicyForPackage = (namingKit, policy) => {
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 {};
}
};

Expand Down Expand Up @@ -354,25 +328,33 @@ export const attenuateGlobals = (
freezeGlobalThisUnlessOptedOut();
};


const diagnoseModulePolicy = (errorHint) => {
if(!errorHint) {
return '';
}
return ` (${errorHint})`;
}

/**
* Throws if importing of the specifier is not allowed by the policy
*
* @param {string} specifier
* @param {object} compartmentDescriptor
* @param {object} [info]
*/
export const enforceModulePolicy = (specifier, compartmentDescriptor, info) => {
export const enforceModulePolicy = (specifier, compartmentDescriptor, info = {}) => {
const { policy, modules } = compartmentDescriptor;
if (!policy) {
return;
}

if (!info.exit) {
if (!modules[specifier]) {
throw Error(
`Importing ${q(specifier)} was not allowed by policy packages:${q(
policy.packages,
)}`,
)}${diagnoseModulePolicy(info.errorHint)}`,
);
}
return;
Expand All @@ -382,7 +364,7 @@ export const enforceModulePolicy = (specifier, compartmentDescriptor, info) => {
throw Error(
`Importing ${q(specifier)} was not allowed by policy builtins:${q(
policy.builtins,
)}`,
)}${diagnoseModulePolicy(info.errorHint)}`,
);
}
};
Expand Down Expand Up @@ -456,61 +438,3 @@ export const attenuateModuleHook = async (
originalModuleRecord,
});
};

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 '';
};
84 changes: 6 additions & 78 deletions packages/compartment-mapper/test/snapshots/test-policy.js.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,113 +4,41 @@ The actual snapshot is saved in `test-policy.js.snap`.

Generated by [AVA](https://avajs.dev).

## policy - insufficient policy detected early / loadLocation

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - insufficient policy detected early / importLocation

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - insufficient policy detected early / makeArchive / parseArchive

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - insufficient policy detected early / makeArchive / parseArchive with a prefix

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - insufficient policy detected early / writeArchive / loadArchive

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - insufficient policy detected early / writeArchive / importArchive

> Snapshot 1
'Package "alice>carol" is allowed for "alice" to import but its policy is not defined. Please add a policy for "alice>carol"'

## policy - malfunction resulting in missing compartment / loadLocation

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - malfunction resulting in missing compartment / importLocation

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - malfunction resulting in missing compartment / makeArchive / parseArchive

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - malfunction resulting in missing compartment / makeArchive / parseArchive with a prefix

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - malfunction resulting in missing compartment / writeArchive / loadArchive

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - malfunction resulting in missing compartment / writeArchive / importArchive

> Snapshot 1
'Failed to load module "./index.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/alice/node_modules/carol/" (Package "carol" is missing. Are you sure there is an entry in policy resources specified for it?)'

## policy - attack - browser alias / loadLocation

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attack - browser alias / importLocation

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attack - browser alias / makeArchive / parseArchive

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attack - browser alias / makeArchive / parseArchive with a prefix

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attack - browser alias / writeArchive / loadArchive

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attack - browser alias / writeArchive / importArchive

> Snapshot 1
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Cannot import from missing compartment "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" (Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/" which seems disallowed by policy. There is likely an override defined that causes another package to be imported as "dan".)'
'Failed to load module "./attack.js" in package "file://.../compartment-mapper/test/fixtures-policy/node_modules/app/" (1 underlying failures: Importing "dan" was not allowed by policy packages:{"dan":true} ( There is an alias defined that causes another package to be imported as "dan". Package "dan" resolves to "." in "file://.../compartment-mapper/test/fixtures-policy/node_modules/hackity/".)'

## policy - attenuator error aggregation / loadLocation

Expand Down
Binary file modified packages/compartment-mapper/test/snapshots/test-policy.js.snap
Binary file not shown.
Loading

0 comments on commit 071dcb4

Please sign in to comment.