WIP, I had to run in the middle of writing this and will flesh it out in more detail later
By default, we sandbox Ember apps running in FastBoot such that they cannot access any built in Node.js features: no file IO, no network, etc.
However, FastBoot apps need to be able to fetch model data over the
network, the same way they do in the browser. Historically, we've
hardcoded the najax
global to be available in the FastBoot context,
which provides a jQuery.ajax
-compatible API for Node.
However, this is approach is brittle, future-hostile and limited (what if the app uses WebSockets?). It would be better to allow addons to specify that npm packages they rely on for their functionality, and allow developers to decide if that's a capability they want to expose to their Ember app.
I propose a system for allowing Ember addons to enumerate the
npm packages they rely on when running in Node. When the FastBoot server
boots, it verifies that these dependencies are available. It then
provides a custom require()
global to the app.
Only whitelisted packages are allowed to be require()
d. If the app
attemps to require()
a package that is not whitelisted, an exception
will be raised.
An ember-fastboot
key is introduced to the package.json
of addons
and apps. It contains a dependencies
hash identical in format to the
standard dependencies
or devDependencies
hash in npm packages.
Example:
{
"name": "ember-network",
"version": "0.1.0",
"ember-fastboot": {
"dependencies": {
"node-fetch": "^0.10.1"
}
},
"dependencies": {
"broccoli-merge-trees": "^1.0.0",
"broccoli-stew": "^1.0.4",
"broccoli-templater": "0.0.2",
"ember-cli-babel": "^5.1.5",
"node-fetch": "^1.3.3",
"whatwg-fetch": "^0.10.1"
}
}
This example package.json
tells Ember that the ember-network
addon
can access a single package, node-fetch
, when running in FastBoot. By
isolating FastBoot-accessible packages in a single list, developers can
simply and reliably audit what capabilities an addon requires to
function.
Files can require a package using the standard CommonJS pattern:
let fetch = require('node-fetch');
export default fetch;
The mix of JavaScript module syntax and CommonJS is ugly and potentially confusing.
Because addons can specify their own capabilities, the increased surface area for security vulnerabilities may be too great of a risk to bear (see the Security open issue).
We need a standardized way to swap out different files depending on the build target (i.e., the browser or FastBoot).
Developers have always had to treat addons with caution, as a malicious addon could add deliberate XSS attacks, exfiltrate priveleged data to an attacker's domain, etc.
However, giving addons the ability to run arbitrary code on a server in
the datacenter dramatically escalates the surface area of potential
attacks. Today's FastBoot apps are totally sandboxed and cannot do
anything other than fetch data through najax
and return a string of
HTML to the FastBoot server.
JavaScript is an inherently insecure language. A package that has been required in a privileged context may hold a reference to filesystem facilities, for example, that can be discovered when it is passed to the sandboxed context. A brute force attack may traverse the entire object graph looking for leaked references that allow it to escalate its capabilities beyond what the author intended.
- Whitelist functionality and expose Node primitives, like network a