retire 3.2.4 → 4.0.1
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 +12 -2
- package/README.md +1 -9
- package/lib/cli.d.ts +2 -0
- package/lib/cli.js +195 -0
- package/lib/repo.d.ts +4 -0
- package/lib/repo.js +148 -103
- package/lib/reporters/console.d.ts +3 -0
- package/lib/reporters/console.js +66 -39
- package/lib/reporters/cyclonedx-json.d.ts +3 -0
- package/lib/reporters/cyclonedx-json.js +71 -42
- package/lib/reporters/cyclonedx.d.ts +3 -0
- package/lib/reporters/cyclonedx.js +62 -34
- package/lib/reporters/depcheck.d.ts +3 -0
- package/lib/reporters/depcheck.js +79 -51
- package/lib/reporters/json.d.ts +3 -0
- package/lib/reporters/json.js +56 -28
- package/lib/reporting.d.ts +36 -0
- package/lib/reporting.js +107 -88
- package/lib/resolve.d.ts +4 -0
- package/lib/resolve.js +52 -100
- package/lib/retire.js +1 -10
- package/lib/scanner.d.ts +4 -0
- package/lib/scanner.js +113 -119
- package/lib/types.d.ts +83 -0
- package/lib/types.js +10 -0
- package/lib/utils.d.ts +11 -0
- package/lib/utils.js +27 -53
- package/package.json +31 -10
- package/bin/retire +0 -236
package/CHANGELOG.md
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## [
|
|
3
|
+
## [4.0.1]
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
- 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
|
|
7
|
+
|
|
8
|
+
## [4.0.0]
|
|
9
|
+
|
|
10
|
+
### Breaking changes
|
|
11
|
+
- npm scanning no longer supported
|
|
12
|
+
|
|
13
|
+
### Changes
|
|
14
|
+
- Complete rewrite to typescript
|
|
4
15
|
|
|
5
|
-
- Bump vulnerable deps
|
|
6
16
|
|
|
7
17
|
## [3.2.3]
|
|
8
18
|
|
package/README.md
CHANGED
|
@@ -13,22 +13,13 @@ Usage
|
|
|
13
13
|
Usage: retire [options]
|
|
14
14
|
|
|
15
15
|
Options:
|
|
16
|
-
|
|
17
|
-
-h, --help output usage information
|
|
18
16
|
-V, --version output the version number
|
|
19
|
-
|
|
20
|
-
-p, --package limit node scan to packages where parent is mentioned in package.json (ignore node_modules)
|
|
21
|
-
-n, --node Run node dependency scan only
|
|
22
|
-
-j, --js Run scan of JavaScript files only
|
|
23
17
|
-v, --verbose Show identified files (by default only vulnerable files are shown)
|
|
24
18
|
-x, --dropexternal Don't include project provided vulnerability repository
|
|
25
19
|
-c, --nocache Don't use local cache
|
|
26
|
-
|
|
27
20
|
--jspath <path> Folder to scan for javascript files
|
|
28
|
-
--nodepath <path> Folder to scan for node files
|
|
29
21
|
--path <path> Folder to scan for both
|
|
30
22
|
--jsrepo <path|url> Local or internal version of repo
|
|
31
|
-
--noderepo <path|url> Local or internal version of repo
|
|
32
23
|
--cachedir <path> Path to use for local cache instead of /tmp/.retire-cache
|
|
33
24
|
--proxy <url> Proxy url (http://some.host:8080)
|
|
34
25
|
--outputformat <format> Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx and cyclonedxJSON
|
|
@@ -41,6 +32,7 @@ Options:
|
|
|
41
32
|
--insecure Enable fetching remote jsrepo/noderepo files from hosts using an insecure or self-signed SSL (TLS) certificate
|
|
42
33
|
--ext <extensions> Comman separated list of file extensions for javascript files. The default is "js"
|
|
43
34
|
--cacert <path> Use the specified certificate file to verify the peer used for fetching remote jsrepo/noderepo files
|
|
35
|
+
-h, --help display help for command
|
|
44
36
|
````
|
|
45
37
|
|
|
46
38
|
The `depcheck` output format mimics the output of OWASP Dependency Check, but lacks some information compared to OWASP Dependency Check, because that information is not in the repo.
|
package/lib/cli.d.ts
ADDED
package/lib/cli.js
ADDED
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
4
|
+
if (k2 === undefined) k2 = k;
|
|
5
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
6
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
7
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
8
|
+
}
|
|
9
|
+
Object.defineProperty(o, k2, desc);
|
|
10
|
+
}) : (function(o, m, k, k2) {
|
|
11
|
+
if (k2 === undefined) k2 = k;
|
|
12
|
+
o[k2] = m[k];
|
|
13
|
+
}));
|
|
14
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
15
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
16
|
+
}) : function(o, v) {
|
|
17
|
+
o["default"] = v;
|
|
18
|
+
});
|
|
19
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
20
|
+
if (mod && mod.__esModule) return mod;
|
|
21
|
+
var result = {};
|
|
22
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
23
|
+
__setModuleDefault(result, mod);
|
|
24
|
+
return result;
|
|
25
|
+
};
|
|
26
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
27
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
28
|
+
};
|
|
29
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
|
|
30
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
31
|
+
const utils = __importStar(require("./utils"));
|
|
32
|
+
const commander_1 = require("commander");
|
|
33
|
+
const retire = __importStar(require("./retire"));
|
|
34
|
+
const repo = __importStar(require("./repo"));
|
|
35
|
+
const resolve = __importStar(require("./resolve"));
|
|
36
|
+
const scanner = __importStar(require("./scanner"));
|
|
37
|
+
const reporting = __importStar(require("./reporting"));
|
|
38
|
+
const os_1 = __importDefault(require("os"));
|
|
39
|
+
const path_1 = __importDefault(require("path"));
|
|
40
|
+
const fs_1 = __importDefault(require("fs"));
|
|
41
|
+
const ansi_colors_1 = __importDefault(require("ansi-colors"));
|
|
42
|
+
const events_1 = require("events");
|
|
43
|
+
const types_1 = require("./types");
|
|
44
|
+
const z = __importStar(require("zod"));
|
|
45
|
+
const events = new events_1.EventEmitter();
|
|
46
|
+
let failProcess = false;
|
|
47
|
+
const defaultIgnoreFiles = ['.retireignore', '.retireignore.json'];
|
|
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
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
/*
|
|
53
|
+
* Parse command line flags.
|
|
54
|
+
*/
|
|
55
|
+
const prg = commander_1.program
|
|
56
|
+
.version(retire.version)
|
|
57
|
+
.option('-v, --verbose', 'Show identified files (by default only vulnerable files are shown)')
|
|
58
|
+
.option('-x, --dropexternal', "Don't include project provided vulnerability repository")
|
|
59
|
+
.option('-c, --nocache', "Don't use local cache")
|
|
60
|
+
.option('--jspath <path>', 'Folder to scan for javascript files')
|
|
61
|
+
.option('--path <path>', 'Folder to scan for both')
|
|
62
|
+
.option('--jsrepo <path|url>', 'Local or internal version of repo')
|
|
63
|
+
.option('--cachedir <path>', 'Path to use for local cache instead of /tmp/.retire-cache')
|
|
64
|
+
.option('--proxy <url>', 'Proxy url (http://some.host:8080)')
|
|
65
|
+
.option('--outputformat <format>', 'Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx and cyclonedxJSON')
|
|
66
|
+
.option('--outputpath <path>', 'File to which output should be written')
|
|
67
|
+
.option('--ignore <paths>', 'Comma delimited list of paths to ignore')
|
|
68
|
+
.option('--ignorefile <path>', 'Custom ignore file, defaults to .retireignore / .retireignore.json')
|
|
69
|
+
.option('--severity <level>', 'Specify the bug severity level from which the process fails. Allowed levels none, low, medium, high, critical. Default: none')
|
|
70
|
+
.option('--exitwith <code>', 'Custom exit code (default: 13) when vulnerabilities are found')
|
|
71
|
+
.option('--colors', 'Enable color output (console output only)')
|
|
72
|
+
.option('--insecure', 'Enable fetching remote jsrepo/noderepo files from hosts using an insecure or self-signed SSL (TLS) certificate')
|
|
73
|
+
.option('--ext <extensions>', 'Comman separated list of file extensions for javascript files. The default is "js"')
|
|
74
|
+
.option('--cacert <path>', 'Use the specified certificate file to verify the peer used for fetching remote jsrepo/noderepo files')
|
|
75
|
+
.parse()
|
|
76
|
+
.opts();
|
|
77
|
+
const colorwarn = prg.colors ? ansi_colors_1.default.red : (x) => x;
|
|
78
|
+
const jsrepolocation = (_a = prg.jsrepo) !== null && _a !== void 0 ? _a : "https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json";
|
|
79
|
+
const ignorefile = (_b = prg.ignoreFile) !== null && _b !== void 0 ? _b : defaultIgnoreFiles.filter((x) => fs_1.default.existsSync(x))[0];
|
|
80
|
+
const scanpath = (_c = prg.path) !== null && _c !== void 0 ? _c : ".";
|
|
81
|
+
const log = reporting.open({
|
|
82
|
+
colors: !!prg.colors,
|
|
83
|
+
colorwarn,
|
|
84
|
+
jsRepo: jsrepolocation,
|
|
85
|
+
outputformat: prg.outputformat,
|
|
86
|
+
outputpath: prg.outputpath,
|
|
87
|
+
path: scanpath,
|
|
88
|
+
verbose: !!prg.verbose
|
|
89
|
+
});
|
|
90
|
+
const severity = (_d = prg.severity) !== null && _d !== void 0 ? _d : "none";
|
|
91
|
+
if (!(severity in types_1.severityLevels)) {
|
|
92
|
+
exitWithError(`Error: Invalid severity level (${severity}). Valid levels are: ${Object.keys(types_1.severityLevels).join(', ')}`);
|
|
93
|
+
}
|
|
94
|
+
const config = {
|
|
95
|
+
path: scanpath,
|
|
96
|
+
ignore: {
|
|
97
|
+
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 : [],
|
|
98
|
+
pathsAsString: [],
|
|
99
|
+
descriptors: []
|
|
100
|
+
},
|
|
101
|
+
colorwarn,
|
|
102
|
+
nocache: prg.nocache ? true : false,
|
|
103
|
+
cachedir: (_h = prg.cachedir) !== null && _h !== void 0 ? _h : path_1.default.resolve(os_1.default.tmpdir(), '.retire-cache/'),
|
|
104
|
+
log: log,
|
|
105
|
+
severity: severity,
|
|
106
|
+
exitwith: (_j = prg.exitwith) !== null && _j !== void 0 ? _j : 13
|
|
107
|
+
};
|
|
108
|
+
log.info(`retire.js v${retire.version}`);
|
|
109
|
+
function exitWithError(msg) {
|
|
110
|
+
log.error(config.colorwarn(msg));
|
|
111
|
+
process.exitCode = 1;
|
|
112
|
+
log.close();
|
|
113
|
+
}
|
|
114
|
+
if (prg.cacert) {
|
|
115
|
+
if (!fs_1.default.existsSync(prg.cacert)) {
|
|
116
|
+
exitWithError(`Error: Could not read cacert file: ${prg.cacert}`);
|
|
117
|
+
}
|
|
118
|
+
config.cacertbuf = fs_1.default.readFileSync(prg.cacert);
|
|
119
|
+
}
|
|
120
|
+
const ignoreFileParser = z.array(z.object({
|
|
121
|
+
justification: z.string()
|
|
122
|
+
}).and(z.object({
|
|
123
|
+
path: z.string(),
|
|
124
|
+
}).or(z.object({
|
|
125
|
+
component: z.string(),
|
|
126
|
+
version: z.string().optional(),
|
|
127
|
+
identifiers: z.record(z.string(), z.string()).optional(),
|
|
128
|
+
}))));
|
|
129
|
+
if (ignorefile) {
|
|
130
|
+
if (!fs_1.default.existsSync(ignorefile)) {
|
|
131
|
+
exitWithError(`Error: Could not read ignore file: ${ignorefile}`);
|
|
132
|
+
}
|
|
133
|
+
if (ignorefile.substr(-5) === ".json") {
|
|
134
|
+
try {
|
|
135
|
+
config.ignore.descriptors = ignoreFileParser.parse(JSON.parse(fs_1.default.readFileSync(ignorefile, "utf-8")));
|
|
136
|
+
}
|
|
137
|
+
catch (e) {
|
|
138
|
+
exitWithError(`Error: Invalid ignore file: ${ignorefile}`);
|
|
139
|
+
}
|
|
140
|
+
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 : [];
|
|
141
|
+
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignoredPaths);
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
const lines = fs_1.default.readFileSync(ignorefile, "utf-8").split(/\r\n|\n/g).filter((e) => e !== '');
|
|
145
|
+
const ignored = lines.map(e => { return e[0] === '@' ? e.slice(1) : path_1.default.resolve(e); });
|
|
146
|
+
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignored);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
config.ignore.paths = config.ignore.pathsAsString
|
|
150
|
+
.map(p => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
151
|
+
.map(p => p.replace(/[*]{1,2}/g, (a) => a.length == 2 ? ".*" : "[^/]*"))
|
|
152
|
+
.map(s => new RegExp(s));
|
|
153
|
+
scanner.on('vulnerable-dependency-found', (result) => {
|
|
154
|
+
const levels = result.results
|
|
155
|
+
.map((r) => {
|
|
156
|
+
return r.vulnerabilities ? r.vulnerabilities.map((v) => {
|
|
157
|
+
var _a;
|
|
158
|
+
return types_1.severityLevels[(_a = v.severity) !== null && _a !== void 0 ? _a : 'critical'];
|
|
159
|
+
}) : [];
|
|
160
|
+
});
|
|
161
|
+
const severity = utils.flatten(levels).reduce((x, y) => x > y ? x : y);
|
|
162
|
+
if (severity >= types_1.severityLevels[config.severity]) {
|
|
163
|
+
failProcess = true;
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
scanner.on('vulnerable-dependency-found', log.logVulnerableDependency);
|
|
167
|
+
scanner.on('dependency-found', log.logDependency);
|
|
168
|
+
events.on('scan-done', () => {
|
|
169
|
+
process.exitCode = failProcess ? config.exitwith : 0;
|
|
170
|
+
log.close();
|
|
171
|
+
});
|
|
172
|
+
process.on('uncaughtException', (err, ...rest) => {
|
|
173
|
+
console.warn('Exception caught: ', err, rest);
|
|
174
|
+
console.warn(err.stack);
|
|
175
|
+
process.exit(1);
|
|
176
|
+
});
|
|
177
|
+
events.on('stop', (err) => {
|
|
178
|
+
exitWithError(err);
|
|
179
|
+
});
|
|
180
|
+
(jsrepolocation.match(/^https?:\/\//)
|
|
181
|
+
? repo.loadrepository(jsrepolocation, config)
|
|
182
|
+
: repo.loadrepositoryFromFile(jsrepolocation, config))
|
|
183
|
+
.then((jsRepo) => {
|
|
184
|
+
resolve.scanJsFiles(config.path, config)
|
|
185
|
+
.on('jsfile', (file) => {
|
|
186
|
+
scanner.scanJsFile(file, jsRepo, config);
|
|
187
|
+
})
|
|
188
|
+
.on('bowerfile', (bowerfile) => {
|
|
189
|
+
const bowerRepo = repo.asbowerrepo(jsRepo);
|
|
190
|
+
scanner.scanBowerFile(bowerfile, bowerRepo, config);
|
|
191
|
+
})
|
|
192
|
+
.on('end', () => {
|
|
193
|
+
events.emit('scan-done');
|
|
194
|
+
});
|
|
195
|
+
}).catch((e) => events.emit('stop', e));
|
package/lib/repo.d.ts
ADDED
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import { Options, Repository } from "./types";
|
|
2
|
+
export declare function asbowerrepo(jsRepo: Repository): Repository;
|
|
3
|
+
export declare function loadrepository(repoUrl: string, options: Options): Promise<Repository>;
|
|
4
|
+
export declare function loadrepositoryFromFile(filepath: string, options: Options): Promise<Repository>;
|
package/lib/repo.js
CHANGED
|
@@ -1,112 +1,157 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
var
|
|
14
|
-
|
|
15
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.loadrepositoryFromFile = exports.loadrepository = exports.asbowerrepo = void 0;
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const path = __importStar(require("path"));
|
|
41
|
+
const http = __importStar(require("http"));
|
|
42
|
+
const https = __importStar(require("https"));
|
|
43
|
+
const retire = __importStar(require("./retire"));
|
|
44
|
+
const URL = __importStar(require("url"));
|
|
45
|
+
const proxy_agent_1 = __importDefault(require("proxy-agent"));
|
|
16
46
|
function loadJson(url, options) {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
47
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
48
|
+
return new Promise((resolve, reject) => {
|
|
49
|
+
options.log.info('Downloading ' + url + ' ...');
|
|
50
|
+
const reqOptions = Object.assign(Object.assign({}, URL.parse(url)), { method: 'GET' });
|
|
51
|
+
const proxyUri = options.proxy || process.env.http_proxy;
|
|
52
|
+
if (proxyUri) {
|
|
53
|
+
reqOptions.agent = new proxy_agent_1.default(proxyUri);
|
|
54
|
+
}
|
|
55
|
+
if (options.insecure) {
|
|
56
|
+
reqOptions.rejectUnauthorized = false;
|
|
57
|
+
}
|
|
58
|
+
if (options.cacertbuf) {
|
|
59
|
+
reqOptions.ca = [options.cacertbuf];
|
|
60
|
+
}
|
|
61
|
+
const req = (url.startsWith("http:") ? http : https).get(reqOptions, (res) => {
|
|
62
|
+
if (res.statusCode != 200)
|
|
63
|
+
return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
|
|
64
|
+
const data = [];
|
|
65
|
+
res.on('data', c => data.push(c));
|
|
66
|
+
res.on('end', () => {
|
|
67
|
+
let d = Buffer.concat(data).toString();
|
|
68
|
+
d = options.process ? options.process(d) : d;
|
|
69
|
+
resolve(JSON.parse(d));
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
req.on('error', e => reject(`Error downloading: ${url}: ${e}`));
|
|
73
|
+
req.end();
|
|
74
|
+
});
|
|
38
75
|
});
|
|
39
|
-
});
|
|
40
|
-
req.on('error', e => events.emit('stop', 'Error downloading: ' + url + ": " + e.toString()));
|
|
41
|
-
req.end();
|
|
42
|
-
return events;
|
|
43
76
|
}
|
|
44
|
-
|
|
45
77
|
function loadJsonFromFile(file, options) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
options.log.debug('Reading ' + file + ' ...');
|
|
80
|
+
return new Promise((resolve, reject) => {
|
|
81
|
+
fs.readFile(file, { encoding: 'utf8' }, (err, data) => {
|
|
82
|
+
if (err) {
|
|
83
|
+
return reject(err.toString());
|
|
84
|
+
}
|
|
85
|
+
data = options.process ? options.process(data) : data;
|
|
86
|
+
resolve(JSON.parse(data));
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
});
|
|
55
90
|
}
|
|
56
|
-
|
|
57
91
|
function loadFromCache(url, cachedir, options) {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
92
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
93
|
+
const cacheIndex = path.resolve(cachedir, 'index.json');
|
|
94
|
+
if (!fs.existsSync(cachedir))
|
|
95
|
+
fs.mkdirSync(cachedir);
|
|
96
|
+
const cache = fs.existsSync(cacheIndex) ? JSON.parse(fs.readFileSync(cacheIndex, "utf-8")) : {};
|
|
97
|
+
const now = new Date().getTime();
|
|
98
|
+
if (cache[url]) {
|
|
99
|
+
if (now - cache[url].date < 60 * 60 * 1000) {
|
|
100
|
+
options.log.info('Loading from cache: ' + url);
|
|
101
|
+
return loadJsonFromFile(path.resolve(cachedir, cache[url].file), options);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
if (fs.existsSync(path.resolve(cachedir, cache[url].date + '.json'))) {
|
|
105
|
+
try {
|
|
106
|
+
fs.unlinkSync(path.resolve(cachedir, cache[url].date + '.json'));
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (error != null && typeof error == "object" && "code" in error && error.code !== 'ENOENT') {
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
else {
|
|
113
|
+
console.warn("Could not delete cache. Ignore this error if you are running multiple retire.js in parallel");
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
76
118
|
}
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
fs.writeFileSync(path.resolve(cachedir, cache[url].file), JSON.stringify(data), { encoding : 'utf8' });
|
|
84
|
-
fs.writeFileSync(cacheIndex, JSON.stringify(cache), { encoding : 'utf8' });
|
|
85
|
-
events.emit('done', data);
|
|
86
|
-
}).on('stop', forward(events, 'stop'));
|
|
87
|
-
return events;
|
|
119
|
+
const data = yield loadJson(url, options);
|
|
120
|
+
cache[url] = { date: now, file: now + '.json' };
|
|
121
|
+
fs.writeFileSync(path.resolve(cachedir, cache[url].file), JSON.stringify(data), { encoding: 'utf8' });
|
|
122
|
+
fs.writeFileSync(cacheIndex, JSON.stringify(cache), { encoding: 'utf8' });
|
|
123
|
+
return data;
|
|
124
|
+
});
|
|
88
125
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
126
|
+
function asbowerrepo(jsRepo) {
|
|
127
|
+
const result = {};
|
|
128
|
+
Object.keys(jsRepo).map((k) => {
|
|
129
|
+
([jsRepo[k].bowername || k]).map((b) => {
|
|
130
|
+
result[b] = result[b] || { vulnerabilities: [] };
|
|
131
|
+
result[b].vulnerabilities = result[b].vulnerabilities.concat(jsRepo[k].vulnerabilities);
|
|
132
|
+
});
|
|
96
133
|
});
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
134
|
+
return result;
|
|
135
|
+
}
|
|
136
|
+
exports.asbowerrepo = asbowerrepo;
|
|
137
|
+
;
|
|
138
|
+
function loadrepository(repoUrl, options) {
|
|
139
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
140
|
+
//options = utils.extend(options, { process : retire.replaceVersion });
|
|
141
|
+
options = Object.assign(Object.assign({}, options), { process: retire.replaceVersion });
|
|
142
|
+
if (options.nocache) {
|
|
143
|
+
return yield loadJson(repoUrl, options);
|
|
144
|
+
}
|
|
145
|
+
return yield loadFromCache(repoUrl, options.cachedir, options);
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
exports.loadrepository = loadrepository;
|
|
149
|
+
;
|
|
150
|
+
function loadrepositoryFromFile(filepath, options) {
|
|
151
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
152
|
+
options = Object.assign(Object.assign({}, options), { process: retire.replaceVersion });
|
|
153
|
+
return yield loadJsonFromFile(filepath, options);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
exports.loadrepositoryFromFile = loadrepositoryFromFile;
|
|
157
|
+
;
|
package/lib/reporters/console.js
CHANGED
|
@@ -1,43 +1,70 @@
|
|
|
1
|
-
|
|
2
|
-
var
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
const retire = __importStar(require("../retire"));
|
|
27
|
+
const utils = __importStar(require("../utils"));
|
|
5
28
|
function printResults(logger, finding, config) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
29
|
+
if (finding.results && finding.results.length > 0) {
|
|
30
|
+
const logFunc = retire.isVulnerable(finding.results) ? logger.warn : logger.info;
|
|
31
|
+
const printed = new Set();
|
|
32
|
+
finding.results.forEach((elm) => {
|
|
33
|
+
if (!config.verbose && !retire.isVulnerable([elm]))
|
|
34
|
+
return;
|
|
35
|
+
const key = `${elm.component} ${elm.version}`;
|
|
36
|
+
logFunc(finding.file);
|
|
37
|
+
logFunc(` ${String.fromCharCode(8627)} ${key}`);
|
|
38
|
+
if (printed.has(key))
|
|
39
|
+
return;
|
|
40
|
+
if (retire.isVulnerable([elm])) {
|
|
41
|
+
logFunc(`${key} has known vulnerabilities:${printVulnerability(elm, config)}`);
|
|
42
|
+
}
|
|
43
|
+
printed.add(key);
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
function printVulnerability(component, config) {
|
|
48
|
+
var _a;
|
|
49
|
+
let string = '';
|
|
50
|
+
(_a = component.vulnerabilities) === null || _a === void 0 ? void 0 : _a.forEach((vulnerability) => {
|
|
51
|
+
string += config.outputformat === 'clean' ? '\n ' : ' ';
|
|
52
|
+
if (vulnerability.severity) {
|
|
53
|
+
string += `severity: ${vulnerability.severity}; `;
|
|
54
|
+
}
|
|
55
|
+
if (vulnerability.identifiers) {
|
|
56
|
+
string += Object.entries(vulnerability.identifiers).map(([id, name]) => {
|
|
57
|
+
return `${name}: ${utils.flatten([[id]]).join(' ')}`;
|
|
58
|
+
}).join(', ') + '; ';
|
|
59
|
+
}
|
|
60
|
+
string += vulnerability.info.join(config.outputformat === 'clean' ? '\n' : ' ');
|
|
19
61
|
});
|
|
20
|
-
|
|
62
|
+
return string;
|
|
21
63
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
if (vulnerability.severity) {
|
|
28
|
-
string += 'severity: ' + vulnerability.severity + '; ';
|
|
64
|
+
exports.default = {
|
|
65
|
+
configure: (logger, _, config) => {
|
|
66
|
+
logger.logDependency = (finding) => { if (config.verbose)
|
|
67
|
+
printResults(logger, finding, config); };
|
|
68
|
+
logger.logVulnerableDependency = (component) => { printResults(logger, component, config); };
|
|
29
69
|
}
|
|
30
|
-
|
|
31
|
-
string += utils.map(vulnerability.identifiers, function(id, name) {
|
|
32
|
-
return name + ': ' + utils.flatten([id]).join(' ');
|
|
33
|
-
}).join(', ') + '; ';
|
|
34
|
-
}
|
|
35
|
-
string += vulnerability.info.join(config.outputformat === 'clean' ? '\n' : ' ');
|
|
36
|
-
});
|
|
37
|
-
return string;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
exports.configure = function(logger, writer, config, hash) {
|
|
41
|
-
logger.logDependency = function(finding) { if (config.verbose) printResults(logger, finding, config); };
|
|
42
|
-
logger.logVulnerableDependency = function(component) { printResults(logger, component, config); };
|
|
43
|
-
};
|
|
70
|
+
};
|