UNPKG

4.86 kBJavaScriptView Raw
1/*
2 * This file is used by the browser plugins and the Cli scanner and thus
3 * cannot have any external dependencies (no require)
4 */
5
6var exports = exports || {};
7exports.version = '2.2.4';
8
9function isDefined(o) {
10 return typeof o !== 'undefined';
11}
12
13function uniq(results){
14 var keys = {};
15 return results.filter(function(r) {
16 var k = r.component + ' ' + r.version;
17 keys[k] = keys[k] || 0;
18 return keys[k]++ === 0;
19 });
20}
21
22function scan(data, extractor, repo, matcher) {
23 matcher = matcher || simpleMatch;
24 var detected = [];
25 for (var component in repo) {
26 var extractors = repo[component].extractors[extractor];
27 if (!isDefined(extractors)) continue;
28 for (var i in extractors) {
29 var match = matcher(extractors[i], data);
30 if (match) {
31 match = match.replace(/(\.|-)min$/, "");
32 detected.push({ version: match, component: component, detection: extractor });
33 }
34 }
35 }
36 return uniq(detected);
37}
38
39function simpleMatch(regex, data) {
40 var re = new RegExp(regex);
41 var match = re.exec(data);
42 return match ? match[1] : null;
43}
44function replacementMatch(regex, data) {
45 var ar = /^\/(.*[^\\])\/([^\/]+)\/$/.exec(regex);
46 var re = new RegExp(ar[1]);
47 var match = re.exec(data);
48 var ver = null;
49 if (match) {
50 ver = match[0].replace(new RegExp(ar[1]), ar[2]);
51 return ver;
52 }
53 return null;
54}
55
56function splitAndMatchAll(tokenizer) {
57 return function(regex, data) {
58 var elm = data.split(tokenizer).pop();
59 return simpleMatch('^' + regex + '$', elm);
60 };
61}
62
63
64
65function scanhash(hash, repo) {
66 for (var component in repo) {
67 var hashes = repo[component].extractors.hashes;
68 if (!isDefined(hashes)) continue;
69 if (hashes.hasOwnProperty(hash)) {
70 return [{ version: hashes[hash], component: component, detection: 'hash' }];
71 }
72 }
73 return [];
74}
75
76
77
78function check(results, repo) {
79 for (var r in results) {
80 var result = results[r];
81 if (!isDefined(repo[result.component])) continue;
82 var vulns = repo[result.component].vulnerabilities;
83 for (var i in vulns) {
84 if (!isDefined(vulns[i].below) || !isAtOrAbove(result.version, vulns[i].below)) {
85 if (isDefined(vulns[i].atOrAbove) && !isAtOrAbove(result.version, vulns[i].atOrAbove)) {
86 continue;
87 }
88 var vulnerability = { info : vulns[i].info, below: vulns[i].below, atOrAbove: vulns[i].atOrAbove };
89 if (vulns[i].severity) {
90 vulnerability.severity = vulns[i].severity;
91 }
92 if (vulns[i].identifiers) {
93 vulnerability.identifiers = vulns[i].identifiers;
94 }
95 result.vulnerabilities = result.vulnerabilities || [];
96 result.vulnerabilities.push(vulnerability);
97 }
98 }
99 }
100 return results;
101}
102
103function unique(ar) {
104 var r = [];
105 ar.forEach(function(e) {
106 if (r.indexOf(e) == -1) r.push(e);
107 });
108 return r;
109}
110
111
112function isAtOrAbove(version1, version2) {
113 var v1 = version1.split(/[\.\-]/g);
114 var v2 = version2.split(/[\.\-]/g);
115 var l = v1.length > v2.length ? v1.length : v2.length;
116 for (var i = 0; i < l; i++) {
117 var v1_c = toComparable(v1[i]);
118 var v2_c = toComparable(v2[i]);
119 if (typeof v1_c !== typeof v2_c) return typeof v1_c === 'number';
120 if (v1_c > v2_c) return true;
121 if (v1_c < v2_c) return false;
122 }
123 return true;
124}
125
126function toComparable(n) {
127 if (!isDefined(n)) return 0;
128 if (n.match(/^[0-9]+$/)) {
129 return parseInt(n, 10);
130 }
131 return n;
132}
133
134
135//------- External API -------
136
137exports.check = function(component, version, repo) {
138 return check([{component: component, version: version}], repo);
139};
140
141exports.replaceVersion = function(jsRepoJsonAsText) {
142 return jsRepoJsonAsText.replace(/§§version§§/g, '[0-9][0-9.a-z_\\\\-]+');
143};
144
145exports.isVulnerable = function(results) {
146 for (var r in results) {
147 if (results[r].hasOwnProperty('vulnerabilities')) return true;
148 }
149 return false;
150};
151
152exports.scanUri = function(uri, repo) {
153 var result = scan(uri, 'uri', repo);
154 return check(result, repo);
155};
156
157exports.scanFileName = function(fileName, repo) {
158 var result = scan(fileName, 'filename', repo, splitAndMatchAll('/'));
159 return check(result, repo);
160};
161
162exports.scanFileContent = function(content, repo, hasher) {
163 var normalizedContent = content.toString().replace(/(\r\n|\r)/g, "\n");
164 var result = scan(normalizedContent, 'filecontent', repo);
165 if (result.length === 0) {
166 result = scan(normalizedContent, 'filecontentreplace', repo, replacementMatch);
167 }
168 if (result.length === 0) {
169 result = scanhash(hasher.sha1(normalizedContent), repo);
170 }
171 return check(result, repo);
172};
173
174exports.scanNodeDependency = function(dependency, npmrepo, options) {
175 if (!isDefined(dependency.version)) {
176 if (options.log) options.log.warn('Missing version for ' + dependency.component + '. Need to run npm install ?');
177 return [];
178 }
179 if (!isDefined(npmrepo[dependency.component])) return [{component: dependency.component, version: dependency.version, file: dependency.file}];
180 return check([dependency], npmrepo);
181};
182
183
184