Labs/Jetpack/Reboot/JEP/106
JEP 106 - Registered Jetpack URLs
- Champion: Atul Varma - avarma@mozilla.com
- Status: Accepted/Pre-Production
- Bug Ticket: bug 549319
- Type: API
Problem Statement
Every file in a Jetpack package needs a unique identifier for a variety of reasons:
- If an error occurs in a Jetpack's code, the traceback needs to include a URL that doesn't give away potentially sensitive information about its location on the user's filesystem.
- If a Jetpack wants to embed one of its images in a web page, it probably needs to do so with a URL, although such could also be done with a data: URI.
- If a Jetpack wants to self-host a web-like UI, it needs a URL to display in the URL bar. This URL should preferably be memorable but doesn't need to be.
- If a Jetpack wants to process the data contained in a file in its package, it should do so via a URL (though a relative path can potentially be used).
A prerequisite for this is that each Jetpack must have a unique ID that cannot be spoofed. This is valuable for a number of reasons:
- The host environment needs a way to keep track of Jetpack-specific resources; thus each Jetpack needs a primary key. If a Jetpack is uninstalled and reinstalled later and the user wants to keep their data, then the primary key may need to be preserved.
- The host environment needs a way of telling that one Jetpack is an upgrade for another, while guaranteeing that the upgrade isn't a forgery.
Proposal
Jetpacks are accessible through the jetpack: protocol. Each Jetpack has a keypair associated with it, and a Jetpack's ID is the SHA-1 hash of its public key. Jetpacks must be signed using their private key, and will not be loaded unless signed; thus a Jetpack ID, if present in the system, is guaranteed to be unforgeable.
Use-Cases
- The wants to display an image/resource/L10n bundled with a Jetpack - option for allowing exposure of resource location needs consideration
- Developer debugging ergonomics
- Dynamically created, cache enabled, resources
Key Issues
There are a number of security issues involved in this.
- Jetpacks should have a way of specifying which of their resources is "private", i.e. accessible only by itself, vs. "public", i.e. accessible from external code like web pages.
- It shouldn't be possible for a Jetpack to "spoof" another by e.g. having the same ID.
- The security policy for HTML code needs to be well-defined; should it follow the Web's standard single-origin policy, or something else? Can scripts be loaded from remote pages?
Dependencies & Requirements
Internal Methods
API Methods
Discussion
My thoughts (developed during a discussion with Atul last week):
- A "jetpack ID" should be the hash of a public verifying key. "jpx create-new-addon" will create a keypair, store the private signing key under ~/.jetpack/, and create a new skeleton add-on directory (with the public key and a manifest, and maybe a hello-world sample).
- There will be a "jpx publish" command which bundles the various files together, signs the bundle, and drops the signed result in a well-known place, perhaps with an option to upload it to AMO
- The main purpose of the signed bundle is to convince the add-on manager/loader to grant the new code the same authorities that were granted to its predecessor. In particular, any data stored by one version of an add-on should be available to the update too.
- A well-known subdirectory of the add-on, perhaps ADDONBASE/resources/, should be used for "bundled resources". These are copied verbatim into the addon bundle.
- Content frames (as described in JEP-115) should have easy access to bundled resources. In particular, creating such a frame with a URL of "/panel.html" should populate the frame with the contents of ADDONBASE/resources/panel.html , and relative URLs inside it should reference bundled resources too.
- There should be a simple call that lets add-on code access the contents of bundled resources (from code, as opposed to from HTML), like:
var resources = require("bundled-resources"); var data = resources.load("panel.html")
- These bundled resources are, in general, *not* available from outside the add-on. However, I think it could be useful to add a "publish" feature, such that an add-on could do something like:
var publisher = require("publisher"); publisher.publish("/foo.txt", function (req) { return "foo!" });
and then the rest of the browser could do a GET of jetpack://id/$JETPACKID/foo.txt and be given "foo!". The use of $JETPACKID here would be unforgeable.
- When an exception occurs and we need put a "filename" property onto the exception object, we can use jetpack://$JETPACKID/jsfilename for debugging purposes. It would not necessarily be possible to paste this into the addressbar and see the source code in question, though (this sounds useful, but I think it's also important to allow add-ons to keep their resources private, so maybe only allow this to work in a debug console of some sort). It might be best for these URLs to have a different protocol, maybe jetpack-exception://, to distinguish it from the URLs managed by the "publisher" feature.
--Brian Warner 06:28, 16 February 2010 (UTC)
Our current plan:
- The Jetpack ID will be made available to add-ons through the "self" package:
var my_id = require("self").id;
- The add-on package's `data` directory will contain the bundled resources. So if the main add-on code lives in PKGROOT/lib/main.js , a typical resource would live in PKGROOT/data/icon.png
- The "self" package has a `data` property which provides access to these resources:
var data = require("self").data; var text = data.load("foo.txt"); // contents of PKGROOT/data/foo.txt var icon_url = data.url("icon.png");
- The URL method will return a URL instance that can be used to load the given resource. This URL can only be used by content frames opened by the same package which provided the resource: it does not provide a way for add-ons to publish data to anyone else (we'll offer some other mechanism for that in the future).
- We expect the URL to look like `resource://jetpack/$JID/main.html`, but the actual syntax is opaque and private: add-on code should not depend upon the details. The only promise made by `data.url()` is that relative links should work correctly: PKGROOT/data/page.html can reference PKGROOT/data/image.png with a normal `<img src="image.png"/>` tag.
- An NSIContentPolicy object will be used to enforce the non-sharedness of these resource URLs. This may not be enforced right away, but eventually it will.
--Brian Warner 22:49, 9 April 2010 (UTC)