Jetpack Security Model
NOTE: This specification uses the word "Booster" in an entirely different way from its use in JEP 25. The eventual aim is to simply call the entities described in JEP 25 "SecurableModules", and to re-define Boosters to mean what this document says they are.
Features
Jetpack is designed to allow casual coders and experienced developers to create useful yet secure functionality that acts on behalf of a user agent (i.e., browser).
By "casual coders" we mean those who know the basics of JavaScript, but aren't previously equipped with knowledge about best practices. They are often self-described as "cut and paste" coders; sometimes they don't even describe themselves as coders at all.
By catering to this kind of developer, the Jetpack Platform needs to protect developers from their own inexperience: put simply, it should be easy to "do the right thing" where security is concerned, and harder to do things that are considered insecure (though not necessarily impossible, since this hampers generativity).
Sandboxing
All Jetpacks are executed in their own sandbox and obey the Principle of Least Authority (POLA). That is, by default, their code is executed in a sandbox that doesn't have access to do anything harmful to the user, be it destroying or stealing valuable information.
It's only with the user's consent—or, more preferably, the consent of a trusted security advisor, such as an AMO reviewer—that a Jetpack is executed with the privileges that the user or advisor grants to it. The suggested specification of privileges provided by the Jetpack is called its manifest.
After the point of execution, it's not possible for a Jetpack to reduce or escalate its privileges, and anything it tries to do that violates its privileges will result in a helpful security error.
Dependency Injection
The manifest is essentially used as a high-level specification that guides the injection of dependencies into the Jetpack's global environment.
For instance, if the manifest doesn't specify that the Jetpack needs access to the network, then the XMLHttpRequest object won't be inserted into the Jetpack's global scope—or perhaps a "dummy" object that raises a helpful security error when accessed will be injected in its place.
Alternatively, if the manifest specifies that the Jetpack only needs to communicate with foo.com, then XMLHttpRequest will be injected but attenuated such that connections to domains other than foo.com will fail.
Extenders
Extenders are like Jetpack's version of "native libraries": like traditional XULRunner extensions, they have full access to the Mozilla platform and its accompanying technologies such as XPCOM and XUL, but they are entrusted to expose a small subset of it to the importing Jetpack via dependency injection. This is useful for e.g. traditional addon developers who want to convert their extension to a Jetpack, but require a feature or two that the Jetpack platform doesn't yet provide.
Libraries
Code reuse is a key to a vibrant development ecosystem, but it must also be done with care lest the user's computer be open to attack. Jetpacks can leverage the CommonJS SecurableModule standard to load packages of CommonJS code; these libraries execute in a private scope, but with the same capabilities as their parent Jetpack. This naturally means that the more powerful a Jetpack is, the more of a threat third-party libraries pose to the user's system.
As per the CommonJS standard, a SecurableModule can be loaded via a call to the global require() function.
Code Signing
Extenders and other code that executes in a highly-privileged space are encouraged to be signed by a trusted authority such as AMO. This ensures the integrity of the code and means that end-users will be presented with a much friendlier user interface when trying to install the containing Jetpack.
The process for code signing is intended to be as unburdensome as possible. Ideally, adding a signature for code should consist of adding a property to a manifest file or a specially-formatted comment to the top of a script.
Privilege Separation
Jetpacks provide a mechanism called "Boosters" that all the Jetpack to embed "sub-Jetpacks" within itself, each with its own privilege manifest. The Jetpack can communicate with its Boosters via a simple JSON-based postMessage protocol.
Boosters are accessible through the jetpack.boosters namespace.
Capability Inference
For pedagogical purproses and to lower the barrier to entry as much as possible, small Jetpacks do not require developers to create explicit manifests. Instead, the Jetpack Platform performs basic static analysis on the Jetpack's source code to infer its manifest based on what API functions it appears to be using. This automatically-generated manifest is then presented to the end-user or advisor for authorization before the Jetpack executes.
That said, any third-party Libraries or Boosters linked to by Jetpacks will need to be expressly given privileges in a manifest; this is done to ensure that the Jetpack author is aware of what privileges they are giving the third-party code. It's also intended as a kind of explicit contract between the user and author of third-party code.
Helpful Errors
The term "helpful" is used to qualify the term "security error" in this document because it's expected that Jetpack authors will inadvertently encounter security errors when developing their own Jetpacks from time to time, and they will need guidance on how to change their code or their manifest to resolve the error, as well as information on how such a change affects the security of their Jetpack.
Therefore, instead of raising cryptic errors like "Security manager vetoed action", the text of the error should include specific information on why the action violated a security policy and what the developer can do about it.
Sample Code
Jetpack authors can bundle their Jetpack in a zip file, in which case a manifest can be placed in a manifest.json file. Alternatively, however, the Jetpack can be a single script that begins with a call to jetpack.manifest.set(), like so:
jetpack.manifest.set({
author: "Atul Varma",
capabilities: {
// Note that these capabilities are in addition to any
// capabilities we infer from static analysis of the Jetpack code.
network: {domains: ["toolness.com"]},
},
// Extenders import functionality from chrome-space (i.e., the Mozilla
// platform) to the Jetpack that aren't yet available in the
// Jetpack platform. They generally add things to the jetpack
// namespace.
extenders: ["http://foo.com/http-listener.zip"],
// These are URLS to CommonJS modules or archives of CommonJS modules.
// They are all given the same privileges as the Jetpack, and have
// access to the same jetpack.* namespace.
libraries: ["http://bar.com/blargy.zip",
"http://bar.com/jquery.js"],
// Boosters are "sub-Jetpacks" that the Jetpack may communicate with
// via a postMessage-like interface. They get their own sandbox and
// have their own capabilities. They're accessible via
// jetpack.boosters.<name>. Unlike the main Jetpack, no
// capability inference is performed on them, so the capabilities
// specified in this manifest are the only ones they get.
boosters: {
blah: {url: "http://toolness.com/md5.js",
// Don't give it any capabilities!
capabilities: {}}
}
});