A practical whitelist in JavaScript: es7-membrane, version 0.7

Several months ago, I announced es7-membrane, a new project for letting JavaScript developers control, through JS proxies, what their customers see of their own libraries.  A proxy, as you may recall, lets its creator define rules for looking up properties, defining properties, calling methods, etc., often with a real object underneath which the proxy’s handler internally refers to.

First off, a little bit of basics: an object is a collection of properties (some of which are functions, and officially named “methods”). This may sound obvious, but it’s important: you refer to other values by the object and a property name.

Proxies allow you to rewrite the rules for referring to other values by that tuple of the (containing) object and a property name. For instance, you can hide “private” members behind a proxy.

(Stack Overflow)

A membrane presents a one-to-one relationship between each proxy and a corresponding real object.  If you’re given a proxy to a document, and not the document itself, you can get other proxies, and those proxies can offer other properties that let you get back to the proxy of the document… but you can’t break out of any of the proxies to the underlying set of objects (or “object graph”).

When I made the announcement back in August of this new project, the membrane I presented was quite useless, implementing only a mirroring capability.  Not anymore.  There’s a few features that the latest version currently supports:

  1. Membrane owners can replace any proxy the membrane generates with a custom proxy, using the .modifyRules API
    • .createChainHandler(…) creates a new ProxyHandler derived from a handler used for an object graph the membrane already knows about.
    • .replaceProxy(oldProxy, newHandler) takes the new handler and returns a new proxy for the original value, provided the new proxy handler was created via .createChainHandler().
  2. Membrane owners can require a proxy to store new properties locally on the proxy, instead of propagating them through to the underlying object.  (.storeUnknownAsLocal(…) )
  3. Membrane owners can require a proxy to delete properties locally, instead of on the underlying object.  (.requireLocalDelete(…) )
  4. Membrane owners can hide existing properties of an object from the proxy’s users, as if those properties do not exist.  (.filterOwnKeys(…) )
  5. Membrane owners can be notified when a new proxy is about to go to the customer, and set up new rules for that proxy before the customer ever sees it.  (ObjectGraphHandler.prototype.addProxyListener(callback), ..removeProxyListener(callback) )
  6. Membrane owners can define as many object graphs as they want.  Traditionally, a membrane in JavaScript has supported only two object graphs.  (“wet”/”dry”, or “protected”/”public”, or “internal”/”external”… you get the idea)  But there’s no technical reason for that to be the upper limit.  The initial design of es7-membrane allowed the owner to define an object graph by name with a string.
    • Having more than one object graph could have a few applications:  different privileges for different users or customers, for example.
    • The es7-membrane test code uses “dry”, “wet” and “damp” object graphs.
    • For lack of a better name, es7-membrane calls these “multisided” membranes.  I have a document explaining how this works at a very temporary location.
  7. A new feature of ECMAScript 6, however, is symbols – which can be used as valid keys to an object, but are not strings.  Per a Github issue by Dr. Mark Miller of Google (who has been advising me on applications every now and then – thanks, Mark), now membrane owners can define object graphs by a “private” JavaScript symbol they create, as well.

Features two through five above combine to make whitelisting of properties in JavaScript very doable:  properties you don’t want exposed you filter out, and customers receiving proxies configured for local properties won’t propagate their changes to the your objects.  The listeners also mean you can apply those filters and local property rules before the customer ever sees any newly created proxies.  So properties you want private and/or protected really are private and/or protected from the malicious end-user.

Version 0.7 added the symbol support today, along with a probable memory leak clean-up and a little more protection for revoked object graphs.

The production files are available in the dist subdirectory of the es7-membrane project’s Github’ repository, or directly usable as a npm module.

I’m still looking for help in a few areas:

  • Building a static interactive demo site on Github
  • Code and documentation reviews
  • More testing
    • additional tests in Jasmine
    • converting tests to test-262 format (someone suggested the standard tests for ECMAScript might use some integration tests, such as a membrane implementation)
    • if there are weaknesses, where a customer who has only “dry” proxies and good old ECMAScript 6+ (no membrane access, no other proxies) can break out to reach “wet” proxies… no one’s seriously explored that yet.
  • Using the membrane module to protect itself from evildoers (dogfooding bonus)
  • Implementing other use cases for a multisided membrane
  • And just in general, another volunteer developer or two would be extremely welcome!