retire 5.6.0 → 6.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/CHANGELOG.md +43 -0
- package/README.md +5 -2
- package/lib/cli.js +73 -103
- package/lib/depsdev.js +20 -20
- package/lib/http.d.ts +14 -0
- package/lib/http.js +122 -0
- package/lib/license.js +1 -1
- package/lib/parseIgnoreFile.d.ts +2 -0
- package/lib/parseIgnoreFile.js +36 -0
- package/lib/repo.js +34 -34
- package/lib/reporters/cyclonedx-1_6-json.d.ts +1 -2
- package/lib/reporters/cyclonedx-1_6-json.js +2 -207
- package/lib/reporters/cyclonedx-1_7-json.d.ts +2 -0
- package/lib/reporters/cyclonedx-1_7-json.js +4 -0
- package/lib/reporters/cyclonedx-json-builder.d.ts +7 -0
- package/lib/reporters/cyclonedx-json-builder.js +281 -0
- package/lib/reporters/cyclonedx-json.js +8 -2
- package/lib/reporters/cyclonedx.js +9 -8
- package/lib/reporting.js +3 -0
- package/lib/resolve.js +1 -1
- package/lib/retire.js +1 -1
- package/lib/scanner.d.ts +2 -2
- package/lib/scanner.js +21 -15
- package/lib/types.d.ts +27 -0
- package/lib/types.js +19 -1
- package/package.json +11 -11
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,48 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [6.0.0]
|
|
4
|
+
|
|
5
|
+
### Breaking changes
|
|
6
|
+
|
|
7
|
+
- Requires Node.js 20.19+ or 22.12+ (previously 18+).
|
|
8
|
+
- `--proxy` (and the `http_proxy` environment variable) now only supports `http://` and `https://` proxy URLs. SOCKS (`socks://`, `socks5://`, ...) and PAC (`pac+http://`, ...) proxies are no longer supported and fail with an `Unsupported proxy protocol` error.
|
|
9
|
+
|
|
10
|
+
### Improvements
|
|
11
|
+
|
|
12
|
+
- Replaced the `proxy-agent` dependency (and its 27 transitive packages) with a small built-in proxy client. Proxy credentials in the URL (`http://user:pass@host:port`) are still supported.
|
|
13
|
+
- Removed the `ansi-colors` dependency.
|
|
14
|
+
|
|
15
|
+
### Bugfixes
|
|
16
|
+
|
|
17
|
+
- `--includeOsv` lookups ignored `--proxy`, `--insecure` and `--cacert`. They now use the same settings as the repository download.
|
|
18
|
+
- `--includeOsv` looked up advisories under the repository's display name instead of the npm package name, so most components that declare an `npmname` silently got no OSV results (#626).
|
|
19
|
+
- `--includeOsv` findings could be missing from the report, because the report was closed before the OSV lookups finished (#623).
|
|
20
|
+
- Invalid CLI input (an unknown `--severity`, an unreadable `--cacert` or ignore file) now stops before scanning with an error message instead of a stack trace (#623).
|
|
21
|
+
- Malformed repository files and malformed OSV responses are reported with their source, instead of crashing the scan (#623).
|
|
22
|
+
- Errors while scanning files or `bower.json` are now reported, and the scan exits with code 1 (#623).
|
|
23
|
+
- `bower.json` files were not detected on Windows paths (#623).
|
|
24
|
+
- Distinct CVEs that share an issue were merged into one finding, while repeated advisories for the same vulnerability were not deduplicated (#623).
|
|
25
|
+
- The upper bound of a license version range was treated as inclusive (#623).
|
|
26
|
+
- CycloneDX output could change the scan results used by other reporters, and duplicate components are now merged together with their vulnerabilities and evidence (#625).
|
|
27
|
+
|
|
28
|
+
## [5.7.0]
|
|
29
|
+
|
|
30
|
+
### Improvements
|
|
31
|
+
|
|
32
|
+
- Added CycloneDX 1.7 output: `--outputformat cyclonedxJSON1_7` and `cyclonedxJSON1_7_VEX`.
|
|
33
|
+
- CycloneDX 1.7 reports every resolved license, each as its own SPDX expression. Earlier spec versions only allow a single expression per component, so 1.4/1.6 output still reports the first license only.
|
|
34
|
+
- The CycloneDX 1.6 and 1.7 reports now include:
|
|
35
|
+
- `$schema`, pointing at the schema for the emitted spec version.
|
|
36
|
+
- `metadata.tools` in the object form introduced in 1.5, replacing the deprecated legacy array.
|
|
37
|
+
- `metadata.component`, describing the scanned path as the subject of the BOM.
|
|
38
|
+
- `dependencies`, linking the scan target to every detected component.
|
|
39
|
+
- `evidence.identity`, reporting how the version was detected (`filecontent`, `filename`, `hash`, `ast`, ...) as a CycloneDX identity technique and confidence.
|
|
40
|
+
- `vulnerabilities[].detail`, when the repository supplies `details` (VEX variants only).
|
|
41
|
+
|
|
42
|
+
### Bugfixes
|
|
43
|
+
|
|
44
|
+
- CycloneDX 1.6 output threw when a vulnerability in a custom `--jsrepo` had no `cwe`, and could throw on a vulnerability with no `identifiers`.
|
|
45
|
+
|
|
3
46
|
## [5.6.0]
|
|
4
47
|
|
|
5
48
|
### Improvements
|
package/README.md
CHANGED
|
@@ -18,7 +18,8 @@ Options:
|
|
|
18
18
|
--jsrepo <path|url> Local or internal version of repo. Can be multiple comma separated. Default: 'central')
|
|
19
19
|
--cachedir <path> Path to use for local cache instead of /tmp/.retire-cache
|
|
20
20
|
--proxy <url> Proxy url (http://some.host:8080)
|
|
21
|
-
--outputformat <format> Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx
|
|
21
|
+
--outputformat <format> Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx, cyclonedxJSON, cyclonedxJSON1_6,
|
|
22
|
+
cyclonedxJSON1_6_VEX, cyclonedxJSON1_7 and cyclonedxJSON1_7_VEX
|
|
22
23
|
--outputpath <path> File to which output should be written
|
|
23
24
|
--ignore <paths> Comma delimited list of paths to ignore
|
|
24
25
|
--ignorefile <path> Custom ignore file, defaults to .retireignore / .retireignore.json
|
|
@@ -35,7 +36,9 @@ Options:
|
|
|
35
36
|
```
|
|
36
37
|
|
|
37
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.
|
|
38
|
-
The `cyclonedx` output format is based on
|
|
39
|
+
The `cyclonedx` output format is based on the https://github.com/CycloneDX spec. `cyclonedx` and `cyclonedxJSON` emit CycloneDX 1.4
|
|
40
|
+
(XML and JSON respectively). `cyclonedxJSON1_6` and `cyclonedxJSON1_7` emit JSON for the corresponding spec versions, and the
|
|
41
|
+
`_VEX` variants of those additionally include a `vulnerabilities` section.
|
|
39
42
|
|
|
40
43
|
## .retireignore
|
|
41
44
|
|
package/lib/cli.js
CHANGED
|
@@ -47,11 +47,8 @@ const reporting = __importStar(require("./reporting"));
|
|
|
47
47
|
const os_1 = __importDefault(require("os"));
|
|
48
48
|
const path_1 = __importDefault(require("path"));
|
|
49
49
|
const fs_1 = __importDefault(require("fs"));
|
|
50
|
-
const ansi_colors_1 = __importDefault(require("ansi-colors"));
|
|
51
|
-
const events_1 = require("events");
|
|
52
50
|
const types_1 = require("./types");
|
|
53
|
-
const
|
|
54
|
-
const events = new events_1.EventEmitter();
|
|
51
|
+
const parseIgnoreFile_1 = require("./parseIgnoreFile");
|
|
55
52
|
let failProcess = false;
|
|
56
53
|
const defaultIgnoreFiles = ['.retireignore', '.retireignore.json'];
|
|
57
54
|
if (process.argv.includes('--node') || process.argv.includes('-n')) {
|
|
@@ -70,7 +67,7 @@ const prg = commander_1.program
|
|
|
70
67
|
.option('--jsrepo <path|url>', "Local or internal version of repo. Can be multiple comma separated. Default: 'central')")
|
|
71
68
|
.option('--cachedir <path>', 'Path to use for local cache instead of /tmp/.retire-cache')
|
|
72
69
|
.option('--proxy <url>', 'Proxy url (http://some.host:8080)')
|
|
73
|
-
.option('--outputformat <format>', 'Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx, cyclonedxJSON, cyclonedxJSON1_6, cyclonedxJSON1_6_VEX')
|
|
70
|
+
.option('--outputformat <format>', 'Valid formats: text, json, jsonsimple, depcheck (experimental), cyclonedx, cyclonedxJSON, cyclonedxJSON1_6, cyclonedxJSON1_6_VEX, cyclonedxJSON1_7, cyclonedxJSON1_7_VEX')
|
|
74
71
|
.option('--outputpath <path>', 'File to which output should be written')
|
|
75
72
|
.option('--ignore <paths>', 'Comma delimited list of paths to ignore')
|
|
76
73
|
.option('--ignorefile <path>', 'Custom ignore file, defaults to .retireignore / .retireignore.json')
|
|
@@ -84,7 +81,8 @@ const prg = commander_1.program
|
|
|
84
81
|
.option('--deep', 'Deep scan (slower and experimental)')
|
|
85
82
|
.parse()
|
|
86
83
|
.opts();
|
|
87
|
-
const
|
|
84
|
+
const red = (x) => `\u001b[31m${x}\u001b[39m`;
|
|
85
|
+
const colorwarn = prg.colors ? red : (x) => x;
|
|
88
86
|
const jsrepolocation = (prg.jsrepo ?? "'central'")
|
|
89
87
|
.split(',')
|
|
90
88
|
.map((x) => x === "'central'"
|
|
@@ -103,9 +101,6 @@ const log = reporting.open({
|
|
|
103
101
|
verbose: !!prg.verbose,
|
|
104
102
|
});
|
|
105
103
|
const severity = prg.severity ?? 'none';
|
|
106
|
-
if (!(severity in types_1.severityLevels)) {
|
|
107
|
-
exitWithError(`Error: Invalid severity level (${severity}). Valid levels are: ${Object.keys(types_1.severityLevels).join(', ')}`);
|
|
108
|
-
}
|
|
109
104
|
const config = {
|
|
110
105
|
path: scanpath,
|
|
111
106
|
ignore: {
|
|
@@ -122,109 +117,84 @@ const config = {
|
|
|
122
117
|
includeOsv: !!prg.includeOsv,
|
|
123
118
|
verbose: !!prg.verbose,
|
|
124
119
|
proxy: prg.proxy,
|
|
120
|
+
insecure: !!prg.insecure,
|
|
125
121
|
deep: !!prg.deep,
|
|
126
122
|
ext: prg.ext ?? 'js',
|
|
127
123
|
};
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
log.error(config.colorwarn(msg));
|
|
124
|
+
function exitWithError(error) {
|
|
125
|
+
log.error(colorwarn(String(error)));
|
|
131
126
|
process.exitCode = 1;
|
|
132
127
|
log.close();
|
|
133
128
|
}
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
129
|
+
function scan() {
|
|
130
|
+
scanner.on('vulnerable-dependency-found', (result) => {
|
|
131
|
+
const levels = result.results.map((r) => {
|
|
132
|
+
return r.vulnerabilities
|
|
133
|
+
? r.vulnerabilities.map((v) => {
|
|
134
|
+
return types_1.severityLevels[v.severity ?? 'critical'];
|
|
135
|
+
})
|
|
136
|
+
: [];
|
|
137
|
+
});
|
|
138
|
+
const severity = utils.flatten(levels).reduce((x, y) => (x > y ? x : y));
|
|
139
|
+
if (severity >= types_1.severityLevels[config.severity]) {
|
|
140
|
+
failProcess = true;
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
scanner.on('vulnerable-dependency-found', log.logVulnerableDependency);
|
|
144
|
+
scanner.on('dependency-found', log.logDependency);
|
|
145
|
+
Promise.all(jsrepolocation.map((jsr) => jsr.match(/^https?:\/\//) ? repo.loadrepository(jsr, config) : repo.loadrepositoryFromFile(jsr, config)))
|
|
146
|
+
.then(async (jsRepos) => {
|
|
147
|
+
const scans = [];
|
|
148
|
+
let scanError;
|
|
149
|
+
const failed = (error) => {
|
|
150
|
+
scanError = error;
|
|
151
|
+
};
|
|
152
|
+
await new Promise((done) => {
|
|
153
|
+
resolve
|
|
154
|
+
.scanJsFiles(config.path, config)
|
|
155
|
+
.on('jsfile', (file) => {
|
|
156
|
+
jsRepos.forEach((jsRepo) => {
|
|
157
|
+
scans.push(scanner.scanJsFile(file, jsRepo, config).catch(failed));
|
|
158
|
+
});
|
|
159
|
+
})
|
|
160
|
+
.on('bowerfile', (bowerfile) => {
|
|
161
|
+
jsRepos.forEach((jsRepo) => {
|
|
162
|
+
const bowerRepo = repo.asbowerrepo(jsRepo);
|
|
163
|
+
scans.push(scanner.scanBowerFile(bowerfile, bowerRepo, config).catch(failed));
|
|
164
|
+
});
|
|
165
|
+
})
|
|
166
|
+
.on('fail', (file, err) => failed(`Could not scan ${file}: ${err ?? 'Unknown error'}`))
|
|
167
|
+
.on('error', failed)
|
|
168
|
+
.on('end', done);
|
|
169
|
+
});
|
|
170
|
+
await Promise.all(scans);
|
|
171
|
+
if (scanError !== undefined)
|
|
172
|
+
throw scanError;
|
|
173
|
+
process.exitCode = failProcess ? config.exitwith : 0;
|
|
174
|
+
log.close();
|
|
175
|
+
})
|
|
176
|
+
.catch(exitWithError);
|
|
139
177
|
}
|
|
140
|
-
|
|
141
|
-
.
|
|
142
|
-
|
|
143
|
-
})
|
|
144
|
-
.and(z
|
|
145
|
-
.object({
|
|
146
|
-
path: z.string(),
|
|
147
|
-
})
|
|
148
|
-
.or(z.object({
|
|
149
|
-
component: z.string(),
|
|
150
|
-
version: z.string().optional(),
|
|
151
|
-
identifiers: z.record(z.string(), z.string()).optional(),
|
|
152
|
-
}))));
|
|
153
|
-
if (ignorefile) {
|
|
154
|
-
if (!fs_1.default.existsSync(ignorefile)) {
|
|
155
|
-
exitWithError(`Error: Could not read ignore file: ${ignorefile}`);
|
|
178
|
+
try {
|
|
179
|
+
if (!types_1.severityParser.safeParse(severity).success) {
|
|
180
|
+
throw new Error(`Invalid severity level (${severity}). Valid levels are: ${Object.keys(types_1.severityLevels).join(', ')}`);
|
|
156
181
|
}
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
catch (e) {
|
|
162
|
-
exitWithError(`Error: Invalid ignore file: ${ignorefile}`);
|
|
182
|
+
log.info(`retire.js v${retire.version}`);
|
|
183
|
+
if (prg.cacert) {
|
|
184
|
+
if (!fs_1.default.existsSync(prg.cacert)) {
|
|
185
|
+
throw new Error(`Could not read cacert file: ${prg.cacert}`);
|
|
163
186
|
}
|
|
164
|
-
|
|
165
|
-
?.map((x) => ('path' in x ? x.path : undefined))
|
|
166
|
-
?.filter((x) => x != undefined) ?? [];
|
|
167
|
-
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignoredPaths);
|
|
187
|
+
config.cacertbuf = fs_1.default.readFileSync(prg.cacert);
|
|
168
188
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
.readFileSync(ignorefile, 'utf-8')
|
|
172
|
-
.split(/\r\n|\n/g)
|
|
173
|
-
.filter((e) => e !== '');
|
|
174
|
-
const ignored = lines.map((e) => {
|
|
175
|
-
return e[0] === '@' ? e.slice(1) : path_1.default.resolve(e);
|
|
176
|
-
});
|
|
177
|
-
config.ignore.pathsAsString = config.ignore.pathsAsString.concat(ignored);
|
|
189
|
+
if (ignorefile) {
|
|
190
|
+
config.ignore.pathsAsString = (0, parseIgnoreFile_1.parseIgnoreFile)(ignorefile, config);
|
|
178
191
|
}
|
|
192
|
+
config.ignore.paths = config.ignore.pathsAsString
|
|
193
|
+
.map((p) => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
194
|
+
.map((p) => p.replace(/[*]{1,2}/g, (a) => (a.length == 2 ? '.*' : '[^/]*')))
|
|
195
|
+
.map((s) => new RegExp(s));
|
|
196
|
+
scan();
|
|
197
|
+
}
|
|
198
|
+
catch (error) {
|
|
199
|
+
exitWithError(error);
|
|
179
200
|
}
|
|
180
|
-
config.ignore.paths = config.ignore.pathsAsString
|
|
181
|
-
.map((p) => p.replace(/[.+?^${}()|[\]\\]/g, '\\$&'))
|
|
182
|
-
.map((p) => p.replace(/[*]{1,2}/g, (a) => (a.length == 2 ? '.*' : '[^/]*')))
|
|
183
|
-
.map((s) => new RegExp(s));
|
|
184
|
-
scanner.on('vulnerable-dependency-found', (result) => {
|
|
185
|
-
const levels = result.results.map((r) => {
|
|
186
|
-
return r.vulnerabilities
|
|
187
|
-
? r.vulnerabilities.map((v) => {
|
|
188
|
-
return types_1.severityLevels[v.severity ?? 'critical'];
|
|
189
|
-
})
|
|
190
|
-
: [];
|
|
191
|
-
});
|
|
192
|
-
const severity = utils.flatten(levels).reduce((x, y) => (x > y ? x : y));
|
|
193
|
-
if (severity >= types_1.severityLevels[config.severity]) {
|
|
194
|
-
failProcess = true;
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
scanner.on('vulnerable-dependency-found', log.logVulnerableDependency);
|
|
198
|
-
scanner.on('dependency-found', log.logDependency);
|
|
199
|
-
events.on('scan-done', () => {
|
|
200
|
-
process.exitCode = failProcess ? config.exitwith : 0;
|
|
201
|
-
log.close();
|
|
202
|
-
});
|
|
203
|
-
process.on('uncaughtException', (err, ...rest) => {
|
|
204
|
-
console.warn('Exception caught: ', err, rest);
|
|
205
|
-
console.warn(err.stack);
|
|
206
|
-
process.exit(1);
|
|
207
|
-
});
|
|
208
|
-
events.on('stop', (err) => {
|
|
209
|
-
exitWithError(err);
|
|
210
|
-
});
|
|
211
|
-
Promise.all(jsrepolocation.map((jsr) => jsr.match(/^https?:\/\//) ? repo.loadrepository(jsr, config) : repo.loadrepositoryFromFile(jsr, config)))
|
|
212
|
-
.then((jsRepos) => {
|
|
213
|
-
resolve
|
|
214
|
-
.scanJsFiles(config.path, config)
|
|
215
|
-
.on('jsfile', (file) => {
|
|
216
|
-
jsRepos.forEach((jsRepo) => {
|
|
217
|
-
scanner.scanJsFile(file, jsRepo, config);
|
|
218
|
-
});
|
|
219
|
-
})
|
|
220
|
-
.on('bowerfile', (bowerfile) => {
|
|
221
|
-
jsRepos.forEach((jsRepo) => {
|
|
222
|
-
const bowerRepo = repo.asbowerrepo(jsRepo);
|
|
223
|
-
scanner.scanBowerFile(bowerfile, bowerRepo, config);
|
|
224
|
-
});
|
|
225
|
-
})
|
|
226
|
-
.on('end', () => {
|
|
227
|
-
events.emit('scan-done');
|
|
228
|
-
});
|
|
229
|
-
})
|
|
230
|
-
.catch((e) => events.emit('stop', e));
|
package/lib/depsdev.js
CHANGED
|
@@ -1,31 +1,31 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.checkOSV = checkOSV;
|
|
7
|
-
const
|
|
4
|
+
const http_1 = require("./http");
|
|
8
5
|
const retire_1 = require("./retire");
|
|
9
|
-
function loadJson(url, options) {
|
|
6
|
+
async function loadJson(url, options) {
|
|
10
7
|
options.log.debug('Downloading ' + url + ' ...');
|
|
8
|
+
const res = await (0, http_1.get)(url, (0, http_1.requestSettings)(options));
|
|
11
9
|
return new Promise((resolve, reject) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
10
|
+
if (res.statusCode != 200)
|
|
11
|
+
res.resume();
|
|
12
|
+
if (res.statusCode == 404)
|
|
13
|
+
return resolve(undefined);
|
|
14
|
+
if (res.statusCode != 200) {
|
|
15
|
+
return reject('HTTP ' + res.statusCode + ' ' + res.statusMessage + ' for ' + url);
|
|
16
|
+
}
|
|
17
|
+
const data = [];
|
|
18
|
+
res.on('data', (c) => data.push(c));
|
|
19
|
+
res.on('error', reject);
|
|
20
|
+
res.on('end', () => {
|
|
21
|
+
const result = Buffer.concat(data).toString();
|
|
22
|
+
try {
|
|
22
23
|
resolve(JSON.parse(result));
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
24
|
+
}
|
|
25
|
+
catch (error) {
|
|
26
|
+
reject(`Invalid JSON from ${url}: ${error}`);
|
|
27
|
+
}
|
|
27
28
|
});
|
|
28
|
-
req.end();
|
|
29
29
|
});
|
|
30
30
|
}
|
|
31
31
|
function getVulnerabilities(packageName, version, options) {
|
package/lib/http.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import * as http from 'http';
|
|
2
|
+
import { Options } from './types';
|
|
3
|
+
export type RequestSettings = {
|
|
4
|
+
proxy?: string;
|
|
5
|
+
insecure?: boolean;
|
|
6
|
+
ca?: Buffer;
|
|
7
|
+
};
|
|
8
|
+
export declare function requestSettings(options: Options): RequestSettings;
|
|
9
|
+
/**
|
|
10
|
+
* GET a URL, optionally through an http:// or https:// proxy.
|
|
11
|
+
* Plain http targets are sent to the proxy as absolute-form requests,
|
|
12
|
+
* https targets are tunnelled through the proxy with CONNECT.
|
|
13
|
+
*/
|
|
14
|
+
export declare function get(url: string, settings?: RequestSettings): Promise<http.IncomingMessage>;
|
package/lib/http.js
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
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 () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.requestSettings = requestSettings;
|
|
37
|
+
exports.get = get;
|
|
38
|
+
const http = __importStar(require("http"));
|
|
39
|
+
const https = __importStar(require("https"));
|
|
40
|
+
const net = __importStar(require("net"));
|
|
41
|
+
const tls = __importStar(require("tls"));
|
|
42
|
+
function requestSettings(options) {
|
|
43
|
+
return {
|
|
44
|
+
proxy: options.proxy || process.env.http_proxy,
|
|
45
|
+
insecure: options.insecure,
|
|
46
|
+
ca: options.cacertbuf,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* GET a URL, optionally through an http:// or https:// proxy.
|
|
51
|
+
* Plain http targets are sent to the proxy as absolute-form requests,
|
|
52
|
+
* https targets are tunnelled through the proxy with CONNECT.
|
|
53
|
+
*/
|
|
54
|
+
async function get(url, settings = {}) {
|
|
55
|
+
const target = new URL(url);
|
|
56
|
+
const tlsSettings = {
|
|
57
|
+
rejectUnauthorized: !settings.insecure,
|
|
58
|
+
...(settings.ca ? { ca: [settings.ca] } : {}),
|
|
59
|
+
};
|
|
60
|
+
if (!settings.proxy) {
|
|
61
|
+
return send(target.protocol === 'http:' ? http : https, url, tlsSettings);
|
|
62
|
+
}
|
|
63
|
+
const proxy = new URL(settings.proxy);
|
|
64
|
+
if (proxy.protocol !== 'http:' && proxy.protocol !== 'https:') {
|
|
65
|
+
throw new Error(`Unsupported proxy protocol: ${proxy.protocol} (only http: and https: are supported)`);
|
|
66
|
+
}
|
|
67
|
+
const proxyModule = proxy.protocol === 'https:' ? https : http;
|
|
68
|
+
const proxyConnection = {
|
|
69
|
+
host: unbracket(proxy.hostname),
|
|
70
|
+
port: proxy.port || (proxy.protocol === 'https:' ? 443 : 80),
|
|
71
|
+
...(proxy.protocol === 'https:' ? tlsSettings : {}),
|
|
72
|
+
};
|
|
73
|
+
if (target.protocol === 'http:') {
|
|
74
|
+
return send(proxyModule, {
|
|
75
|
+
...proxyConnection,
|
|
76
|
+
path: url,
|
|
77
|
+
headers: { host: target.host, ...proxyAuthorization(proxy) },
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
const socket = await tunnel(proxyModule, proxyConnection, proxy, target);
|
|
81
|
+
const servername = net.isIP(unbracket(target.hostname)) ? undefined : target.hostname;
|
|
82
|
+
// No agent: Node only honours createConnection when the request has no agent.
|
|
83
|
+
return send(https, url, {
|
|
84
|
+
createConnection: () => tls.connect({ ...tlsSettings, socket, servername }),
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
function tunnel(proxyModule, proxyConnection, proxy, target) {
|
|
88
|
+
const authority = `${target.hostname}:${target.port || 443}`;
|
|
89
|
+
return new Promise((resolve, reject) => {
|
|
90
|
+
const req = proxyModule.request({
|
|
91
|
+
...proxyConnection,
|
|
92
|
+
method: 'CONNECT',
|
|
93
|
+
path: authority,
|
|
94
|
+
agent: false,
|
|
95
|
+
headers: { host: authority, ...proxyAuthorization(proxy) },
|
|
96
|
+
});
|
|
97
|
+
req.on('connect', (res, socket) => {
|
|
98
|
+
if (res.statusCode !== 200) {
|
|
99
|
+
socket.destroy();
|
|
100
|
+
return reject(new Error(`Proxy refused tunnel to ${authority}: HTTP ${res.statusCode} ${res.statusMessage}`));
|
|
101
|
+
}
|
|
102
|
+
resolve(socket);
|
|
103
|
+
});
|
|
104
|
+
req.on('error', reject);
|
|
105
|
+
req.end();
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
function send(module, url, options = {}) {
|
|
109
|
+
return new Promise((resolve, reject) => {
|
|
110
|
+
const req = typeof url === 'string' ? module.get(url, options, resolve) : module.get(url, resolve);
|
|
111
|
+
req.on('error', reject);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
function proxyAuthorization(proxy) {
|
|
115
|
+
if (!proxy.username)
|
|
116
|
+
return {};
|
|
117
|
+
const credentials = `${decodeURIComponent(proxy.username)}:${decodeURIComponent(proxy.password)}`;
|
|
118
|
+
return { 'proxy-authorization': `Basic ${Buffer.from(credentials).toString('base64')}` };
|
|
119
|
+
}
|
|
120
|
+
function unbracket(hostname) {
|
|
121
|
+
return hostname.replace(/^\[(.*)\]$/, '$1');
|
|
122
|
+
}
|
package/lib/license.js
CHANGED
|
@@ -32,7 +32,7 @@ function evaluateLicense(licenses, version) {
|
|
|
32
32
|
.filter((parsedLicense) => {
|
|
33
33
|
return parsedLicense.ranges.some((range) => {
|
|
34
34
|
if (range.from && range.to) {
|
|
35
|
-
return (0, retire_1.isAtOrAbove)(version, range.from) && (0, retire_1.isAtOrAbove)(range.to
|
|
35
|
+
return (0, retire_1.isAtOrAbove)(version, range.from) && !(0, retire_1.isAtOrAbove)(version, range.to);
|
|
36
36
|
}
|
|
37
37
|
if (range.from) {
|
|
38
38
|
return (0, retire_1.isAtOrAbove)(version, range.from);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.parseIgnoreFile = parseIgnoreFile;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const types_1 = require("./types");
|
|
10
|
+
function parseIgnoreFile(ignorefile, config) {
|
|
11
|
+
if (!fs_1.default.existsSync(ignorefile)) {
|
|
12
|
+
throw new Error(`Could not read ignore file: ${ignorefile}`);
|
|
13
|
+
}
|
|
14
|
+
if (ignorefile.endsWith('.json')) {
|
|
15
|
+
try {
|
|
16
|
+
config.ignore.descriptors = types_1.ignoreFileParser.parse(JSON.parse(fs_1.default.readFileSync(ignorefile, 'utf-8')));
|
|
17
|
+
}
|
|
18
|
+
catch (e) {
|
|
19
|
+
throw new Error(`Invalid ignore file: ${ignorefile}`, { cause: e });
|
|
20
|
+
}
|
|
21
|
+
const ignoredPaths = config.ignore.descriptors
|
|
22
|
+
?.map((x) => ('path' in x ? x.path : undefined))
|
|
23
|
+
?.filter((x) => x != undefined) ?? [];
|
|
24
|
+
return config.ignore.pathsAsString.concat(ignoredPaths);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
const lines = fs_1.default
|
|
28
|
+
.readFileSync(ignorefile, 'utf-8')
|
|
29
|
+
.split(/\r\n|\n/g)
|
|
30
|
+
.filter((e) => e !== '');
|
|
31
|
+
const ignored = lines.map((e) => {
|
|
32
|
+
return e[0] === '@' ? e.slice(1) : path_1.default.resolve(e);
|
|
33
|
+
});
|
|
34
|
+
return config.ignore.pathsAsString.concat(ignored);
|
|
35
|
+
}
|
|
36
|
+
}
|
package/lib/repo.js
CHANGED
|
@@ -39,11 +39,8 @@ exports.loadrepository = loadrepository;
|
|
|
39
39
|
exports.loadrepositoryFromFile = loadrepositoryFromFile;
|
|
40
40
|
const fs = __importStar(require("fs"));
|
|
41
41
|
const path = __importStar(require("path"));
|
|
42
|
-
const http = __importStar(require("http"));
|
|
43
|
-
const https = __importStar(require("https"));
|
|
44
42
|
const retire = __importStar(require("./retire"));
|
|
45
|
-
const
|
|
46
|
-
const proxy_agent_1 = require("proxy-agent");
|
|
43
|
+
const http_1 = require("./http");
|
|
47
44
|
const z = __importStar(require("zod"));
|
|
48
45
|
const types_1 = require("./types");
|
|
49
46
|
function validateRepository(repo, replacer) {
|
|
@@ -108,7 +105,7 @@ function validateRepository(repo, replacer) {
|
|
|
108
105
|
try {
|
|
109
106
|
new RegExp(s);
|
|
110
107
|
}
|
|
111
|
-
catch
|
|
108
|
+
catch {
|
|
112
109
|
ctx.addIssue({
|
|
113
110
|
code: z.ZodIssueCode.custom,
|
|
114
111
|
message: 'Invalid regex: ' + s,
|
|
@@ -175,27 +172,24 @@ function formatValidationError(error) {
|
|
|
175
172
|
return JSON.stringify(error.format(), (key, value) => (Array.isArray(value) && value.length === 0 ? undefined : value), 2);
|
|
176
173
|
}
|
|
177
174
|
async function loadJson(url, options) {
|
|
175
|
+
options.log.info('Downloading ' + url + ' ...');
|
|
176
|
+
let res;
|
|
177
|
+
try {
|
|
178
|
+
res = await (0, http_1.get)(url, (0, http_1.requestSettings)(options));
|
|
179
|
+
}
|
|
180
|
+
catch (e) {
|
|
181
|
+
throw `Error downloading: ${url}: ${e}`;
|
|
182
|
+
}
|
|
178
183
|
return new Promise((resolve, reject) => {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
if (proxyUri) {
|
|
183
|
-
reqOptions.agent = new proxy_agent_1.ProxyAgent({
|
|
184
|
-
getProxyForUrl: () => proxyUri,
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
|
-
if (options.insecure) {
|
|
188
|
-
reqOptions.rejectUnauthorized = false;
|
|
184
|
+
if (res.statusCode != 200) {
|
|
185
|
+
res.resume();
|
|
186
|
+
return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
|
|
189
187
|
}
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
return reject(`Error downloading: ${url}: HTTP ${res.statusCode} ${res.statusMessage}`);
|
|
196
|
-
const data = [];
|
|
197
|
-
res.on('data', (c) => data.push(c));
|
|
198
|
-
res.on('end', () => {
|
|
188
|
+
const data = [];
|
|
189
|
+
res.on('data', (c) => data.push(c));
|
|
190
|
+
res.on('error', (e) => reject(`Error downloading: ${url}: ${e}`));
|
|
191
|
+
res.on('end', () => {
|
|
192
|
+
try {
|
|
199
193
|
let d = Buffer.concat(data).toString();
|
|
200
194
|
d = options.process ? options.process(d) : d;
|
|
201
195
|
const json = JSON.parse(d);
|
|
@@ -206,10 +200,11 @@ async function loadJson(url, options) {
|
|
|
206
200
|
else {
|
|
207
201
|
reject(`Invalid repository from ${url}: ${formatValidationError(vresult.error)}`);
|
|
208
202
|
}
|
|
209
|
-
}
|
|
203
|
+
}
|
|
204
|
+
catch (error) {
|
|
205
|
+
reject(`Invalid repository from ${url}: ${error}`);
|
|
206
|
+
}
|
|
210
207
|
});
|
|
211
|
-
req.on('error', (e) => reject(`Error downloading: ${url}: ${e}`));
|
|
212
|
-
req.end();
|
|
213
208
|
});
|
|
214
209
|
}
|
|
215
210
|
async function loadJsonFromFile(file, options) {
|
|
@@ -219,14 +214,19 @@ async function loadJsonFromFile(file, options) {
|
|
|
219
214
|
if (err) {
|
|
220
215
|
return reject(err.toString());
|
|
221
216
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
217
|
+
try {
|
|
218
|
+
data = options.process ? options.process(data) : data;
|
|
219
|
+
const json = JSON.parse(data);
|
|
220
|
+
const vresult = validateRepository(json, options.process);
|
|
221
|
+
if (vresult.success) {
|
|
222
|
+
resolve(json);
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
reject(`Invalid repository from ${file}: ${formatValidationError(vresult.error)}`);
|
|
226
|
+
}
|
|
227
227
|
}
|
|
228
|
-
|
|
229
|
-
reject(`Invalid repository from ${file}: ${
|
|
228
|
+
catch (error) {
|
|
229
|
+
reject(`Invalid repository from ${file}: ${error}`);
|
|
230
230
|
}
|
|
231
231
|
});
|
|
232
232
|
});
|