forked from mozilla/npm-lockdown
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathlockdown.js
More file actions
executable file
·209 lines (179 loc) · 6.38 KB
/
lockdown.js
File metadata and controls
executable file
·209 lines (179 loc) · 6.38 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
#!/usr/bin/env node
if (process.env['NPM_LOCKDOWN_RUNNING']) process.exit(0);
console.log("NPM Lockdown is here to check your dependencies! Never fear!");
var http = require('http'),
RegistryClient = require('silent-npm-registry-client'),
npmClient = new RegistryClient(),
url = require('url'),
crypto = require('crypto'),
exec = require('child_process').exec,
fs = require('fs'),
path = require('path'),
args = Array.prototype.splice.call(process.argv, 0, 2);
const ignoreSha = args.indexOf('--ignore-sha') >= 0;
try {
var lockdownJson = JSON.parse(fs.readFileSync(path.join(process.cwd(), 'lockdown.json')));
} catch(e) {
console.log("\nERROR: I cannot read lockdown.json! run node_modules/.bin/lockdown-relock to generate!\n");
process.exit(1);
}
exec('npm config get registry', function(err, stdout, stderr) {
if (err) throw err;
const registry = url.parse(stdout.trim());
// strip any trailing slash from the registry path
registry.pathname = registry.pathname && registry.pathname.replace(/\/$/,'');
console.log("using registry: " + registry.href);
var boundPort;
// during execution fatal errors will be appended to this list
var errors = [];
// during execution non-fatal warnings will be appended to this list
var warn = [];
function rewriteURL(u) {
return u.replace(registry.host + registry.pathname, '127.0.0.1:' + boundPort);
}
function packageOk(name, ver, sha, required) {
if (!lockdownJson[name]) {
if (required) {
errors.push("package '" + name + "' not in lockdown.json!");
}
return false;
}
if (lockdownJson[name][ver] === undefined) {
if (required) {
errors.push("package version " + name + "@" + ver + " not in lockdown.json!");
}
return false;
}
if (ignoreSha) {
// a '*' shasum is not checked
var wantSHA = lockdownJson[name][ver];
if (wantSHA !== '*' && wantSHA !== sha) {
if (required) {
errors.push("package " + name + "@" + ver + " has a different checksum (" +
wantSHA + " v. " + sha + ")");
}
return false;
}
if (wantSHA === '*') {
warn.push("Lockdown cannot guarantee your safety! No sha for pkg " + name + "@" + ver +
" in lockdown.json");
}
}
return true;
}
function rewriteVersionMD(json) {
if (typeof json === 'string') json = JSON.parse(json);
if (!json.error) {
json.dist.tarball = rewriteURL(json.dist.tarball);
// is the name/version/sha in our lockdown.json?
if (!packageOk(json.name, json.version, json.dist.shasum, true)) return null;
}
return JSON.stringify(json);
}
function rewritePackageMD(json) {
if (typeof json === 'string') json = JSON.parse(json);
if (!json.error) {
Object.keys(json.versions).forEach(function(ver) {
var data = json.versions[ver];
var name = data.name;
var sha = data.dist ? data.dist.shasum : undefined;
if (packageOk(name, ver, sha, false)) {
data.dist.tarball = rewriteURL(data.dist.tarball);
} else {
delete json.versions[ver];
}
});
}
return JSON.stringify(json);
}
function copy(from, to) {
for (var k in from) {
to[k] = from[k];
}
return to;
}
var server = http.createServer(function (req, res) {
if (req.method !== 'GET') {
return res.end('non GET requests not supported', 501);
}
// what type of request is this?
// 1. specific version json metadata (when explicit dependency is expressed)
// - for these requests we should verify the name/version/sha advertised is allowed
// 2. package version json metadata (when version range is expressed - including '*')
// XXX: for these requests we should prune all versions that are not allowed
// 3. tarball - actual bits
// XXX: for these requests we should verify the name/version/sha matches something
// allowed, otherwise block the transaction
var arr = req.url.substr(1).split('/');
var type = [ '', 'package_metadata', 'version_metadata', 'tarball' ][arr.length];
// let's extract pkg name and version sensitive to the type of request being performed.
var pkgname, pkgver;
if (type === 'tarball') {
pkgname = arr[0];
var getVer = new RegExp("^" + pkgname + "-(.*)\\.tgz$");
pkgver = getVer.exec(arr[2])[1];
} else if (type === 'version_metadata') {
pkgname = arr[0];
pkgver = arr[1];
} else if (type === 'package_metadata') {
pkgname = arr[0];
}
var hash = crypto.createHash('sha1');
var pkgUrl = registry.href + req.url.substr(1);
if (type === 'tarball') {
npmClient.fetch(pkgUrl, { timeout: 2000 }, function(err, rres) {
if (err) {
console.log('ERROR fetching: ', pkgUrl);
throw err;
} else {
rres.on('data', function(chunk) {
res.write(chunk);
});
rres.on('end', function() {
res.end();
});
}
});
} else {
npmClient.get(pkgUrl, { timeout: 2000 }, function(err, data, raw, rres) {
var lockedPkg = rewritePackageMD(raw);
if (lockedPkg === null) {
res.writeHead(404);
res.end("package installation disallowed by lockdown");
} else {
res.setHeader('Content-Length', Buffer.byteLength(lockedPkg));
res.writeHead(rres.statusCode);
res.end(lockedPkg);
}
});
}
});
server.listen(process.env['LOCKDOWN_PORT'] || 0, '127.0.0.1', function() {
boundPort = server.address().port;
var localRegistry = 'http://127.0.0.1:' + boundPort;
var env = copy(process.env, {
NPM_CONFIG_REGISTRY: localRegistry,
NPM_LOCKDOWN_RUNNING: "true"
});
var child = exec('npm install --registry=' + localRegistry, {
cwd: process.cwd(),
env: env
}, function(e) {
if (warn.length) {
console.log();
console.log("LOCKDOWN WARNINGS:");
warn.forEach(function(e) { console.log(" ", e); });
console.log();
}
if (errors.length) {
console.log();
console.log("LOCKDOWN ERRORS:");
errors.forEach(function(e) { console.log(" ", e); });
console.log();
}
process.exit(e ? 1 : 0);
});
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
});
});