Skip to content

Commit

Permalink
fix: secure the console and nestedEvaluate endowments
Browse files Browse the repository at this point in the history
We need to evaluate their wrappers in the SES root realm so that
their prototype chain can't be abused to escape into the primal
realm.
  • Loading branch information
michaelfig committed Mar 19, 2020
1 parent d39c827 commit ed13e80
Show file tree
Hide file tree
Showing 3 changed files with 73 additions and 20 deletions.
43 changes: 23 additions & 20 deletions packages/SwingSet/src/controller.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ import kernelSourceFunc from './bundles/kernel';
import { insistStorageAPI } from './storageAPI';
import { insistCapData } from './capdata';
import { parseVatSlot } from './parseVatSlots';
import { SES1MakeConsole } from './makeConsole';
import { SES1MakeNestedEvaluate } from './makeNestedEvaluate';

const log = anylogger('SwingSet:controller');

Expand Down Expand Up @@ -153,7 +155,6 @@ function makeSESEvaluator(registerEndOfCrank) {
const s = SES.makeSESRootRealm({
...otherOptions,
transforms: [...transforms, meteringTransformer],
consoleMode: 'allow',
errorStackMode: 'allow',
shims: [SES1TameMeteringShim, ...shims],
configurableGlobals: true,
Expand Down Expand Up @@ -205,25 +206,27 @@ function realmRegisterEndOfCrank(fn) {

return (src, tag = 'anonymous') => {
const filePrefix = `/SwingSet/${tag}`;
const localLog = anylogger(`SwingSet:${tag}`);

const nestedEvaluate = source =>
s.evaluate(source, {
// Support both getExport and nestedEvaluate module format.
require: r,
nestedEvaluate,

console: harden(localLog),

// FIXME: Note that this replaceGlobalMeter endowment is not any
// worse than before metering existed. However, it probably is
// only necessary to be added to the kernel, rather than all
// static vats once we add metering support to the dynamic vat
// implementation.
// FIXME: Same for registerEndOfCrank.
registerEndOfCrank: realmRegisterEndOfCrank,
replaceGlobalMeter,
});
const localConsole = SES1MakeConsole(s, anylogger(`SwingSet:${tag}`));

const nestedEvaluate = SES1MakeNestedEvaluate(s, {
// Support both getExport and nestedEvaluate module format.
require: r,

// This isn't installed on the global, but at least it's secure.
console: localConsole,

// Note that replaceGlobalMeter is evaluated within the SES realm.
// FIXME: Also note that this replaceGlobalMeter endowment is not any
// worse than before metering existed. However, it probably is
// only necessary to be added to the kernel, rather than all
// static vats once we add metering support to the dynamic vat
// implementation.
replaceGlobalMeter,

// FIXME: Same for registerEndOfCrank.
registerEndOfCrank: realmRegisterEndOfCrank,
});

return nestedEvaluate(src)(filePrefix).default;
};
}
Expand Down
28 changes: 28 additions & 0 deletions packages/SwingSet/src/makeConsole.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import harden from '@agoric/harden';

// We assume we're running in a SES environment.
export function makeConsole(baseConsole) {
// We restrict `console` to the API that can be implemented
// by the anylogger API.
// It should be unobservable by the code using it, notably
// only send output via the base console, and don't return a value.
const console = {};
for (const level of ['debug', 'log', 'info', 'warn', 'error']) {
console[level] = (...args) => {
baseConsole[level](...args);
return undefined;
};
}
return harden(console);
}

// This function implements makeConsole with SES1 (pre-lockdown).
export function SES1MakeConsole(SES1, baseConsole) {
// We evaluate within SES so that the resulting object does
// not give away access to the root realm.
const source = `(${makeConsole})`;
const myConsole = SES1.evaluate(source, {
harden: SES1.global.SES.harden,
})(baseConsole);
return myConsole;
}
22 changes: 22 additions & 0 deletions packages/SwingSet/src/makeNestedEvaluate.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import harden from '@agoric/harden';

// We assume we're running in a SES environment.
export function makeNestedEvaluate(compartment, endowments = {}) {
// Create a simple wrapper for the evaluator.
const nestedEvaluate = source => compartment.evaluate(source, endowments);

// Feed back the endowment to itself.
endowments.nestedEvaluate = nestedEvaluate;
return harden(nestedEvaluate);
}

// This function implements makeNestedEvaluate with SES1 (pre-lockdown).
export function SES1MakeNestedEvaluate(SES1, endowments = {}) {
// We evaluate within SES so that the resulting object does
// not give away access to the root realm.
const source = `(${makeNestedEvaluate})`;
const nestedEvaluate = SES1.evaluate(source, {
harden: SES1.global.SES.harden,
})(SES1, endowments);
return nestedEvaluate;
}

0 comments on commit ed13e80

Please sign in to comment.