Skip to content

Commit

Permalink
feat(compartment-mapper): wildcard policy specified as "any"
Browse files Browse the repository at this point in the history
  • Loading branch information
naugtur committed Feb 14, 2023
1 parent a6384dc commit 89e7104
Show file tree
Hide file tree
Showing 3 changed files with 101 additions and 33 deletions.
10 changes: 2 additions & 8 deletions packages/compartment-mapper/demo/policy/index.mjs
Expand Up @@ -26,14 +26,8 @@ const ApiSubsetOfBuffer = harden({ from: Buffer.from });
const options = {
policy: {
entry: {
globals: {
Buffer: true,
console: true,
},
packages: {
entropoetry: true,
dotenv: true,
},
globals: 'any',
packages: 'any',
builtins: {
fs: {
attenuate: '@endo/compartment-mapper-demo-policy-attenuator1',
Expand Down
52 changes: 42 additions & 10 deletions packages/compartment-mapper/src/policy.js
Expand Up @@ -70,6 +70,36 @@ const generateCanonicalName = ({ isEntry = false, name, path }) => {
return path.join('>');
};

const POLICY_FIELDS = ['builtins', 'globals', 'packages'];

/**
*
* @param {Object} packagePolicy
* @param {string} field
* @param {string} itemName
* @returns {boolean|Object}
*/
const policyLookupHelper = (packagePolicy, field, itemName) => {
if (!POLICY_FIELDS.includes(field)) {
throw Error(`Invalid field ${q(field)}`);
}
if (
typeof packagePolicy !== 'object' ||
packagePolicy === null ||
!packagePolicy[field]
) {
return false;
}

if (packagePolicy[field] === 'any') {
return true;
}
if (packagePolicy[field][itemName]) {
return packagePolicy[field][itemName];
}
return false;
};

/**
* Verifies if a module identified by namingKit can be a dependency of a package per packagePolicy.
* packagePolicy is required, when policy is not set, skipping needs to be handled by the caller.
Expand All @@ -84,12 +114,12 @@ export const dependencyAllowedByPolicy = (namingKit, packagePolicy) => {
return false;
}
const canonicalName = generateCanonicalName(namingKit);
return packagePolicy.packages && !!packagePolicy.packages[canonicalName];
return !!policyLookupHelper(packagePolicy, 'packages', canonicalName);
};

const validateDependencies = (policy, canonicalName) => {
const packages = policy.resources[canonicalName].packages;
if (!packages) {
if (!packages || packages === 'any') {
return;
}

Expand Down Expand Up @@ -162,7 +192,7 @@ const getGlobalsList = packagePolicy => {
* @returns {object} limitedGlobals
*/
export const getAllowedGlobals = (globals, packagePolicy) => {
if (!packagePolicy) {
if (!packagePolicy || packagePolicy.globals === 'any') {
return globals;
}
const list = getGlobalsList(packagePolicy);
Expand Down Expand Up @@ -193,7 +223,7 @@ export const assertModulePolicy = (specifier, compartmentDescriptor, info) => {
return;
}

if (!policy.builtins || !policy.builtins[specifier]) {
if (!policyLookupHelper(policy, 'builtins', specifier)) {
throw Error(
`Importing '${specifier}' was not allowed by policy 'builtins':${q(
policy.builtins,
Expand Down Expand Up @@ -247,21 +277,23 @@ export const attenuateModuleHook = (
policy,
attenuators,
) => {
if (policy && (!policy.builtins || !policy.builtins[specifier])) {
const policyValue = policyLookupHelper(policy, 'builtins', specifier);
if (!policy || policyValue === true) {
return originalModule;
}

if (!policyValue) {
throw Error(
`Attenuation failed '${specifier}' was not in policy builtins:${q(
policy.builtins,
)}`,
);
}
if (!policy || policy.builtins[specifier] === true) {
return originalModule;
}

return attenuateModule({
attenuators,
name: policy.builtins[specifier].attenuate,
params: policy.builtins[specifier].params,
name: policyValue.attenuate,
params: policyValue.params,
originalModule,
});
};
Expand Down
72 changes: 57 additions & 15 deletions packages/compartment-mapper/test/test-policy.js
Expand Up @@ -63,38 +63,80 @@ const policy = {
myattenuator: {},
},
};
const ANY = {
globals: 'any',
packages: 'any',
builtins: 'any',
};
const anyPolicy = {
entry: policy.entry,
resources: {
...policy.resources,
'alice>carol': ANY,
},
};

const expectations = {
alice: { bluePill: 'undefined', redPill: 'number', purplePill: 'undefined' },
bob: { bluePill: 'number', redPill: 'undefined', purplePill: 'undefined' },
carol: { bluePill: 'undefined', redPill: 'undefined', purplePill: 'number' },
builtins: 'a,b',
const defaultExpectations = {
namespace: {
alice: {
bluePill: 'undefined',
redPill: 'number',
purplePill: 'undefined',
},
bob: { bluePill: 'number', redPill: 'undefined', purplePill: 'undefined' },
carol: {
bluePill: 'undefined',
redPill: 'undefined',
purplePill: 'number',
},
scopedBob: { scoped: 1 },
builtins: 'a,b',
},
};
const anyExpectations = {
namespace: {
...defaultExpectations.namespace,
carol: { bluePill: 'number', redPill: 'number', purplePill: 'number' },
},
};

const fixtureAssertionCount = 2;
const assertFixture = async (t, { namespace, compartments }) => {
const { alice, bob, carol, builtins } = namespace;
t.deepEqual({ alice, bob, carol, builtins }, expectations);
const makeAssertions =
expectations =>
async (t, { namespace, compartments }) => {
t.deepEqual(namespace, expectations.namespace);

await t.throwsAsync(
() => compartments.find(c => c.name.includes('alice')).import('hackity'),
{ message: /Failed to load module "hackity" in package .*alice/ },
'Attempting to import a package into a compartment despite polict should fail.',
);
};
await t.throwsAsync(
() => compartments.find(c => c.name.includes('alice')).import('hackity'),
{ message: /Failed to load module "hackity" in package .*alice/ },
'Attempting to import a package into a compartment despite policy should fail.',
);
};

scaffold(
'policy enforcement',
test,
fixture,
assertFixture,
makeAssertions(defaultExpectations),
fixtureAssertionCount,
{
addGlobals: globals,
policy,
},
);

scaffold(
'policy enforcement with "any" policy',
test,
fixture,
makeAssertions(anyExpectations),
fixtureAssertionCount,
{
addGlobals: globals,
policy: anyPolicy,
},
);

const assertTestAlwaysThrows = t => {
t.fail('Expected it to throw.');
};
Expand Down

0 comments on commit 89e7104

Please sign in to comment.