Skip to content

Instantly share code, notes, and snippets.

@indexzero
Forked from zaach/seal.js
Created July 10, 2012 03:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save indexzero/3080704 to your computer and use it in GitHub Desktop.
Save indexzero/3080704 to your computer and use it in GitHub Desktop.
#!/usr/bin/env node
/*
* This module can verify that packages installed during development are
* identical to those installed during deployment. The standard npm shrinkwrap
* only ensures that package versions are the same, but does not verify contents.
* This module checks the shasum of the package tarballs downloaded by npm during
* development and deployment to ensure they are the same.
*
* Usage:
* 1) Install you packages
* 2) Generate shrinkwrap
* 3) Generate a sealed shrinkwrap file (using this module)
* 4) Deploy code and install packages
* 5) Check sealed shrinkwrap against installed packages
* during deployment (after performing `npm install`).
* */
const crypto = require('crypto');
const fs = require('fs');
const path = require('path');
const CACHE_DIR = process.env['HOME'] + '/.npm';
// generate shasums
function hashFile (filename, cb) {
var shasum = crypto.createHash('sha256');
var s = fs.ReadStream(filename);
s.on('data', function(d) {
shasum.update(d);
});
s.on('end', function() {
var d = shasum.digest('hex');
if (cb) cb(null, d);
});
}
// load (sealed) shrinkwrap file
function loadWrap (file) {
return JSON.parse(fs.readFileSync(path.resolve(file), "utf8"));
}
// Generate a shrinkseal file
// packageDir is the directory of the package you want to generate a shrinkseal file for
// there should be a shrinkwrap file in the directory root
// cacheDir is the directory where the dependency packages are cached
function generateSeal (shrinkwrapFile, cacheDir, cb) {
var wrap = loadWrap(shrinkwrapFile || 'npm-shrinkwrap.json');
traverseWrap(wrap, cacheDir,
function (err, name, dep, d) {
if (err) throw err;
dep.shasum = d;
}, function (err, wrap) {
if (err) throw err;
fs.writeFile('sealed-npm-shrinkwrap.json',JSON.stringify(wrap, null, ' '), function (err) {
if (err) throw err;
});
});
}
// Check that the sealed shrinkwrap corresponds to the currently installed modules
// packageDir is the directory of the package you want to generate a shrinkseal file for
// there should be a shrinkwrap file in the directory root
// cacheDir is the directory where the dependency packages are cached
function checkSeal (sealedwrapFile, cacheDir, cb) {
var sealed = loadWrap(sealedwrapFile || 'sealed-npm-shrinkwrap.json');
var errors = [];
traverseWrap(sealed, cacheDir,
function (err, name, dep, d) {
if (err) throw err;
if (dep.shasum !== d) {
errors.push({dep: name + ' ' + dep.version, expected: dep.shasum, actual: d});
}
}, function (err, wrap) {
if (err) throw err;
if (errors.length) {
console.error(JSON.stringify(errors, null, ' '));
process.exit(1);
}
});
}
function traverseWrap (wrap, cacheDir, hashCB, endCB) {
var toHash = 0;
Object.keys(wrap.dependencies).forEach(function (depName) {
_traverseWrap(depName, wrap.dependencies[depName]);
});
function _traverseWrap (name, dep) {
toHash++;
hashFile(path.resolve(path.join(cacheDir, name, dep.version, "package.tgz")), function (err, d) {
hashCB(err, name, dep, d);
if (--toHash === 0) {
endCB(err, wrap);
}
});
if (dep.dependencies) {
Object.keys(dep.dependencies).forEach(function (depName) {
_traverseWrap(depName, dep.dependencies[depName]);
});
}
}
}
exports.generateSeal = generateSeal;
exports.checkSeal = checkSeal;
//generateSeal(process.argv[2], process.argv[3] || CACHE_DIR);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment