retire 4.1.0 → 4.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -2
- package/lib/cli.js +46 -34
- package/lib/depsdev.d.ts +2 -2
- package/lib/depsdev.js +46 -25
- package/lib/repo.d.ts +1 -1
- package/lib/repo.js +7 -10
- package/lib/reporters/console.d.ts +1 -1
- package/lib/reporters/console.js +14 -7
- package/lib/reporters/cyclonedx-json.d.ts +1 -1
- package/lib/reporters/cyclonedx-json.js +29 -20
- package/lib/reporters/cyclonedx.d.ts +1 -1
- package/lib/reporters/cyclonedx.js +16 -7
- package/lib/reporters/depcheck.d.ts +1 -1
- package/lib/reporters/depcheck.js +34 -19
- package/lib/reporters/json.d.ts +1 -1
- package/lib/reporters/json.js +8 -4
- package/lib/reporting.d.ts +2 -2
- package/lib/reporting.js +33 -17
- package/lib/resolve.d.ts +2 -2
- package/lib/resolve.js +2 -2
- package/lib/retire.js +117 -120
- package/lib/scanner.d.ts +1 -1
- package/lib/scanner.js +17 -19
- package/lib/types.d.ts +4 -3
- package/lib/types.js +1 -1
- package/lib/utils.d.ts +1 -1
- package/lib/utils.js +0 -2
- package/package.json +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,28 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [4.2.0]
|
|
4
|
+
|
|
5
|
+
### Changes
|
|
6
|
+
|
|
7
|
+
- Use prettier to get formatting of code
|
|
8
|
+
- Add provenance
|
|
9
|
+
|
|
10
|
+
## Security fixes
|
|
11
|
+
|
|
12
|
+
- Upgrade dependencies
|
|
13
|
+
|
|
14
|
+
## [4.1.1]
|
|
15
|
+
|
|
16
|
+
### Bugfixes
|
|
17
|
+
|
|
18
|
+
- Handle error if OSV does not have library or version
|
|
3
19
|
|
|
4
20
|
## [4.1.0]
|
|
5
21
|
|
|
6
22
|
### Additions
|
|
23
|
+
|
|
7
24
|
- Option to check results for the component from OSV
|
|
8
25
|
- Option to use more than one JS repository
|
|
9
26
|
|
|
10
27
|
### Bugfixes
|
|
28
|
+
|
|
11
29
|
- Remove dropexternal as it never worked
|
|
12
30
|
|
|
13
31
|
## [4.0.1]
|
|
14
32
|
|
|
15
33
|
### Breaking changes
|
|
34
|
+
|
|
16
35
|
- Not really a breaking change as the change was introduced in 4.0.0, but node.js >= 14.0.0 is required to run retire.js >= 4.0.0 going forward
|
|
17
36
|
|
|
18
37
|
## [4.0.0]
|
|
19
38
|
|
|
20
39
|
### Breaking changes
|
|
40
|
+
|
|
21
41
|
- npm scanning no longer supported
|
|
22
42
|
|
|
23
43
|
### Changes
|
|
24
|
-
- Complete rewrite to typescript
|
|
25
44
|
|
|
45
|
+
- Complete rewrite to typescript
|
|
26
46
|
|
|
27
47
|
## [3.2.3]
|
|
28
48
|
|
|
@@ -64,7 +84,6 @@
|
|
|
64
84
|
|
|
65
85
|
- Updating proxy library to fix proxy issues
|
|
66
86
|
|
|
67
|
-
|
|
68
87
|
## [3.0.6]
|
|
69
88
|
|
|
70
89
|
### Security
|
package/lib/cli.js
CHANGED
|
@@ -45,8 +45,8 @@ const z = __importStar(require("zod"));
|
|
|
45
45
|
const events = new events_1.EventEmitter();
|
|
46
46
|
let failProcess = false;
|
|
47
47
|
const defaultIgnoreFiles = ['.retireignore', '.retireignore.json'];
|
|
48
|
-
if (process.argv.includes(
|
|
49
|
-
console.log(
|
|
48
|
+
if (process.argv.includes('--node') || process.argv.includes('-n')) {
|
|
49
|
+
console.log('Error: retire.js no longer supports scanning node packages. Use npm audit instead.');
|
|
50
50
|
process.exit(1);
|
|
51
51
|
}
|
|
52
52
|
/*
|
|
@@ -58,7 +58,7 @@ const prg = commander_1.program
|
|
|
58
58
|
.option('-c, --nocache', "Don't use local cache")
|
|
59
59
|
.option('--jspath <path>', 'Folder to scan for javascript files')
|
|
60
60
|
.option('--path <path>', 'Folder to scan for both')
|
|
61
|
-
.option('--jsrepo <path|url>',
|
|
61
|
+
.option('--jsrepo <path|url>', "Local or internal version of repo. Can be multiple comma separated. Default: 'central')")
|
|
62
62
|
.option('--cachedir <path>', 'Path to use for local cache instead of /tmp/.retire-cache')
|
|
63
63
|
.option('--proxy <url>', 'Proxy url (http://some.host:8080)')
|
|
64
64
|
.option('--outputformat <format>', 'Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx and cyclonedxJSON')
|
|
@@ -75,29 +75,30 @@ const prg = commander_1.program
|
|
|
75
75
|
.parse()
|
|
76
76
|
.opts();
|
|
77
77
|
const colorwarn = prg.colors ? ansi_colors_1.default.red : (x) => x;
|
|
78
|
-
const jsrepolocation = ((_a = prg.jsrepo) !== null && _a !== void 0 ? _a : "'central'")
|
|
79
|
-
.
|
|
78
|
+
const jsrepolocation = ((_a = prg.jsrepo) !== null && _a !== void 0 ? _a : "'central'")
|
|
79
|
+
.split(',')
|
|
80
|
+
.map((x) => x === "'central'" ? 'https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json' : x);
|
|
80
81
|
const ignorefile = (_b = prg.ignoreFile) !== null && _b !== void 0 ? _b : defaultIgnoreFiles.filter((x) => fs_1.default.existsSync(x))[0];
|
|
81
|
-
const scanpath = (_c = prg.path) !== null && _c !== void 0 ? _c :
|
|
82
|
+
const scanpath = (_c = prg.path) !== null && _c !== void 0 ? _c : '.';
|
|
82
83
|
const log = reporting.open({
|
|
83
84
|
colors: !!prg.colors,
|
|
84
85
|
colorwarn,
|
|
85
|
-
jsRepo: jsrepolocation.join(
|
|
86
|
+
jsRepo: jsrepolocation.join(', '),
|
|
86
87
|
outputformat: prg.outputformat,
|
|
87
88
|
outputpath: prg.outputpath,
|
|
88
89
|
path: scanpath,
|
|
89
90
|
verbose: !!prg.verbose,
|
|
90
91
|
});
|
|
91
|
-
const severity = (_d = prg.severity) !== null && _d !== void 0 ? _d :
|
|
92
|
+
const severity = (_d = prg.severity) !== null && _d !== void 0 ? _d : 'none';
|
|
92
93
|
if (!(severity in types_1.severityLevels)) {
|
|
93
94
|
exitWithError(`Error: Invalid severity level (${severity}). Valid levels are: ${Object.keys(types_1.severityLevels).join(', ')}`);
|
|
94
95
|
}
|
|
95
96
|
const config = {
|
|
96
97
|
path: scanpath,
|
|
97
98
|
ignore: {
|
|
98
|
-
paths: (_g = (_f = (_e = prg.ignore) === null || _e === void 0 ? void 0 : _e.split(
|
|
99
|
+
paths: (_g = (_f = (_e = prg.ignore) === null || _e === void 0 ? void 0 : _e.split(',')) === null || _f === void 0 ? void 0 : _f.map((x) => path_1.default.resolve(x))) !== null && _g !== void 0 ? _g : [],
|
|
99
100
|
pathsAsString: [],
|
|
100
|
-
descriptors: []
|
|
101
|
+
descriptors: [],
|
|
101
102
|
},
|
|
102
103
|
colorwarn,
|
|
103
104
|
nocache: prg.nocache ? true : false,
|
|
@@ -120,11 +121,15 @@ if (prg.cacert) {
|
|
|
120
121
|
}
|
|
121
122
|
config.cacertbuf = fs_1.default.readFileSync(prg.cacert);
|
|
122
123
|
}
|
|
123
|
-
const ignoreFileParser = z.array(z
|
|
124
|
-
|
|
125
|
-
|
|
124
|
+
const ignoreFileParser = z.array(z
|
|
125
|
+
.object({
|
|
126
|
+
justification: z.string(),
|
|
127
|
+
})
|
|
128
|
+
.and(z
|
|
129
|
+
.object({
|
|
126
130
|
path: z.string(),
|
|
127
|
-
})
|
|
131
|
+
})
|
|
132
|
+
.or(z.object({
|
|
128
133
|
component: z.string(),
|
|
129
134
|
version: z.string().optional(),
|
|
130
135
|
identifiers: z.record(z.string(), z.string()).optional(),
|
|
@@ -133,35 +138,41 @@ if (ignorefile) {
|
|
|
133
138
|
if (!fs_1.default.existsSync(ignorefile)) {
|
|
134
139
|
exitWithError(`Error: Could not read ignore file: ${ignorefile}`);
|
|
135
140
|
}
|
|
136
|
-
if (ignorefile.substr(-5) ===
|
|
141
|
+
if (ignorefile.substr(-5) === '.json') {
|
|
137
142
|
try {
|
|
138
|
-
config.ignore.descriptors = ignoreFileParser.parse(JSON.parse(fs_1.default.readFileSync(ignorefile,
|
|
143
|
+
config.ignore.descriptors = ignoreFileParser.parse(JSON.parse(fs_1.default.readFileSync(ignorefile, 'utf-8')));
|
|
139
144
|
}
|
|
140
145
|
catch (e) {
|
|
141
146
|
exitWithError(`Error: Invalid ignore file: ${ignorefile}`);
|
|
142
147
|
}
|
|
143
|
-
const ignoredPaths = (_m = (_l = (_k = config.ignore.descriptors) === null || _k === void 0 ? void 0 : _k.map((x) =>
|
|
148
|
+
const ignoredPaths = (_m = (_l = (_k = config.ignore.descriptors) === null || _k === void 0 ? void 0 : _k.map((x) => ('path' in x ? x.path : undefined))) === null || _l === void 0 ? void 0 : _l.filter((x) => x != undefined)) !== null && _m !== void 0 ? _m : [];
|
|
144
149
|
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignoredPaths);
|
|
145
150
|
}
|
|
146
151
|
else {
|
|
147
|
-
const lines = fs_1.default
|
|
148
|
-
|
|
152
|
+
const lines = fs_1.default
|
|
153
|
+
.readFileSync(ignorefile, 'utf-8')
|
|
154
|
+
.split(/\r\n|\n/g)
|
|
155
|
+
.filter((e) => e !== '');
|
|
156
|
+
const ignored = lines.map((e) => {
|
|
157
|
+
return e[0] === '@' ? e.slice(1) : path_1.default.resolve(e);
|
|
158
|
+
});
|
|
149
159
|
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignored);
|
|
150
160
|
}
|
|
151
161
|
}
|
|
152
162
|
config.ignore.paths = config.ignore.pathsAsString
|
|
153
|
-
.map(p => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
154
|
-
.map(p => p.replace(/[*]{1,2}/g, (a) => a.length == 2 ?
|
|
155
|
-
.map(s => new RegExp(s));
|
|
163
|
+
.map((p) => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
164
|
+
.map((p) => p.replace(/[*]{1,2}/g, (a) => (a.length == 2 ? '.*' : '[^/]*')))
|
|
165
|
+
.map((s) => new RegExp(s));
|
|
156
166
|
scanner.on('vulnerable-dependency-found', (result) => {
|
|
157
|
-
const levels = result.results
|
|
158
|
-
.
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
167
|
+
const levels = result.results.map((r) => {
|
|
168
|
+
return r.vulnerabilities
|
|
169
|
+
? r.vulnerabilities.map((v) => {
|
|
170
|
+
var _a;
|
|
171
|
+
return types_1.severityLevels[(_a = v.severity) !== null && _a !== void 0 ? _a : 'critical'];
|
|
172
|
+
})
|
|
173
|
+
: [];
|
|
163
174
|
});
|
|
164
|
-
const severity = utils.flatten(levels).reduce((x, y) => x > y ? x : y);
|
|
175
|
+
const severity = utils.flatten(levels).reduce((x, y) => (x > y ? x : y));
|
|
165
176
|
if (severity >= types_1.severityLevels[config.severity]) {
|
|
166
177
|
failProcess = true;
|
|
167
178
|
}
|
|
@@ -180,10 +191,10 @@ process.on('uncaughtException', (err, ...rest) => {
|
|
|
180
191
|
events.on('stop', (err) => {
|
|
181
192
|
exitWithError(err);
|
|
182
193
|
});
|
|
183
|
-
Promise.all(jsrepolocation.map((jsr) =>
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
194
|
+
Promise.all(jsrepolocation.map((jsr) => jsr.match(/^https?:\/\//) ? repo.loadrepository(jsr, config) : repo.loadrepositoryFromFile(jsr, config)))
|
|
195
|
+
.then((jsRepos) => {
|
|
196
|
+
resolve
|
|
197
|
+
.scanJsFiles(config.path, config)
|
|
187
198
|
.on('jsfile', (file) => {
|
|
188
199
|
jsRepos.forEach((jsRepo) => {
|
|
189
200
|
scanner.scanJsFile(file, jsRepo, config);
|
|
@@ -198,4 +209,5 @@ Promise.all(jsrepolocation.map((jsr) => (jsr.match(/^https?:\/\//)
|
|
|
198
209
|
.on('end', () => {
|
|
199
210
|
events.emit('scan-done');
|
|
200
211
|
});
|
|
201
|
-
})
|
|
212
|
+
})
|
|
213
|
+
.catch((e) => events.emit('stop', e));
|
package/lib/depsdev.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import { Options } from
|
|
2
|
-
import { Vulnerability } from
|
|
1
|
+
import { Options } from './types';
|
|
2
|
+
import { Vulnerability } from './types';
|
|
3
3
|
export declare function checkOSV(packageName: string, version: string, options: Options): Promise<Vulnerability[]>;
|
package/lib/depsdev.js
CHANGED
|
@@ -16,17 +16,22 @@ exports.checkOSV = void 0;
|
|
|
16
16
|
const https_1 = __importDefault(require("https"));
|
|
17
17
|
const retire_1 = require("./retire");
|
|
18
18
|
function loadJson(url, options) {
|
|
19
|
-
options.log.debug(
|
|
19
|
+
options.log.debug('Downloading ' + url + ' ...');
|
|
20
20
|
return new Promise((resolve, reject) => {
|
|
21
|
-
const req = https_1.default.request(url, res => {
|
|
21
|
+
const req = https_1.default.request(url, (res) => {
|
|
22
|
+
if (res.statusCode == 404)
|
|
23
|
+
return resolve(undefined);
|
|
24
|
+
if (res.statusCode != 200) {
|
|
25
|
+
return reject('HTTP ' + res.statusCode + ' ' + res.statusMessage + ' for ' + url);
|
|
26
|
+
}
|
|
22
27
|
const data = [];
|
|
23
|
-
res.on('data', c => data.push(c));
|
|
28
|
+
res.on('data', (c) => data.push(c));
|
|
24
29
|
res.on('end', () => {
|
|
25
30
|
const result = Buffer.concat(data).toString();
|
|
26
31
|
resolve(JSON.parse(result));
|
|
27
32
|
});
|
|
28
33
|
});
|
|
29
|
-
req.on('error', err => {
|
|
34
|
+
req.on('error', (err) => {
|
|
30
35
|
reject(err);
|
|
31
36
|
});
|
|
32
37
|
req.end();
|
|
@@ -37,42 +42,58 @@ function getVulnerabilities(packageName, version, options) {
|
|
|
37
42
|
}
|
|
38
43
|
function scoreToSeverity(score) {
|
|
39
44
|
if (score > 7)
|
|
40
|
-
return
|
|
45
|
+
return 'high';
|
|
41
46
|
if (score > 4)
|
|
42
|
-
return
|
|
43
|
-
return
|
|
47
|
+
return 'medium';
|
|
48
|
+
return 'low';
|
|
44
49
|
}
|
|
45
50
|
function loadAdvisory(packageName, version, id, options) {
|
|
46
51
|
return __awaiter(this, void 0, void 0, function* () {
|
|
47
52
|
const osvAdvisory = yield loadJson(`https://api.osv.dev/v1/vulns/${id}`, options);
|
|
48
53
|
const advisory = yield loadJson(`https://api.deps.dev/v3alpha/advisories/${id}`, options);
|
|
54
|
+
if (!advisory || !osvAdvisory)
|
|
55
|
+
return [];
|
|
49
56
|
const simplifiedRepo = {
|
|
50
57
|
[packageName]: {
|
|
51
|
-
vulnerabilities: osvAdvisory.affected
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
58
|
+
vulnerabilities: osvAdvisory.affected
|
|
59
|
+
.map(({ ranges }) => ranges.map(({ events }) => {
|
|
60
|
+
var _a, _b;
|
|
61
|
+
return ({
|
|
62
|
+
atOrAbove: events[0].introduced,
|
|
63
|
+
below: events[0].fixed,
|
|
64
|
+
severity: scoreToSeverity(advisory.cvss3Score),
|
|
65
|
+
cwe: (_b = (_a = osvAdvisory.database_specific) === null || _a === void 0 ? void 0 : _a.cwe_ids) !== null && _b !== void 0 ? _b : [],
|
|
66
|
+
identifiers: {
|
|
67
|
+
githubID: id,
|
|
68
|
+
CVE: osvAdvisory.aliases.filter((x) => x.startsWith('CVE-')),
|
|
69
|
+
summary: advisory.title,
|
|
70
|
+
},
|
|
71
|
+
info: osvAdvisory.references.map(({ url }) => url),
|
|
72
|
+
});
|
|
73
|
+
}))
|
|
74
|
+
.reduce((a, b) => a.concat(b), []),
|
|
75
|
+
extractors: {},
|
|
76
|
+
},
|
|
64
77
|
};
|
|
65
78
|
return (0, retire_1.check)(packageName, version, simplifiedRepo);
|
|
66
79
|
});
|
|
67
80
|
}
|
|
68
81
|
function checkOSV(packageName, version, options) {
|
|
69
82
|
return __awaiter(this, void 0, void 0, function* () {
|
|
70
|
-
|
|
71
|
-
|
|
83
|
+
try {
|
|
84
|
+
const versionInfo = yield getVulnerabilities(packageName, version, options);
|
|
85
|
+
if (!versionInfo)
|
|
86
|
+
return [];
|
|
87
|
+
if (versionInfo.advisoryKeys.length == 0)
|
|
88
|
+
return [];
|
|
89
|
+
const comps = yield Promise.all(versionInfo.advisoryKeys.map(({ id }) => loadAdvisory(packageName, version, id, options)));
|
|
90
|
+
const flattened = comps.reduce((a, b) => a.concat(b), []);
|
|
91
|
+
return flattened.map((x) => { var _a; return (_a = x.vulnerabilities) !== null && _a !== void 0 ? _a : []; }).reduce((a, b) => a.concat(b), []);
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
options.log.warn('Error checking OSV: ' + e);
|
|
72
95
|
return [];
|
|
73
|
-
|
|
74
|
-
const flattened = comps.reduce((a, b) => a.concat(b), []);
|
|
75
|
-
return flattened.map(x => { var _a; return (_a = x.vulnerabilities) !== null && _a !== void 0 ? _a : []; }).reduce((a, b) => a.concat(b), []);
|
|
96
|
+
}
|
|
76
97
|
});
|
|
77
98
|
}
|
|
78
99
|
exports.checkOSV = checkOSV;
|
package/lib/repo.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Options, Repository } from
|
|
1
|
+
import { Options, Repository } from './types';
|
|
2
2
|
export declare function asbowerrepo(jsRepo: Repository): Repository;
|
|
3
3
|
export declare function loadrepository(repoUrl: string, options: Options): Promise<Repository>;
|
|
4
4
|
export declare function loadrepositoryFromFile(filepath: string, options: Options): Promise<Repository>;
|
package/lib/repo.js
CHANGED
|
@@ -58,18 +58,18 @@ function loadJson(url, options) {
|
|
|
58
58
|
if (options.cacertbuf) {
|
|
59
59
|
reqOptions.ca = [options.cacertbuf];
|
|
60
60
|
}
|
|
61
|
-
const req = (url.startsWith(
|
|
61
|
+
const req = (url.startsWith('http:') ? http : https).get(reqOptions, (res) => {
|
|
62
62
|
if (res.statusCode != 200)
|
|
63
63
|
return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
|
|
64
64
|
const data = [];
|
|
65
|
-
res.on('data', c => data.push(c));
|
|
65
|
+
res.on('data', (c) => data.push(c));
|
|
66
66
|
res.on('end', () => {
|
|
67
67
|
let d = Buffer.concat(data).toString();
|
|
68
68
|
d = options.process ? options.process(d) : d;
|
|
69
69
|
resolve(JSON.parse(d));
|
|
70
70
|
});
|
|
71
71
|
});
|
|
72
|
-
req.on('error', e => reject(`Error downloading: ${url}: ${e}`));
|
|
72
|
+
req.on('error', (e) => reject(`Error downloading: ${url}: ${e}`));
|
|
73
73
|
req.end();
|
|
74
74
|
});
|
|
75
75
|
});
|
|
@@ -93,7 +93,7 @@ function loadFromCache(url, cachedir, options) {
|
|
|
93
93
|
const cacheIndex = path.resolve(cachedir, 'index.json');
|
|
94
94
|
if (!fs.existsSync(cachedir))
|
|
95
95
|
fs.mkdirSync(cachedir);
|
|
96
|
-
const cache = fs.existsSync(cacheIndex) ? JSON.parse(fs.readFileSync(cacheIndex,
|
|
96
|
+
const cache = fs.existsSync(cacheIndex) ? JSON.parse(fs.readFileSync(cacheIndex, 'utf-8')) : {};
|
|
97
97
|
const now = new Date().getTime();
|
|
98
98
|
if (cache[url]) {
|
|
99
99
|
if (now - cache[url].date < 60 * 60 * 1000) {
|
|
@@ -106,11 +106,11 @@ function loadFromCache(url, cachedir, options) {
|
|
|
106
106
|
fs.unlinkSync(path.resolve(cachedir, cache[url].date + '.json'));
|
|
107
107
|
}
|
|
108
108
|
catch (error) {
|
|
109
|
-
if (error != null && typeof error ==
|
|
109
|
+
if (error != null && typeof error == 'object' && 'code' in error && error.code !== 'ENOENT') {
|
|
110
110
|
throw error;
|
|
111
111
|
}
|
|
112
112
|
else {
|
|
113
|
-
console.warn(
|
|
113
|
+
console.warn('Could not delete cache. Ignore this error if you are running multiple retire.js in parallel');
|
|
114
114
|
}
|
|
115
115
|
}
|
|
116
116
|
}
|
|
@@ -126,7 +126,7 @@ function loadFromCache(url, cachedir, options) {
|
|
|
126
126
|
function asbowerrepo(jsRepo) {
|
|
127
127
|
const result = {};
|
|
128
128
|
Object.keys(jsRepo).map((k) => {
|
|
129
|
-
|
|
129
|
+
[jsRepo[k].bowername || k].map((b) => {
|
|
130
130
|
result[b] = result[b] || { vulnerabilities: [] };
|
|
131
131
|
result[b].vulnerabilities = result[b].vulnerabilities.concat(jsRepo[k].vulnerabilities);
|
|
132
132
|
});
|
|
@@ -134,7 +134,6 @@ function asbowerrepo(jsRepo) {
|
|
|
134
134
|
return result;
|
|
135
135
|
}
|
|
136
136
|
exports.asbowerrepo = asbowerrepo;
|
|
137
|
-
;
|
|
138
137
|
function loadrepository(repoUrl, options) {
|
|
139
138
|
return __awaiter(this, void 0, void 0, function* () {
|
|
140
139
|
//options = utils.extend(options, { process : retire.replaceVersion });
|
|
@@ -146,7 +145,6 @@ function loadrepository(repoUrl, options) {
|
|
|
146
145
|
});
|
|
147
146
|
}
|
|
148
147
|
exports.loadrepository = loadrepository;
|
|
149
|
-
;
|
|
150
148
|
function loadrepositoryFromFile(filepath, options) {
|
|
151
149
|
return __awaiter(this, void 0, void 0, function* () {
|
|
152
150
|
options = Object.assign(Object.assign({}, options), { process: retire.replaceVersion });
|
|
@@ -154,4 +152,3 @@ function loadrepositoryFromFile(filepath, options) {
|
|
|
154
152
|
});
|
|
155
153
|
}
|
|
156
154
|
exports.loadrepositoryFromFile = loadrepositoryFromFile;
|
|
157
|
-
;
|
package/lib/reporters/console.js
CHANGED
|
@@ -53,9 +53,12 @@ function printVulnerability(component, config) {
|
|
|
53
53
|
string += `severity: ${vulnerability.severity}; `;
|
|
54
54
|
}
|
|
55
55
|
if (vulnerability.identifiers) {
|
|
56
|
-
string +=
|
|
57
|
-
|
|
58
|
-
|
|
56
|
+
string +=
|
|
57
|
+
Object.entries(vulnerability.identifiers)
|
|
58
|
+
.map(([name, id]) => {
|
|
59
|
+
return `${name}: ${utils.flatten([Array.isArray(id) ? id : [id]]).join(' ')}`;
|
|
60
|
+
})
|
|
61
|
+
.join(', ') + '; ';
|
|
59
62
|
}
|
|
60
63
|
string += vulnerability.info.join(config.outputformat === 'clean' ? '\n' : ' ');
|
|
61
64
|
});
|
|
@@ -63,8 +66,12 @@ function printVulnerability(component, config) {
|
|
|
63
66
|
}
|
|
64
67
|
exports.default = {
|
|
65
68
|
configure: (logger, _, config) => {
|
|
66
|
-
logger.logDependency = (finding) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
69
|
+
logger.logDependency = (finding) => {
|
|
70
|
+
if (config.verbose)
|
|
71
|
+
printResults(logger, finding, config);
|
|
72
|
+
};
|
|
73
|
+
logger.logVulnerableDependency = (component) => {
|
|
74
|
+
printResults(logger, component, config);
|
|
75
|
+
};
|
|
76
|
+
},
|
|
70
77
|
};
|
|
@@ -34,10 +34,14 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
34
34
|
start: new Date().toISOString(),
|
|
35
35
|
data: [],
|
|
36
36
|
messages: [],
|
|
37
|
-
errors: []
|
|
37
|
+
errors: [],
|
|
38
38
|
};
|
|
39
39
|
logger.info = finalResults.messages.push;
|
|
40
|
-
logger.debug = config.verbose
|
|
40
|
+
logger.debug = config.verbose
|
|
41
|
+
? finalResults.messages.push
|
|
42
|
+
: function () {
|
|
43
|
+
return;
|
|
44
|
+
};
|
|
41
45
|
logger.warn = logger.error = finalResults.errors.push;
|
|
42
46
|
logger.logVulnerableDependency = function (finding) {
|
|
43
47
|
vulnsFound = true;
|
|
@@ -51,17 +55,20 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
51
55
|
logger.close = function (callback) {
|
|
52
56
|
const write = vulnsFound ? writer.err : writer.out;
|
|
53
57
|
const seen = new Set();
|
|
54
|
-
const components = finalResults.data
|
|
55
|
-
|
|
58
|
+
const components = finalResults.data
|
|
59
|
+
.filter((d) => d.results)
|
|
60
|
+
.map((r) => r.results
|
|
61
|
+
.map((dep) => {
|
|
62
|
+
dep.version = (dep.version.split('.').length >= 3 ? dep.version : dep.version + '.0').replace(/-/g, '.');
|
|
56
63
|
let hashes;
|
|
57
64
|
const filepath = r.file;
|
|
58
65
|
if (filepath) {
|
|
59
66
|
const file = fs.readFileSync(filepath);
|
|
60
67
|
hashes = [
|
|
61
|
-
{
|
|
62
|
-
{
|
|
63
|
-
{
|
|
64
|
-
{
|
|
68
|
+
{ alg: 'MD5', content: hash.md5(file) },
|
|
69
|
+
{ alg: 'SHA-1', content: hash.sha1(file) },
|
|
70
|
+
{ alg: 'SHA-256', content: hash.sha256(file) },
|
|
71
|
+
{ alg: 'SHA-512', content: hash.sha512(file) },
|
|
65
72
|
];
|
|
66
73
|
}
|
|
67
74
|
const purl = `pkg:npm/${dep.component}@${dep.version}`;
|
|
@@ -69,33 +76,35 @@ function configureCycloneDXJSONLogger(logger, writer, config, hash) {
|
|
|
69
76
|
return undefined;
|
|
70
77
|
seen.add(purl);
|
|
71
78
|
return {
|
|
72
|
-
type:
|
|
79
|
+
type: 'library',
|
|
73
80
|
name: dep.component,
|
|
74
81
|
version: dep.version,
|
|
75
82
|
purl: purl,
|
|
76
|
-
hashes: hashes
|
|
83
|
+
hashes: hashes,
|
|
77
84
|
};
|
|
78
|
-
})
|
|
85
|
+
})
|
|
86
|
+
.filter((x) => x != undefined))
|
|
87
|
+
.reduce((a, b) => a.concat(b));
|
|
79
88
|
write(JSON.stringify({
|
|
80
|
-
bomFormat:
|
|
81
|
-
specVersion:
|
|
89
|
+
bomFormat: 'CycloneDX',
|
|
90
|
+
specVersion: '1.4',
|
|
82
91
|
serialNumber: `urn:uuid:${(0, uuid_1.v4)()}`,
|
|
83
92
|
version: 1,
|
|
84
93
|
metadata: {
|
|
85
94
|
timestamp: finalResults.start,
|
|
86
95
|
tools: [
|
|
87
96
|
{
|
|
88
|
-
vendor:
|
|
89
|
-
name:
|
|
90
|
-
version: retire.version
|
|
91
|
-
}
|
|
92
|
-
]
|
|
97
|
+
vendor: 'RetireJS',
|
|
98
|
+
name: 'retire.js',
|
|
99
|
+
version: retire.version,
|
|
100
|
+
},
|
|
101
|
+
],
|
|
93
102
|
},
|
|
94
|
-
components: components
|
|
103
|
+
components: components,
|
|
95
104
|
}, undefined, 2));
|
|
96
105
|
writer.close(callback);
|
|
97
106
|
};
|
|
98
107
|
}
|
|
99
108
|
exports.default = {
|
|
100
|
-
configure: configureCycloneDXJSONLogger
|
|
109
|
+
configure: configureCycloneDXJSONLogger,
|
|
101
110
|
};
|
|
@@ -34,10 +34,14 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
34
34
|
start: new Date(),
|
|
35
35
|
data: [],
|
|
36
36
|
messages: [],
|
|
37
|
-
errors: []
|
|
37
|
+
errors: [],
|
|
38
38
|
};
|
|
39
39
|
logger.info = finalResults.messages.push;
|
|
40
|
-
logger.debug = config.verbose
|
|
40
|
+
logger.debug = config.verbose
|
|
41
|
+
? finalResults.messages.push
|
|
42
|
+
: () => {
|
|
43
|
+
return;
|
|
44
|
+
};
|
|
41
45
|
logger.warn = logger.error = finalResults.errors.push;
|
|
42
46
|
logger.logVulnerableDependency = function (finding) {
|
|
43
47
|
vulnsFound = true;
|
|
@@ -51,10 +55,13 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
51
55
|
logger.close = function (callback) {
|
|
52
56
|
const write = vulnsFound ? writer.err : writer.out;
|
|
53
57
|
const seen = new Set();
|
|
54
|
-
const components = finalResults.data
|
|
55
|
-
|
|
58
|
+
const components = finalResults.data
|
|
59
|
+
.filter((d) => d.results)
|
|
60
|
+
.map((r) => r.results
|
|
61
|
+
.map((dep) => {
|
|
62
|
+
dep.version = (dep.version.split('.').length >= 3 ? dep.version : dep.version + '.0').replace(/-/g, '.');
|
|
56
63
|
const filepath = r.file;
|
|
57
|
-
let hashes =
|
|
64
|
+
let hashes = '';
|
|
58
65
|
if (filepath) {
|
|
59
66
|
const file = fs.readFileSync(filepath);
|
|
60
67
|
hashes = `
|
|
@@ -76,7 +83,9 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
76
83
|
<purl>${purl}</purl>
|
|
77
84
|
<modified>false</modified>
|
|
78
85
|
</component>`;
|
|
79
|
-
})
|
|
86
|
+
})
|
|
87
|
+
.join(''))
|
|
88
|
+
.join('');
|
|
80
89
|
write(`<?xml version="1.0"?>
|
|
81
90
|
<bom xmlns="http://cyclonedx.org/schema/bom/1.4" serialNumber="urn:uuid:${(0, uuid_1.v4)()}" version="1">
|
|
82
91
|
<metadata>
|
|
@@ -96,5 +105,5 @@ function configureCycloneDXLogger(logger, writer, config, hash) {
|
|
|
96
105
|
};
|
|
97
106
|
}
|
|
98
107
|
exports.default = {
|
|
99
|
-
configure: configureCycloneDXLogger
|
|
108
|
+
configure: configureCycloneDXLogger,
|
|
100
109
|
};
|