Skip to content

Instantly share code, notes, and snippets.

@tomdale
Created December 9, 2015 22:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tomdale/239676ad88289c56b74e to your computer and use it in GitHub Desktop.
Save tomdale/239676ad88289c56b74e to your computer and use it in GitHub Desktop.
FastBoot dependencies

WIP, I had to run in the middle of writing this and will flesh it out in more detail later

Managing Node.js Dependencies in FastBoot Apps

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.

Proposal

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.

Specifying Dependencies

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.

Using Dependencies

Files can require a package using the standard CommonJS pattern:

let fetch = require('node-fetch');

export default fetch;

Drawbacks

Mixed Module Syntax

The mix of JavaScript module syntax and CommonJS is ugly and potentially confusing.

Arbitrary Access to Capabilities

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).

Open Issues

Environment-Specific Code

We need a standardized way to swap out different files depending on the build target (i.e., the browser or FastBoot).

Security

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.

Alternatives

  1. Whitelist functionality and expose Node primitives, like network a
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment