Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to import console.log safely? #189

Open
NodeGuy opened this issue Dec 20, 2018 · 5 comments

Comments

@NodeGuy
Copy link

commented Dec 20, 2018

Apologies for my confusion; I've watched the Report on Realms Shim Security Review and read many of the issues here but I'm still unclear about how to import a side-effect generating function such as console.log into a realm safely, or are we not quite there yet?

@erights

This comment has been minimized.

Copy link
Collaborator

commented Dec 21, 2018

For "importing", we're not quite there yet, though @warner or @jfparadis may be able to help you get there with the current state.

FWIW, we have been passing our own wrapping of console.log safely as log, as seen in the (currently commented out) calls at https://github.com/Agoric/PlaygroundVat/blob/master/examples/contract/makeMint.js
It'll take a bit of searching to find the code that sets that up.

We're working towards a module system that would work well with SES. Please let us know if you'd like to be involved.

@NodeGuy

This comment has been minimized.

Copy link
Author

commented Dec 24, 2018

I see, thank you. It looks like the code you linked is doing something like this:

"use strict";

const Realm = require(`../lib/realm-shim.js`);
const realm = Realm.makeRootRealm();

const log = (...args) => {
  console.log(...args);
};

realm.evaluate(`log("Hello outside world!")`, { log });

Is that considered safe?

We're working towards a module system that would work well with SES. Please let us know if you'd like to be involved.

Sure, how can I help?

@caridy

This comment has been minimized.

Copy link
Collaborator

commented Dec 26, 2018

@NodeGuy no, that is not safe, that has the same issue as the original console.log, it gives access to the outer realm's intrinsics via the proto chain of the new log function. This is the tricky part of the realms, folks will get confused really easy about the identity of the objects.

What the module system provides is an easy path to control that process, although it is not strictly necessary, you can always implement the shimming process for all the intrinsics that you want to allow inside your newly created realm, it is just a little bit of work to make it safe.

@jfparadis

This comment has been minimized.

Copy link
Collaborator

commented Dec 28, 2018

@NodeGuy, the main problem is that your log function is created in the outer realm, and will inherit the intrinsics of the outer realm. If you append these lines to your code, both condition fails:

const outerIntrinsics = log instanceof Function;
const innerIntrinsics = realm.evaluate(`log instanceof Function`, { log });
console.log(outerIntrinsics, innerIntrinsics);
// outputs true false

Here is one solution:

"use strict";

const Realm = require(`../lib/realm-shim.js`);
const realm = Realm.makeRootRealm();

// Create a factory function in the target realm. 
// The factory return a new function holding a closure.
const safeLogFactory = realm.evaluate(`
	(function safeLogFactory(unsafeLog) { 
		return function safeLog(...args) {
			unsafeLog(...args);
		}
	})
`);

// Create a safe function
const safeLog = safeLogFactory(console.log);

// Test it, abort if unsafe
const outerIntrinsics = safeLog instanceof Function;
const innerIntrinsics = realm.evaluate(`log instanceof Function`, { log: safeLog });
if (outerIntrinsics || !innerIntrinsics) throw new TypeError(); 

// Use it
realm.evaluate(`log("Hello outside world!")`, { log: safeLog });
@NodeGuy

This comment has been minimized.

Copy link
Author

commented Jan 2, 2019

Ah ha, thank you @jfparadis, I think I see the light!

Could I then import an async function safely like so?

"use strict";

const Realm = require(`../lib/realm-shim.js`);
const realm = Realm.makeRootRealm();

// Create a factory function in the target realm.
// The factory returns a new function holding a closure.
const safeAsyncFactory = realm.evaluate(`
  (function safeAsyncFactory(unsafeAsync) { 
    return async function safeAsync(...args) {
      // Resolve the outer realm promise and return a new inner realm promise.
      // Catch an outer realm exception and throw a new inner realm exception.
      // Assume a string return value but cast to string just in case.

      try {
        return String(await unsafeAsync(...args));
      } catch (exception) {
        throw new Error(String(exception.message))
      }
    }
  })
`);

// a function that returns a promise
const mockFetch = async url =>
  `The page at ${url} probably would have responded with "Hello world".`;

// Create a safe function
const safeFetch = safeAsyncFactory(mockFetch);

// Test it, abort if unsafe
const outerIntrinsics = safeFetch instanceof Function;

const innerIntrinsics = realm.evaluate(`mockFetch instanceof Function`, {
  mockFetch: safeFetch
});

if (outerIntrinsics || !innerIntrinsics) throw new TypeError();

// Test returned promise, abort if unsafe
const returnedPromise = realm.evaluate(`mockFetch("https://www.example.com")`, {
  mockFetch: safeFetch
});

const returnOuterIntrinsics = returnedPromise instanceof Promise;

const returninnerIntrinsics = realm.evaluate(
  `returnedPromise instanceof Promise`,
  {
    returnedPromise
  }
);

if (returnOuterIntrinsics || !returninnerIntrinsics) throw new TypeError();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
4 participants
You can’t perform that action at this time.