Source Content Checks
The node runtime's module loader uses the _compile
method to actually
turn file content into code thus:
// Run the file contents in the correct scope or sandbox. Expose
// the correct helper variables (require, module, exports) to
// the file.
// Returns exception, if any.
Module.prototype._compile = function(content, filename) {
content = internalModule.stripShebang(content);
// create wrapper function
var wrapper = Module.wrap(content);
var compiledWrapper = vm.runInThisContext(wrapper, {
At the top of that method body, we can check that the content is on a list of production sources.
The entire process looks like:
- Developer develops and tests their app iteratively as normal.
- The developer generates a list of production sources via the dynamic bundling scheme outlined earlier, a static tool like webpack, or some combination.
- The bundling tool generates a file with a cryptographic hash
for each production source.
We prefer hashing to checking paths for reasons that will become
apparent later when we discuss
eval
. - A deploy script copies the bundle and the hashes to a production server.
- The server startup script passes a flag to
node
ornpm start
telling the runtime where to look for the production source hashes. - The runtime reads the hashes and combines it with any hashes necessary
to whitelist any
node
internal JavaScript files that might load viarequire
. - When a call to
require(x)
reachesModule.prototype.compile
it hashescontent
and checks that the hash is in the allowed set. If not, it logs that and, if not in report-only-mode, raises an exception. - Normal log collecting and monitoring communicates failures to the development team.
This is similar to Content-Security-Policy (CSP) but for server-side code. Like CSP, there is an intermediate step that might be useful between no enforcement and full enforcement: report only mode.