retire 4.3.4 → 4.4.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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [4.4.1]
4
+
5
+ ### Chore
6
+
7
+ - Dependency upgrades
8
+
9
+ ## [4.4.0]
10
+
11
+ ### Added
12
+
13
+ - Adding proper repository validation to ensure it's on the expected format
14
+
3
15
  ## [4.3.4]
4
16
 
5
17
  ### Bugfix
package/lib/cli.js CHANGED
@@ -26,7 +26,6 @@ var __importStar = (this && this.__importStar) || function (mod) {
26
26
  var __importDefault = (this && this.__importDefault) || function (mod) {
27
27
  return (mod && mod.__esModule) ? mod : { "default": mod };
28
28
  };
29
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o;
30
29
  Object.defineProperty(exports, "__esModule", { value: true });
31
30
  const utils = __importStar(require("./utils"));
32
31
  const commander_1 = require("commander");
@@ -75,11 +74,11 @@ const prg = commander_1.program
75
74
  .parse()
76
75
  .opts();
77
76
  const colorwarn = prg.colors ? ansi_colors_1.default.red : (x) => x;
78
- const jsrepolocation = ((_a = prg.jsrepo) !== null && _a !== void 0 ? _a : "'central'")
77
+ const jsrepolocation = (prg.jsrepo ?? "'central'")
79
78
  .split(',')
80
79
  .map((x) => x === "'central'" ? 'https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json' : x);
81
- const ignorefile = (_b = prg.ignoreFile) !== null && _b !== void 0 ? _b : defaultIgnoreFiles.filter((x) => fs_1.default.existsSync(x))[0];
82
- const scanpath = (_d = (_c = prg.path) !== null && _c !== void 0 ? _c : prg.jspath) !== null && _d !== void 0 ? _d : '.';
80
+ const ignorefile = prg.ignoreFile ?? defaultIgnoreFiles.filter((x) => fs_1.default.existsSync(x))[0];
81
+ const scanpath = prg.path ?? prg.jspath ?? '.';
83
82
  const log = reporting.open({
84
83
  colors: !!prg.colors,
85
84
  colorwarn,
@@ -89,7 +88,7 @@ const log = reporting.open({
89
88
  path: scanpath,
90
89
  verbose: !!prg.verbose,
91
90
  });
92
- const severity = (_e = prg.severity) !== null && _e !== void 0 ? _e : 'none';
91
+ const severity = prg.severity ?? 'none';
93
92
  if (!(severity in types_1.severityLevels)) {
94
93
  exitWithError(`Error: Invalid severity level (${severity}). Valid levels are: ${Object.keys(types_1.severityLevels).join(', ')}`);
95
94
  }
@@ -97,15 +96,15 @@ const config = {
97
96
  path: scanpath,
98
97
  ignore: {
99
98
  paths: [],
100
- pathsAsString: (_h = (_g = (_f = prg.ignore) === null || _f === void 0 ? void 0 : _f.split(',')) === null || _g === void 0 ? void 0 : _g.map((x) => path_1.default.resolve(x))) !== null && _h !== void 0 ? _h : [],
99
+ pathsAsString: prg.ignore?.split(',')?.map((x) => path_1.default.resolve(x)) ?? [],
101
100
  descriptors: [],
102
101
  },
103
102
  colorwarn,
104
103
  nocache: prg.nocache ? true : false,
105
- cachedir: (_j = prg.cachedir) !== null && _j !== void 0 ? _j : path_1.default.resolve(os_1.default.tmpdir(), '.retire-cache/'),
104
+ cachedir: prg.cachedir ?? path_1.default.resolve(os_1.default.tmpdir(), '.retire-cache/'),
106
105
  log: log,
107
106
  severity: severity,
108
- exitwith: (_k = prg.exitwith) !== null && _k !== void 0 ? _k : 13,
107
+ exitwith: prg.exitwith ?? 13,
109
108
  includeOsv: !!prg.includeOsv,
110
109
  verbose: !!prg.verbose,
111
110
  proxy: prg.proxy,
@@ -146,7 +145,9 @@ if (ignorefile) {
146
145
  catch (e) {
147
146
  exitWithError(`Error: Invalid ignore file: ${ignorefile}`);
148
147
  }
149
- const ignoredPaths = (_o = (_m = (_l = config.ignore.descriptors) === null || _l === void 0 ? void 0 : _l.map((x) => ('path' in x ? x.path : undefined))) === null || _m === void 0 ? void 0 : _m.filter((x) => x != undefined)) !== null && _o !== void 0 ? _o : [];
148
+ const ignoredPaths = config.ignore.descriptors
149
+ ?.map((x) => ('path' in x ? x.path : undefined))
150
+ ?.filter((x) => x != undefined) ?? [];
150
151
  config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignoredPaths);
151
152
  }
152
153
  else {
@@ -168,8 +169,7 @@ scanner.on('vulnerable-dependency-found', (result) => {
168
169
  const levels = result.results.map((r) => {
169
170
  return r.vulnerabilities
170
171
  ? r.vulnerabilities.map((v) => {
171
- var _a;
172
- return types_1.severityLevels[(_a = v.severity) !== null && _a !== void 0 ? _a : 'critical'];
172
+ return types_1.severityLevels[v.severity ?? 'critical'];
173
173
  })
174
174
  : [];
175
175
  });
package/lib/depsdev.js CHANGED
@@ -1,13 +1,4 @@
1
1
  "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
2
  var __importDefault = (this && this.__importDefault) || function (mod) {
12
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
4
  };
@@ -47,53 +38,46 @@ function scoreToSeverity(score) {
47
38
  return 'medium';
48
39
  return 'low';
49
40
  }
50
- function loadAdvisory(packageName, version, id, options) {
51
- return __awaiter(this, void 0, void 0, function* () {
52
- const osvAdvisory = yield loadJson(`https://api.osv.dev/v1/vulns/${id}`, options);
53
- const advisory = yield loadJson(`https://api.deps.dev/v3alpha/advisories/${id}`, options);
54
- if (!advisory || !osvAdvisory)
55
- return [];
56
- const simplifiedRepo = {
57
- [packageName]: {
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
- },
77
- };
78
- return (0, retire_1.check)(packageName, version, simplifiedRepo);
79
- });
41
+ async function loadAdvisory(packageName, version, id, options) {
42
+ const osvAdvisory = await loadJson(`https://api.osv.dev/v1/vulns/${id}`, options);
43
+ const advisory = await loadJson(`https://api.deps.dev/v3alpha/advisories/${id}`, options);
44
+ if (!advisory || !osvAdvisory)
45
+ return [];
46
+ const simplifiedRepo = {
47
+ [packageName]: {
48
+ vulnerabilities: osvAdvisory.affected
49
+ .map(({ ranges }) => ranges.map(({ events }) => ({
50
+ atOrAbove: events[0].introduced,
51
+ below: events[0].fixed,
52
+ severity: scoreToSeverity(advisory.cvss3Score),
53
+ cwe: osvAdvisory.database_specific?.cwe_ids ?? [],
54
+ identifiers: {
55
+ githubID: id,
56
+ CVE: osvAdvisory.aliases.filter((x) => x.startsWith('CVE-')),
57
+ summary: advisory.title,
58
+ },
59
+ info: osvAdvisory.references.map(({ url }) => url),
60
+ })))
61
+ .reduce((a, b) => a.concat(b), []),
62
+ extractors: {},
63
+ },
64
+ };
65
+ return (0, retire_1.check)(packageName, version, simplifiedRepo);
80
66
  }
81
- function checkOSV(packageName, version, options) {
82
- return __awaiter(this, void 0, void 0, function* () {
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);
67
+ async function checkOSV(packageName, version, options) {
68
+ try {
69
+ const versionInfo = await getVulnerabilities(packageName, version, options);
70
+ if (!versionInfo)
95
71
  return [];
96
- }
97
- });
72
+ if (versionInfo.advisoryKeys.length == 0)
73
+ return [];
74
+ const comps = await Promise.all(versionInfo.advisoryKeys.map(({ id }) => loadAdvisory(packageName, version, id, options)));
75
+ const flattened = comps.reduce((a, b) => a.concat(b), []);
76
+ return flattened.map((x) => x.vulnerabilities ?? []).reduce((a, b) => a.concat(b), []);
77
+ }
78
+ catch (e) {
79
+ options.log.warn('Error checking OSV: ' + e);
80
+ return [];
81
+ }
98
82
  }
99
83
  exports.checkOSV = checkOSV;
package/lib/repo.d.ts CHANGED
@@ -1,4 +1,6 @@
1
1
  import { Options, Repository } from './types';
2
+ import * as z from 'zod';
3
+ export declare function validateRepository(repo: Repository, replacer?: Options['process']): z.SafeParseReturnType<unknown, Repository>;
2
4
  export declare function asbowerrepo(jsRepo: Repository): Repository;
3
5
  export declare function loadrepository(repoUrl: string, options: Options): Promise<Repository>;
4
6
  export declare function loadrepositoryFromFile(filepath: string, options: Options): Promise<Repository>;
package/lib/repo.js CHANGED
@@ -22,17 +22,8 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
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
25
  Object.defineProperty(exports, "__esModule", { value: true });
35
- exports.loadrepositoryFromFile = exports.loadrepository = exports.asbowerrepo = void 0;
26
+ exports.loadrepositoryFromFile = exports.loadrepository = exports.asbowerrepo = exports.validateRepository = void 0;
36
27
  const fs = __importStar(require("fs"));
37
28
  const path = __importStar(require("path"));
38
29
  const http = __importStar(require("http"));
@@ -40,87 +31,218 @@ const https = __importStar(require("https"));
40
31
  const retire = __importStar(require("./retire"));
41
32
  const URL = __importStar(require("url"));
42
33
  const proxy_agent_1 = require("proxy-agent");
43
- function loadJson(url, options) {
44
- return __awaiter(this, void 0, void 0, function* () {
45
- return new Promise((resolve, reject) => {
46
- options.log.info('Downloading ' + url + ' ...');
47
- const reqOptions = Object.assign(Object.assign({}, URL.parse(url)), { method: 'GET' });
48
- const proxyUri = options.proxy || process.env.http_proxy;
49
- if (proxyUri) {
50
- reqOptions.agent = new proxy_agent_1.ProxyAgent({
51
- getProxyForUrl: () => proxyUri,
52
- });
53
- }
54
- if (options.insecure) {
55
- reqOptions.rejectUnauthorized = false;
56
- }
57
- if (options.cacertbuf) {
58
- reqOptions.ca = [options.cacertbuf];
59
- }
60
- const req = (url.startsWith('http:') ? http : https).get(reqOptions, (res) => {
61
- if (res.statusCode != 200)
62
- return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
63
- const data = [];
64
- res.on('data', (c) => data.push(c));
65
- res.on('end', () => {
66
- let d = Buffer.concat(data).toString();
67
- d = options.process ? options.process(d) : d;
68
- resolve(JSON.parse(d));
69
- });
34
+ const z = __importStar(require("zod"));
35
+ const types_1 = require("./types");
36
+ function validateRepository(repo, replacer) {
37
+ const keys = Object.keys(types_1.severityLevels);
38
+ const versionValidator = z.string().regex(/^[\d.]+([a-zA-Z\d.-]+)?$/);
39
+ const numericString = z.string().regex(/^[\d]+$/);
40
+ const vulnValidator = z
41
+ .object({
42
+ below: versionValidator,
43
+ atOrAbove: versionValidator.optional(),
44
+ severity: z.enum(keys),
45
+ cwe: z.array(z.string().regex(/^CWE-[0-9]+$/)).min(1),
46
+ identifiers: z
47
+ .object({
48
+ CVE: z.array(z.string().regex(/^CVE-[0-9X-]+$/)).optional(),
49
+ bug: z
50
+ .string()
51
+ .regex(/^[a-z0-9-]+$/i)
52
+ .optional(),
53
+ issue: numericString.optional(),
54
+ summary: z.string().min(3).optional(),
55
+ githubID: z
56
+ .string()
57
+ .regex(/^GHSA[A-Z0-9-]+$/i)
58
+ .optional(),
59
+ osvdb: z.array(numericString).optional(),
60
+ gist: z
61
+ .string()
62
+ .regex(/^[a-z0-9-]+\/[a-f0-9]+$/i)
63
+ .optional(),
64
+ tenable: numericString.optional(),
65
+ blog: z
66
+ .string()
67
+ .min(10)
68
+ .regex(/^[:a-z0-9/-]+$/)
69
+ .optional(),
70
+ release: z.string().min(5).optional(),
71
+ PR: numericString.optional(),
72
+ retid: z
73
+ .string()
74
+ .regex(/^[\d]+$/)
75
+ .optional(),
76
+ })
77
+ .strict()
78
+ .superRefine((o, ctx) => {
79
+ if (Object.keys(o).filter((k) => k != 'summary').length == 0)
80
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Must have at least one identifier' });
81
+ const ids = Object.values(o)
82
+ .map((x) => (Array.isArray(x) ? x : [x]))
83
+ .reduce((a, b) => a.concat(b), []).length;
84
+ if (ids == 0)
85
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Must have at least one identifier' });
86
+ }),
87
+ info: z.array(z.string().regex(/^https?:\/\/.+/)),
88
+ })
89
+ .strict();
90
+ const regexValidator = z.string().superRefine((s, ctx) => {
91
+ if (ctx.path[0] == 'dont check')
92
+ return;
93
+ try {
94
+ new RegExp(s);
95
+ }
96
+ catch (error) {
97
+ ctx.addIssue({
98
+ code: z.ZodIssueCode.custom,
99
+ message: 'Invalid regex: ' + s,
70
100
  });
71
- req.on('error', (e) => reject(`Error downloading: ${url}: ${e}`));
72
- req.end();
101
+ }
102
+ if (s.includes('[]'))
103
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Regex must not contain []: ' + s });
104
+ if (s.includes('{}'))
105
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Regex must not contain {}: ' + s });
106
+ [/.*[^\\]\{[^0-9,\\]\}.*/, /[^,0-9\\]\}/, /[^\\]\{[^,0-9]/].forEach((r) => {
107
+ if (r.test(s))
108
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'There is something odd with this regex: ' + s });
73
109
  });
110
+ let versionMatcher = '§§version§§';
111
+ if (replacer)
112
+ versionMatcher = JSON.parse(`"${replacer(versionMatcher)}"`);
113
+ const versionIndex = s.indexOf(versionMatcher);
114
+ if (versionIndex == -1 || (versionIndex > 0 && s.substring(versionIndex - 1, versionIndex) == '\\')) {
115
+ ctx.addIssue({ code: z.ZodIssueCode.custom, message: 'Regex must contain (§§version§§): ' + s });
116
+ }
117
+ else if (s.replace(/\(\?:/g, '').replace(/\\\(/g, '').split(/\(/)[1].indexOf(versionMatcher) != 0) {
118
+ ctx.addIssue({
119
+ code: z.ZodIssueCode.custom,
120
+ message: 'Regex must contain (§§version§§) as first capture group: ' + s.replace(/\(\?:/g, '').replace(/\\\(/g, ''),
121
+ });
122
+ }
74
123
  });
124
+ const replaceValidator = z
125
+ .string()
126
+ .regex(/^\/(.*[^\\])\/([^/]+)\/$/, 'RegExp error - should be on format "/search/replacement/"');
127
+ const validator = z.record(z
128
+ .object({
129
+ bowername: z.array(z.string().regex(/^[a-z0-9.-]+$/i)).optional(),
130
+ basePurl: z
131
+ .string()
132
+ .regex(/^pkg:[a-z0-9/]+$/i)
133
+ .optional(),
134
+ npmname: z
135
+ .string()
136
+ .regex(/^[a-z0-9.-]+$/i)
137
+ .optional(),
138
+ vulnerabilities: z.array(vulnValidator),
139
+ extractors: z
140
+ .object({
141
+ func: z.array(z.string().min(5)).optional(),
142
+ uri: z.array(regexValidator).optional(),
143
+ filename: z.array(regexValidator).optional(),
144
+ filecontent: z.array(regexValidator).optional(),
145
+ filecontentreplace: z.array(replaceValidator).optional(),
146
+ hashes: z.record(z.string().regex(/^[a-f0-9]+$/i), versionValidator).optional(),
147
+ })
148
+ .strict(),
149
+ })
150
+ .strict());
151
+ return validator.safeParse(repo);
75
152
  }
76
- function loadJsonFromFile(file, options) {
77
- return __awaiter(this, void 0, void 0, function* () {
78
- options.log.debug('Reading ' + file + ' ...');
79
- return new Promise((resolve, reject) => {
80
- fs.readFile(file, { encoding: 'utf8' }, (err, data) => {
81
- if (err) {
82
- return reject(err.toString());
153
+ exports.validateRepository = validateRepository;
154
+ function formatValidationError(error) {
155
+ return JSON.stringify(error.format(), (key, value) => (Array.isArray(value) && value.length === 0 ? undefined : value), 2);
156
+ }
157
+ async function loadJson(url, options) {
158
+ return new Promise((resolve, reject) => {
159
+ options.log.info('Downloading ' + url + ' ...');
160
+ const reqOptions = { ...URL.parse(url), method: 'GET' };
161
+ const proxyUri = options.proxy || process.env.http_proxy;
162
+ if (proxyUri) {
163
+ reqOptions.agent = new proxy_agent_1.ProxyAgent({
164
+ getProxyForUrl: () => proxyUri,
165
+ });
166
+ }
167
+ if (options.insecure) {
168
+ reqOptions.rejectUnauthorized = false;
169
+ }
170
+ if (options.cacertbuf) {
171
+ reqOptions.ca = [options.cacertbuf];
172
+ }
173
+ const req = (url.startsWith('http:') ? http : https).get(reqOptions, (res) => {
174
+ if (res.statusCode != 200)
175
+ return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
176
+ const data = [];
177
+ res.on('data', (c) => data.push(c));
178
+ res.on('end', () => {
179
+ let d = Buffer.concat(data).toString();
180
+ d = options.process ? options.process(d) : d;
181
+ const json = JSON.parse(d);
182
+ const vresult = validateRepository(json, options.process);
183
+ if (vresult.success) {
184
+ resolve(json);
185
+ }
186
+ else {
187
+ reject(`Invalid repository from ${url}: ${formatValidationError(vresult.error)}`);
83
188
  }
84
- data = options.process ? options.process(data) : data;
85
- resolve(JSON.parse(data));
86
189
  });
87
190
  });
191
+ req.on('error', (e) => reject(`Error downloading: ${url}: ${e}`));
192
+ req.end();
88
193
  });
89
194
  }
90
- function loadFromCache(url, cachedir, options) {
91
- return __awaiter(this, void 0, void 0, function* () {
92
- const cacheIndex = path.resolve(cachedir, 'index.json');
93
- if (!fs.existsSync(cachedir))
94
- fs.mkdirSync(cachedir);
95
- const cache = fs.existsSync(cacheIndex) ? JSON.parse(fs.readFileSync(cacheIndex, 'utf-8')) : {};
96
- const now = new Date().getTime();
97
- if (cache[url]) {
98
- if (now - cache[url].date < 60 * 60 * 1000) {
99
- options.log.info('Loading from cache: ' + url);
100
- return loadJsonFromFile(path.resolve(cachedir, cache[url].file), options);
195
+ async function loadJsonFromFile(file, options) {
196
+ options.log.debug('Reading ' + file + ' ...');
197
+ return new Promise((resolve, reject) => {
198
+ fs.readFile(file, { encoding: 'utf8' }, (err, data) => {
199
+ if (err) {
200
+ return reject(err.toString());
201
+ }
202
+ data = options.process ? options.process(data) : data;
203
+ const json = JSON.parse(data);
204
+ const vresult = validateRepository(json, options.process);
205
+ if (vresult.success) {
206
+ resolve(json);
101
207
  }
102
208
  else {
103
- if (fs.existsSync(path.resolve(cachedir, cache[url].date + '.json'))) {
104
- try {
105
- fs.unlinkSync(path.resolve(cachedir, cache[url].date + '.json'));
209
+ reject(`Invalid repository from ${file}: ${formatValidationError(vresult.error)}`);
210
+ }
211
+ });
212
+ });
213
+ }
214
+ async function loadFromCache(url, cachedir, options) {
215
+ const cacheIndex = path.resolve(cachedir, 'index.json');
216
+ if (!fs.existsSync(cachedir))
217
+ fs.mkdirSync(cachedir);
218
+ const cache = fs.existsSync(cacheIndex) ? JSON.parse(fs.readFileSync(cacheIndex, 'utf-8')) : {};
219
+ const now = new Date().getTime();
220
+ if (cache[url]) {
221
+ if (now - cache[url].date < 60 * 60 * 1000) {
222
+ options.log.info('Loading from cache: ' + url);
223
+ return loadJsonFromFile(path.resolve(cachedir, cache[url].file), options);
224
+ }
225
+ else {
226
+ if (fs.existsSync(path.resolve(cachedir, cache[url].date + '.json'))) {
227
+ try {
228
+ fs.unlinkSync(path.resolve(cachedir, cache[url].date + '.json'));
229
+ }
230
+ catch (error) {
231
+ if (error != null && typeof error == 'object' && 'code' in error && error.code !== 'ENOENT') {
232
+ throw error;
106
233
  }
107
- catch (error) {
108
- if (error != null && typeof error == 'object' && 'code' in error && error.code !== 'ENOENT') {
109
- throw error;
110
- }
111
- else {
112
- console.warn('Could not delete cache. Ignore this error if you are running multiple retire.js in parallel');
113
- }
234
+ else {
235
+ console.warn('Could not delete cache. Ignore this error if you are running multiple retire.js in parallel');
114
236
  }
115
237
  }
116
238
  }
117
239
  }
118
- const data = yield loadJson(url, options);
119
- cache[url] = { date: now, file: now + '.json' };
120
- fs.writeFileSync(path.resolve(cachedir, cache[url].file), JSON.stringify(data), { encoding: 'utf8' });
121
- fs.writeFileSync(cacheIndex, JSON.stringify(cache), { encoding: 'utf8' });
122
- return data;
123
- });
240
+ }
241
+ const data = await loadJson(url, options);
242
+ cache[url] = { date: now, file: now + '.json' };
243
+ fs.writeFileSync(path.resolve(cachedir, cache[url].file), JSON.stringify(data), { encoding: 'utf8' });
244
+ fs.writeFileSync(cacheIndex, JSON.stringify(cache), { encoding: 'utf8' });
245
+ return data;
124
246
  }
125
247
  function asbowerrepo(jsRepo) {
126
248
  const result = {};
@@ -133,21 +255,16 @@ function asbowerrepo(jsRepo) {
133
255
  return result;
134
256
  }
135
257
  exports.asbowerrepo = asbowerrepo;
136
- function loadrepository(repoUrl, options) {
137
- return __awaiter(this, void 0, void 0, function* () {
138
- //options = utils.extend(options, { process : retire.replaceVersion });
139
- options = Object.assign(Object.assign({}, options), { process: retire.replaceVersion });
140
- if (options.nocache) {
141
- return yield loadJson(repoUrl, options);
142
- }
143
- return yield loadFromCache(repoUrl, options.cachedir, options);
144
- });
258
+ async function loadrepository(repoUrl, options) {
259
+ options = { ...options, process: retire.replaceVersion };
260
+ if (options.nocache) {
261
+ return await loadJson(repoUrl, options);
262
+ }
263
+ return await loadFromCache(repoUrl, options.cachedir, options);
145
264
  }
146
265
  exports.loadrepository = loadrepository;
147
- function loadrepositoryFromFile(filepath, options) {
148
- return __awaiter(this, void 0, void 0, function* () {
149
- options = Object.assign(Object.assign({}, options), { process: retire.replaceVersion });
150
- return yield loadJsonFromFile(filepath, options);
151
- });
266
+ async function loadrepositoryFromFile(filepath, options) {
267
+ options = { ...options, process: retire.replaceVersion };
268
+ return await loadJsonFromFile(filepath, options);
152
269
  }
153
270
  exports.loadrepositoryFromFile = loadrepositoryFromFile;
@@ -45,9 +45,8 @@ function printResults(logger, finding, config) {
45
45
  }
46
46
  }
47
47
  function printVulnerability(component, config) {
48
- var _a;
49
48
  let string = '';
50
- (_a = component.vulnerabilities) === null || _a === void 0 ? void 0 : _a.forEach((vulnerability) => {
49
+ component.vulnerabilities?.forEach((vulnerability) => {
51
50
  string += config.outputformat === 'clean' ? '\n ' : ' ';
52
51
  if (vulnerability.severity) {
53
52
  string += `severity: ${vulnerability.severity}; `;
@@ -1,4 +1,6 @@
1
1
  /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ /// <reference types="node" />
2
4
  import { Finding } from './types';
3
5
  export type LoggerOptions = {
4
6
  outputpath: string;
package/lib/reporting.js CHANGED
@@ -115,8 +115,7 @@ function configureFileWriter(config) {
115
115
  };
116
116
  }
117
117
  function open(config) {
118
- var _a;
119
- verbose = (_a = config.verbose) !== null && _a !== void 0 ? _a : false;
118
+ verbose = config.verbose ?? false;
120
119
  if (config.colors)
121
120
  colorwarn = config.colorwarn;
122
121
  const format = config.outputformat || 'console';
package/lib/retire.js CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
 
6
6
  var exports = exports || {};
7
- exports.version = '4.3.4';
7
+ exports.version = '4.4.1';
8
8
 
9
9
  function isDefined(o) {
10
10
  return typeof o !== 'undefined';
package/lib/scanner.js CHANGED
@@ -40,18 +40,17 @@ const hash = {
40
40
  };
41
41
  function emitResults(finding, options) {
42
42
  if (options.includeOsv === true) {
43
- Promise.all(finding.results.map((r) => (0, depsdev_1.checkOSV)(r.component, r.version, options).then((v) => { var _a; return (r.vulnerabilities = ((_a = r.vulnerabilities) !== null && _a !== void 0 ? _a : []).concat(v)); }))).then(() => filterAndEmitResults(finding, options));
43
+ Promise.all(finding.results.map((r) => (0, depsdev_1.checkOSV)(r.component, r.version, options).then((v) => (r.vulnerabilities = (r.vulnerabilities ?? []).concat(v))))).then(() => filterAndEmitResults(finding, options));
44
44
  }
45
45
  else {
46
46
  filterAndEmitResults(finding, options);
47
47
  }
48
48
  }
49
49
  function getIdentifiers(v) {
50
- var _a, _b, _c, _d, _e, _f, _g, _h;
51
- return ((_b = (_a = v.identifiers) === null || _a === void 0 ? void 0 : _a.CVE) !== null && _b !== void 0 ? _b : [])
52
- .concat((_d = (_c = v.identifiers) === null || _c === void 0 ? void 0 : _c.bug) !== null && _d !== void 0 ? _d : [])
53
- .concat((_f = (_e = v.identifiers) === null || _e === void 0 ? void 0 : _e.issue) !== null && _f !== void 0 ? _f : [])
54
- .concat((_h = (_g = v.identifiers) === null || _g === void 0 ? void 0 : _g.githubID) !== null && _h !== void 0 ? _h : []);
50
+ return (v.identifiers?.CVE ?? [])
51
+ .concat(v.identifiers?.bug ?? [])
52
+ .concat(v.identifiers?.issue ?? [])
53
+ .concat(v.identifiers?.githubID ?? []);
55
54
  }
56
55
  function uniqueVulnerabilities(vulnerabilities) {
57
56
  if (!vulnerabilities)
@@ -78,45 +77,43 @@ function filterAndEmitResults(finding, options) {
78
77
  }
79
78
  }
80
79
  function shouldIgnorePath(fileSpecs, ignores) {
81
- var _a, _b;
82
- return ((_b = (_a = ignores.paths) === null || _a === void 0 ? void 0 : _a.some((i) => {
80
+ return (ignores.paths?.some((i) => {
83
81
  return fileSpecs.some((j) => i.test(j) || i.test(path.resolve(j)));
84
- })) !== null && _b !== void 0 ? _b : false);
82
+ }) ?? false);
85
83
  }
86
84
  function removeIgnored(results, ignores) {
87
85
  if (!('descriptors' in ignores))
88
86
  return;
89
87
  results.forEach((r) => {
90
- var _a, _b;
91
88
  if (!('vulnerabilities' in r))
92
89
  return;
93
- (_a = ignores.descriptors) === null || _a === void 0 ? void 0 : _a.filter((d) => 'component' in d).forEach((i) => {
94
- var _a;
90
+ ignores.descriptors
91
+ ?.filter((d) => 'component' in d)
92
+ .forEach((i) => {
95
93
  if (r.component !== i.component)
96
94
  return;
97
95
  if (i.version && r.version !== i.version)
98
96
  return;
99
97
  if (i.severity) {
100
98
  //Remove vulnerabilities with the severity we want to drop
101
- r.vulnerabilities = (_a = r.vulnerabilities) === null || _a === void 0 ? void 0 : _a.filter((v) => v.severity != i.severity);
99
+ r.vulnerabilities = r.vulnerabilities?.filter((v) => v.severity != i.severity);
102
100
  return;
103
101
  }
104
102
  if (i.identifiers) {
105
- removeIgnoredVulnerabilitiesByIdentifier(Object.assign({}, i.identifiers), r);
103
+ removeIgnoredVulnerabilitiesByIdentifier({ ...i.identifiers }, r);
106
104
  return;
107
105
  }
108
106
  r.vulnerabilities = [];
109
107
  });
110
- if (((_b = r.vulnerabilities) === null || _b === void 0 ? void 0 : _b.length) === 0)
108
+ if (r.vulnerabilities?.length === 0)
111
109
  delete r.vulnerabilities;
112
110
  });
113
111
  }
114
112
  function removeIgnoredVulnerabilitiesByIdentifier(identifiers, result) {
115
- var _a;
116
- result.vulnerabilities = (_a = result.vulnerabilities) === null || _a === void 0 ? void 0 : _a.filter((v) => {
113
+ result.vulnerabilities = result.vulnerabilities?.filter((v) => {
117
114
  if (!('identifiers' in v))
118
115
  return true;
119
- return !Object.entries(identifiers || {}).every(([key, value]) => hasIdentifier(Object.assign({}, v.identifiers), key, value));
116
+ return !Object.entries(identifiers || {}).every(([key, value]) => hasIdentifier({ ...v.identifiers }, key, value));
120
117
  });
121
118
  }
122
119
  function hasIdentifier(identifiers, key, value) {
package/lib/types.d.ts CHANGED
@@ -19,7 +19,7 @@ export type Vulnerability = {
19
19
  atOrAbove?: string;
20
20
  severity: SeverityLevel;
21
21
  cwe: string[];
22
- identifiers?: {
22
+ identifiers: {
23
23
  CVE?: string[];
24
24
  bug?: string;
25
25
  issue?: string;
package/package.json CHANGED
@@ -2,7 +2,7 @@
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": "4.3.4",
5
+ "version": "4.4.1",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
8
8
  "type": "git",
@@ -14,24 +14,24 @@
14
14
  "main": "./lib/retire.js",
15
15
  "dependencies": {
16
16
  "ansi-colors": "^4.1.1",
17
- "commander": "^10.0.0",
18
- "proxy-agent": "^6.2.0",
19
- "uuid": "^9.0.0",
17
+ "commander": "^10.0.1",
18
+ "proxy-agent": "^6.4.0",
19
+ "uuid": "^9.0.1",
20
20
  "walkdir": "0.4.1",
21
- "zod": "^3.20.6"
21
+ "zod": "^3.22.4"
22
22
  },
23
23
  "devDependencies": {
24
24
  "@types/node": "^18.13.0",
25
25
  "@types/uuid": "^9.0.0",
26
- "@typescript-eslint/eslint-plugin": "^5.51.0",
27
- "@typescript-eslint/parser": "^5.51.0",
26
+ "@typescript-eslint/eslint-plugin": "^6.11.0",
27
+ "@typescript-eslint/parser": "^6.11.0",
28
28
  "chai": "^4.3.4",
29
29
  "eslint": "^8.34.0",
30
- "eslint-config-prettier": "^8.6.0",
30
+ "eslint-config-prettier": "^9.0.0",
31
31
  "jsonschema": "^1.4.1",
32
32
  "libxmljs": "^1.0.8",
33
33
  "mocha": "^10.2.0",
34
- "prettier": "^2.8.4",
34
+ "prettier": "^3.1.0",
35
35
  "typescript": "^5.0.4"
36
36
  },
37
37
  "scripts": {
@@ -43,7 +43,7 @@
43
43
  "check": "npm run lint && npm run typecheck"
44
44
  },
45
45
  "engines": {
46
- "node": ">= 14.0.0"
46
+ "node": ">= 18.0.0"
47
47
  },
48
48
  "keywords": [
49
49
  "sbom",