retire 3.2.3 → 4.0.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/lib/types.d.ts ADDED
@@ -0,0 +1,83 @@
1
+ /// <reference types="node" />
2
+ import { Logger } from "./reporting";
3
+ export type Repository = Record<string, {
4
+ bowername?: string;
5
+ vulnerabilities: Vulnerability[];
6
+ extractors: {
7
+ func?: string[];
8
+ uri?: string[];
9
+ filename?: string[];
10
+ filecontent?: string[];
11
+ filecontentreplace?: string[];
12
+ hashes?: Record<string, string>;
13
+ };
14
+ }>;
15
+ export type Vulnerability = {
16
+ below: string;
17
+ atOrAbove?: string;
18
+ severity: SeverityLevel;
19
+ identifiers?: {
20
+ CVE?: string[];
21
+ bug?: string;
22
+ issue?: string;
23
+ summary?: string;
24
+ };
25
+ info: string[];
26
+ };
27
+ export type Component = {
28
+ component: string;
29
+ version: string;
30
+ vulnerabilities?: Vulnerability[];
31
+ };
32
+ export type Finding = {
33
+ results: Component[];
34
+ file: string;
35
+ };
36
+ export type Hasher = {
37
+ sha1: (data: string) => string;
38
+ };
39
+ export declare const severityLevels: {
40
+ readonly none: 0;
41
+ readonly low: 1;
42
+ readonly medium: 2;
43
+ readonly high: 3;
44
+ readonly critical: 4;
45
+ };
46
+ export type SeverityLevel = keyof typeof severityLevels;
47
+ export type PathDescriptor = {
48
+ path: string;
49
+ justification?: string;
50
+ };
51
+ export type ComponentDescriptor = {
52
+ component: string;
53
+ version?: string;
54
+ severity?: string;
55
+ identifiers?: Vulnerability["identifiers"];
56
+ justification?: string;
57
+ };
58
+ type Descriptor = (PathDescriptor | ComponentDescriptor);
59
+ export type Options = {
60
+ log: Logger;
61
+ proxy?: string;
62
+ insecure?: boolean;
63
+ cacertbuf?: Buffer;
64
+ process?: (data: string) => string;
65
+ nocache: boolean;
66
+ cachedir: string;
67
+ ext?: string;
68
+ ignore: {
69
+ descriptors?: Array<Descriptor>;
70
+ paths: RegExp[];
71
+ pathsAsString: string[];
72
+ };
73
+ severity: SeverityLevel;
74
+ verbose?: boolean;
75
+ outputformat?: string;
76
+ outputpath?: string;
77
+ colors?: boolean;
78
+ colorwarn: (msg: string) => string;
79
+ jsRepo?: string;
80
+ path: string;
81
+ exitwith: number;
82
+ };
83
+ export {};
package/lib/types.js ADDED
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.severityLevels = void 0;
4
+ exports.severityLevels = {
5
+ none: 0,
6
+ low: 1,
7
+ medium: 2,
8
+ high: 3,
9
+ critical: 4
10
+ };
package/lib/utils.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ type LogMethod = (...message: unknown[]) => void;
2
+ export declare function info(options: {
3
+ logger?: LogMethod;
4
+ }): (...message: unknown[]) => void;
5
+ export declare function warn(options: {
6
+ warnlogger?: LogMethod;
7
+ logger?: LogMethod;
8
+ }): (...message: unknown[]) => void;
9
+ export declare function pick(p: Record<string, unknown>, keys: string[]): Record<typeof keys[number], unknown>;
10
+ export declare function flatten<T>(e: T[][]): T[];
11
+ export {};
package/lib/utils.js CHANGED
@@ -1,57 +1,31 @@
1
-
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.flatten = exports.pick = exports.warn = exports.info = void 0;
2
4
  function info(options) {
3
- return function(message) {
4
- (options.logger || console.log)(message);
5
- };
5
+ return function (...message) {
6
+ (options.logger || console.log)(message);
7
+ };
6
8
  }
7
-
9
+ exports.info = info;
8
10
  function warn(options) {
9
- return function(message) {
10
- (options.warnlogger || options.logger || console.warn)(message);
11
- };
11
+ return function (...message) {
12
+ (options.warnlogger || options.logger || console.warn)(message);
13
+ };
12
14
  }
13
-
14
- exports.pick = function(p, keys) {
15
- var result = {};
16
- keys.forEach(function(k) {
17
- if (p.hasOwnProperty(k)) {
18
- result[k] = p[k];
19
- }
20
- });
21
- return result;
22
- };
23
-
24
- exports.extend = function(o, a) {
25
- var result = exports.pick(o, Object.keys(o));
26
- exports.map(a, function(v,k){ result[k] = v; });
27
- return result;
28
- };
29
-
30
- exports.map = function(o, fn) {
31
- return Object.keys(o).map(function(k) { return fn(o[k], k); });
32
- };
33
-
34
- exports.find = function(ar, fn) {
35
- for(var i in ar) {
36
- if (fn(ar[i])) return ar[i];
37
- }
38
- return undefined;
39
- };
40
-
41
- exports.detect = exports.find;
42
-
43
- exports.flatten = function(e) {
44
- return e.reduce(function(x,y) { return x.concat(y); }, []);
45
- };
46
-
47
- exports.forwardEvent = function(emitter, evt) {
48
- return function() {
49
- emitter.emit(evt, arguments[0]);
50
- };
51
- };
52
-
53
- exports.every = function(things, predicate){
54
- return Object.keys(things)
55
- .map(function(k) { return predicate(k, things[k]); })
56
- .reduce(function(x,y) { return x && y; }, true);
57
- };
15
+ exports.warn = warn;
16
+ function pick(p, keys) {
17
+ const result = {};
18
+ keys.forEach((k) => {
19
+ if (k in p) {
20
+ result[k] = p[k];
21
+ }
22
+ });
23
+ return result;
24
+ }
25
+ exports.pick = pick;
26
+ ;
27
+ function flatten(e) {
28
+ return e.reduce((x, y) => x.concat(y), []);
29
+ }
30
+ exports.flatten = flatten;
31
+ ;
package/package.json CHANGED
@@ -2,38 +2,59 @@
2
2
  "author": "Erlend Oftedal <erlend@oftedal.no>",
3
3
  "name": "retire",
4
4
  "description": "Retire is a tool for detecting use of vulnerable libraries",
5
- "version": "3.2.3",
5
+ "version": "4.0.0",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
9
9
  "url": "https://github.com/RetireJS/retire.js.git"
10
10
  },
11
11
  "bin": {
12
- "retire": "./bin/retire"
12
+ "retire": "./lib/cli.js"
13
13
  },
14
14
  "main": "./lib/retire.js",
15
15
  "dependencies": {
16
16
  "ansi-colors": "^4.1.1",
17
- "commander": "2.5.x",
17
+ "commander": "^10.0.0",
18
18
  "proxy-agent": "^5.0.0",
19
- "read-installed": "^4.0.3",
20
19
  "uuid": "^8.3.2",
21
- "walkdir": "0.4.1"
20
+ "walkdir": "0.4.1",
21
+ "zod": "^3.20.6"
22
22
  },
23
23
  "devDependencies": {
24
+ "@types/node": "^18.13.0",
25
+ "@types/uuid": "^9.0.0",
26
+ "@typescript-eslint/eslint-plugin": "^5.51.0",
27
+ "@typescript-eslint/parser": "^5.51.0",
24
28
  "chai": "^4.3.4",
25
- "jshint": "^2.13.4",
29
+ "eslint": "^8.34.0",
30
+ "eslint-config-prettier": "^8.6.0",
26
31
  "jsonschema": "^1.4.1",
27
- "libxmljs": "^0.19.10",
28
- "mocha": "^9.2.0"
32
+ "libxmljs": "^1.0.8",
33
+ "mocha": "^9.2.0",
34
+ "prettier": "^2.8.4",
35
+ "typescript": "^4.9.5"
29
36
  },
30
37
  "scripts": {
31
- "test": "./test"
38
+ "test": "./test",
39
+ "build": "tsc && chmod ugo+x lib/cli.js",
40
+ "watch": "tsc --watch ",
41
+ "lint": "eslint . --ext .ts --fix --ignore-path ../.gitignore",
42
+ "check": "npm run lint && npm run typecheck"
32
43
  },
33
44
  "engines": {
34
45
  "node": ">= 6.0.0"
35
46
  },
36
47
  "keywords": [
37
- "sbom", "sbom-tool", "sbom-generator", "security", "cli", "software-composition-analysis", "sca"
48
+ "sbom",
49
+ "sbom-tool",
50
+ "sbom-generator",
51
+ "security",
52
+ "cli",
53
+ "software-composition-analysis",
54
+ "sca"
55
+ ],
56
+ "files" : [
57
+ "lib/**/*",
58
+ "CHANGELOG.md"
38
59
  ]
39
60
  }
package/bin/retire DELETED
@@ -1,236 +0,0 @@
1
- #!/usr/bin/env node
2
- /*jshint esversion: 6 */
3
-
4
- var utils = require('../lib/utils'),
5
- program = require('commander'),
6
- retire = require('../lib/retire'),
7
- repo = require('../lib/repo'),
8
- resolve = require('../lib/resolve'),
9
- scanner = require('../lib/scanner'),
10
- reporting = require('../lib/reporting'),
11
- forward = require('../lib/utils').forwardEvent,
12
- os = require('os'),
13
- path = require('path'),
14
- fs = require('fs'),
15
- colors = require('ansi-colors'),
16
- emitter = new require('events').EventEmitter;
17
-
18
- var events = new emitter();
19
- var jsRepo = null;
20
- var bowerRepo = null;
21
- var nodeRepo = null;
22
- var failProcess = false;
23
- var defaultIgnoreFiles = ['.retireignore', '.retireignore.json'];
24
-
25
- var severityLevels = {
26
- none: 0,
27
- low: 1,
28
- medium: 2,
29
- high: 3,
30
- critical: 4
31
- };
32
-
33
-
34
-
35
- /*
36
- * Parse command line flags.
37
- */
38
- program
39
- .version(retire.version)
40
- .option('')
41
- .option('-p, --package', 'limit node scan to packages where parent is mentioned in package.json (ignore node_modules)')
42
- .option('-n, --node', 'Run node dependency scan only')
43
- .option('-j, --js', 'Run scan of JavaScript files only')
44
- .option('-v, --verbose', 'Show identified files (by default only vulnerable files are shown)')
45
- .option('-x, --dropexternal', "Don't include project provided vulnerability repository")
46
- .option('-c, --nocache', "Don't use local cache")
47
- .option('')
48
- .option('--jspath <path>', 'Folder to scan for javascript files')
49
- .option('--nodepath <path>', 'Folder to scan for node files')
50
- .option('--path <path>', 'Folder to scan for both')
51
- .option('--jsrepo <path|url>', 'Local or internal version of repo')
52
- .option('--noderepo <path|url>', 'Local or internal version of repo')
53
- .option('--cachedir <path>', 'Path to use for local cache instead of /tmp/.retire-cache')
54
- .option('--proxy <url>', 'Proxy url (http://some.host:8080)')
55
- .option('--outputformat <format>', 'Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx and cyclonedxJSON')
56
- .option('--outputpath <path>', 'File to which output should be written')
57
- .option('--ignore <paths>', 'Comma delimited list of paths to ignore')
58
- .option('--ignorefile <path>', 'Custom ignore file, defaults to .retireignore / .retireignore.json')
59
- .option('--severity <level>', 'Specify the bug severity level from which the process fails. Allowed levels none, low, medium, high, critical. Default: none')
60
- .option('--exitwith <code>', 'Custom exit code (default: 13) when vulnerabilities are found')
61
- .option('--colors', 'Enable color output (console output only)')
62
- .option('--insecure', 'Enable fetching remote jsrepo/noderepo files from hosts using an insecure or self-signed SSL (TLS) certificate')
63
- .option('--ext <extensions>', 'Comman separated list of file extensions for javascript files. The default is "js"')
64
- .option('--cacert <path>', 'Use the specified certificate file to verify the peer used for fetching remote jsrepo/noderepo files')
65
- .parse(process.argv);
66
-
67
- var config = utils.extend({ path: '.' }, utils.pick(program, [
68
- 'package', 'node', 'js', 'jspath', 'verbose', 'nodepath', 'path', 'jsrepo', 'noderepo',
69
- 'dropexternal', 'nocache', 'proxy', 'ignore', 'ignorefile', 'outputformat', 'outputpath',
70
- 'severity', 'exitwith', 'colors', 'includemeta', 'cachedir', 'insecure', 'cacert', 'ext'
71
- ]));
72
-
73
- if (!config.nocache && !config.cachedir) {
74
- config.cachedir = path.resolve(os.tmpdir(), '.retire-cache/');
75
- }
76
-
77
- config.ignore = config.ignore ? utils.map(config.ignore.split(','), function(e) { return path.resolve(e); }) : [];
78
- config.ignore = { paths : config.ignore, descriptors: [] };
79
- config.colorwarn = config.colors ? colors.red : x => x;
80
-
81
- if (!config.ignorefile) {
82
- config.ignorefile = defaultIgnoreFiles.filter(function(x){ return fs.existsSync(x); })[0];
83
- }
84
- var log = reporting.open(config);
85
- config.log = log;
86
- log.info("retire.js v" + retire.version);
87
-
88
- function exitWithError(msg) {
89
- log.error(config.colorwarn(msg));
90
- process.exitCode = 1;
91
- log.close();
92
- }
93
-
94
-
95
- if(!config.severity) {
96
- config.severity = 'none';
97
- } else if (!severityLevels.hasOwnProperty(config.severity)) {
98
- exitWithError('Error: Invalid severity level (' + config.severity + '). Valid levels are: ' + Object.keys(severityLevels).join(', '));
99
- }
100
-
101
- if(config.cacert) {
102
- if (!fs.existsSync(config.cacert)) {
103
- exitWithError('Error: Could not read cacert file: ' + config.cacert);
104
- }
105
- config.cacertbuf = fs.readFileSync(config.cacert);
106
- }
107
-
108
- if(config.ignorefile) {
109
- if (!fs.existsSync(config.ignorefile)) {
110
- exitWithError('Error: Could not read ignore file: ' + config.ignorefile);
111
- }
112
- if (config.ignorefile.substr(-5) === ".json") {
113
- try {
114
- var ignored = JSON.parse(fs.readFileSync(config.ignorefile).toString());
115
- } catch(e) {
116
- exitWithError('Error: Invalid ignore file: ' + config.ignorefile, e);
117
- }
118
- config.ignore.descriptors = ignored;
119
- var ignoredPaths = ignored
120
- .map(function(x) { return x.path; })
121
- .filter(function(x) { return x; });
122
- config.ignore.paths = config.ignore.paths.concat(ignoredPaths);
123
- } else {
124
- var lines = fs.readFileSync(config.ignorefile).toString().split(/\r\n|\n/g).filter(function(e) { return e !== ''; });
125
- ignored = utils.map(lines, function(e) { return e[0] === '@' ? e.slice(1) : path.resolve(e); });
126
- config.ignore.paths = config.ignore.paths.concat(ignored);
127
- }
128
- }
129
- config.ignore.paths = config.ignore.paths
130
- .map(p => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
131
- .map(p => p.replace(/[*]{1,2}/g, (a) => a.length == 2 ? ".*" : "[^/]*"))
132
- .map(s => new RegExp(s)
133
- );
134
-
135
- scanner.on('vulnerable-dependency-found', function(result) {
136
- vulnsFound = true;
137
- var levels = result.results
138
- .map(function(r) {
139
- return r.vulnerabilities ? r.vulnerabilities.map(function(v) {
140
- return severityLevels[v.severity || 'critical'];
141
- }) : []; });
142
- var severity = utils.flatten(levels).reduce(function(x,y) { return x > y ? x : y; });
143
- if(severity >= severityLevels[config.severity]) {
144
- failProcess = true;
145
- }
146
- });
147
-
148
- scanner.on('vulnerable-dependency-found', log.logVulnerableDependency);
149
- scanner.on('dependency-found', log.logDependency);
150
-
151
-
152
- events.on('load-js-repo', function() {
153
- (config.jsrepo ?
154
- (config.jsrepo.match(/^https?:\/\//) ?
155
- repo.loadrepository(config.jsrepo, config)
156
- : repo.loadrepositoryFromFile(config.jsrepo, config))
157
- : repo.loadrepository('https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json', config)
158
- ).on('stop', forward(events, 'stop'))
159
- .on('done', function(repo) {
160
- jsRepo = repo;
161
- events.emit('js-repo-loaded');
162
- });
163
- });
164
-
165
-
166
- events.on('load-node-repo', function() {
167
- (config.noderepo ?
168
- (config.noderepo.match(/^https?:\/\//) ?
169
- repo.loadrepository(config.noderepo, config)
170
- : repo.loadrepositoryFromFile(config.noderepo, config))
171
- : repo.loadrepository('https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/npmrepository.json', config)
172
- ).on('done', function(repo) {
173
- nodeRepo = repo;
174
- events.emit('node-repo-loaded');
175
- }).on('stop', forward(events, 'stop'));
176
- });
177
-
178
- events.on('js-repo-loaded', function() {
179
- events.emit(config.js ? 'scan-js' : 'load-node-repo');
180
- });
181
-
182
- events.on('node-repo-loaded', function() {
183
- events.emit(config.node ? 'scan-node' : 'scan-js');
184
- });
185
-
186
-
187
- events.on('scan-js', function() {
188
- resolve.scanJsFiles(config.jspath || config.path, config)
189
- .on('jsfile', function(file) {
190
- scanner.scanJsFile(file, jsRepo, config);
191
- })
192
- .on('bowerfile', function(bowerfile) {
193
- bowerRepo = bowerRepo || repo.asbowerrepo(jsRepo);
194
- scanner.scanBowerFile(bowerfile, bowerRepo, config);
195
- })
196
- .on('end', function() {
197
- events.emit('js-scanned');
198
- });
199
- });
200
-
201
- events.on('scan-node', function() {
202
- console.warn('DEPRECATION NOTICE: The node scanning is deprecated and will be removed soon. See https://github.com/RetireJS/retire.js/wiki/Deprecating-the-node.js-scanner ');
203
- resolve.getNodeDependencies(config.nodepath || config.path, config.package).on('done', function(dependencies) {
204
- scanner.scanDependencies(dependencies, nodeRepo, config);
205
- events.emit('scan-done');
206
- }).on('error', function(err) {
207
- console.warn("ERROR: " + err);
208
- process.exit(1);
209
- });
210
- });
211
-
212
- events.on('js-scanned', function() {
213
- events.emit(!config.js ? 'scan-node' : 'scan-done');
214
- });
215
-
216
- events.on('scan-done', function() {
217
- process.exitCode = failProcess ? (config.exitwith || 13) : 0;
218
- log.close();
219
- });
220
-
221
-
222
- process.on('uncaughtException', function (err) {
223
- console.warn('Exception caught: ', arguments);
224
- console.warn(err.stack);
225
- process.exit(1);
226
- });
227
-
228
- events.on('stop', function() {
229
- exitWithError.apply(null, arguments);
230
- });
231
-
232
- if (config.node) {
233
- events.emit('load-node-repo');
234
- } else {
235
- events.emit('load-js-repo');
236
- }