[e-lang] [Caja] An experiment and some questions
Andrea Campi
andrea.campi at gmail.com
Thu Feb 7 06:59:05 EST 2008
Hi,
I don't have any answer really, but I was reasoning about similar
lines, so here's more food for thought.
On Feb 7, 2008 1:55 AM, <ihab.awad at gmail.com> wrote:
> I have been experimenting with writing the rewriter rules in JavaScript. I
> will put that on hold for the moment while I take care of the other issues
> but this raises a question. Is there a standard mechanism whereby a Caja
> module can import another Caja module in a capability safe manner?
>
...
> * What, if any, is or should be the equivalent functionality in Caja? Should
> an importing module import another into the same ___OUTERS___, or should the
> ___OUTERS___ for the imported one be "cleanroom" isolated?
Re-reading Mark's thesis, I wondered again about modules vs plugins.
We conflate the two together, but wouldn't it be useful to keep the
distinction?
To recap, the Caja spec defines a module as "A package of Caja source code
to be cajoled together" (and as a consequence, the cajoled output too,
I would say). Furthermore, "Each instantiation of a Caja module is a
separate plugin."
But in fact, loadModule also instances a plugin, by calling
myNewModuleHandler.handle().
Simply splitting the task would recover the distinction, and also
allow a container to instance the same module twice, with different
outers, without having to reload the module--which is particularly
interesting if you factor in loading from the internet and JavaScript
cajoling:
function loadModule(module) {
return primFreeze(simpleFunc(module));
}
function loadModuleFromURL(url) {
var module = ... // load and cajole
return primFreeze(simpleFunc(module));
}
function newInstance(module) {
callPub(myNewModuleHandler, 'handle', [module]);
}
(Of course one can always build convenience functions to merge two
steps into one--load a module and instance a plugin in one shot.)
Now that I think of it, the only reason for having a module handler at
all is that a caja module auto-loads its own instances. Changing code
like this would actually allow for much simpler code, since
newInstance could simply pass an outer environment as an argument.
Your question about the "sub-environment" is interesting and I was
reasoning on that too (looks similar in purpose to the arena out of
section 11.3 of Mark's thesis, although there are differences). In
general, both would be useful, as in some case you may be loading a
friendly helper, and in others you may be dealing with untrusted code.
Leaving the choice to the implementor may sound risky, but the fact is
that even if you force the safer alternative (creating an arena) can
be defeated by pushing too much authority into it. As such, you cannot
really protect people from themselves.
What I was trying to do is come up with a complete set of primitives
for manipulating environments that provides safe and coincise idioms.
As I wrote before, I really don't like the way outers are populated,
I'd prefer (and I have proposed an implementation) something like:
// loading a module doesn't instance a plugin
<script src="aModule.js"></script>
<script src="anotherModule.js"></script>
<script src="untrustedModule.js"></script>
<script>
// create a per-plugin outers cloned from the shared outers
createScope(sharedOuters, function(outers) {
// get() is from my CajaCatalog package and allows you to retrieve them by
// name instead of having to keep track manually
createPlugin(get('aModule', outers);
createPlugin(get('anotherModule', outers);
});
createScope(sharedOuters, function(outers) {
createPlugin(get('aModule', outers);
// create an empty scope, and only endow it with a safe proxy
// (defined by aModule)
var sandbox = createScope(null, function(outers2) {
outers2.aModule = aModuleProxy;
createPlugin(get('untrustedModule', outers2);
});
// or we can clone our own outers
var sandbox2 = createScope(outers, function(outers2) {
outers2.untrustedModule = sandbox.untrustedModule;
createPlugin(get('anotherModule', outers2);
});
});
</script>
This is a contrived example, but it does show how easy and elegant it can get.
OTOH your question is more on how can a module load another one; in
that case, createScope would have to be available to it. Which means
it either needs to be part of Caja, or your module would have a
dependency on a 3rd party (i.e. my CajaCatalog) module. Food for
thought.
Next: all of this is quite similar to Mark's description of a loader,
except that it trusts the module to figure out what it needs from the
environment. I started thinking how could we change to above to:
createPlugin(module,
['aModule': aModule,
'anotherModule': aReadOnlyFacet
]);
The reason for this is, outers strike me as still providing ambient
authority, no matter how little (introducing successive cloning as
above makes things worse from this point of view, since it becomes
harder to ensure a well-controlled environment). Explicit binding
would eliminate any such risk.
The problem is that we would need a good deal of metadata to be able
to do so. Maybe some of it could be extraded during cajoling, but
still, a lot of work to do in this area.
Thoughts?
Bye,
Andrea
More information about the e-lang
mailing list