retire 4.4.2 → 5.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 +6 -1
- package/README.md +18 -19
- package/lib/cli.js +5 -1
- package/lib/deepscan.d.ts +2 -0
- package/lib/deepscan.js +37 -0
- package/lib/repo.js +1 -0
- package/lib/retire.js +6 -6
- package/lib/scanner.js +6 -1
- package/lib/types.d.ts +3 -0
- package/package.json +5 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,12 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.0.0-beta.1]
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Deepscan option with `--deep` where retire.js will also try to detect libraries using JavaScript AST (slower)
|
|
8
|
+
|
|
3
9
|
## [4.4.2]
|
|
4
10
|
|
|
5
11
|
### Fix
|
|
6
12
|
|
|
7
13
|
- Fix matching to include all matches
|
|
8
14
|
|
|
9
|
-
|
|
10
15
|
## [4.4.1]
|
|
11
16
|
|
|
12
17
|
### Chore
|
package/README.md
CHANGED
|
@@ -1,23 +1,20 @@
|
|
|
1
1
|
Command line scanner looking for use of known vulnerable js files and node modules in web projects and/or node projects.
|
|
2
2
|
|
|
3
|
-
Install
|
|
4
|
-
-------
|
|
3
|
+
## Install
|
|
5
4
|
|
|
6
5
|
npm install -g retire
|
|
7
6
|
|
|
7
|
+
## Usage
|
|
8
8
|
|
|
9
|
-
|
|
10
|
-
-----
|
|
11
|
-
|
|
12
|
-
````
|
|
9
|
+
```
|
|
13
10
|
Usage: retire [options]
|
|
14
11
|
|
|
15
12
|
Options:
|
|
16
13
|
-V, --version output the version number
|
|
17
14
|
-v, --verbose Show identified files (by default only vulnerable files are shown)
|
|
18
15
|
-c, --nocache Don't use local cache
|
|
19
|
-
--jspath <path> Folder to scan for javascript files
|
|
20
|
-
--path <path> Folder to scan for
|
|
16
|
+
--jspath <path> Folder to scan for javascript files (deprecated)
|
|
17
|
+
--path <path> Folder to scan for javascript files
|
|
21
18
|
--jsrepo <path|url> Local or internal version of repo. Can be multiple comma separated. Default: 'central')
|
|
22
19
|
--cachedir <path> Path to use for local cache instead of /tmp/.retire-cache
|
|
23
20
|
--proxy <url> Proxy url (http://some.host:8080)
|
|
@@ -33,23 +30,25 @@ Options:
|
|
|
33
30
|
--ext <extensions> Comma separated list of file extensions for JavaScript files. The default is "js"
|
|
34
31
|
--cacert <path> Use the specified certificate file to verify the peer used for fetching remote jsrepo/noderepo files
|
|
35
32
|
--includeOsv Include OSV advisories in the output
|
|
33
|
+
--deep Deep scan (slower and experimental)
|
|
36
34
|
-h, --help display help for command
|
|
37
|
-
|
|
35
|
+
```
|
|
38
36
|
|
|
39
37
|
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.
|
|
40
38
|
The `cyclonedx` output format is based on based on the https://github.com/CycloneDX spec.
|
|
41
39
|
|
|
42
|
-
.retireignore
|
|
43
|
-
|
|
44
|
-
|
|
40
|
+
## .retireignore
|
|
41
|
+
|
|
42
|
+
```
|
|
45
43
|
@qs # ignore this module regardless of location
|
|
46
44
|
node_modules/connect/node_modules/body-parser/node_modules/qs # ignore specific path
|
|
47
|
-
|
|
45
|
+
```
|
|
46
|
+
|
|
48
47
|
Due to a bug in ignore resolving, please upgrade to >= 1.1.3
|
|
49
48
|
|
|
50
|
-
.retireignore.json
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
## .retireignore.json
|
|
50
|
+
|
|
51
|
+
```
|
|
53
52
|
[
|
|
54
53
|
{
|
|
55
54
|
"component": "jquery",
|
|
@@ -67,8 +66,8 @@ Due to a bug in ignore resolving, please upgrade to >= 1.1.3
|
|
|
67
66
|
}
|
|
68
67
|
|
|
69
68
|
]
|
|
70
|
-
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
## Source code / Reporting an issue
|
|
71
72
|
|
|
72
|
-
Source code / Reporting an issue
|
|
73
|
-
--------------------------------
|
|
74
73
|
The source code and issue tracker can be found at [https://github.com/RetireJS/retire.js](https://github.com/RetireJS/retire.js)
|
package/lib/cli.js
CHANGED
|
@@ -71,12 +71,15 @@ const prg = commander_1.program
|
|
|
71
71
|
.option('--ext <extensions>', 'Comma separated list of file extensions for JavaScript files. The default is "js"')
|
|
72
72
|
.option('--cacert <path>', 'Use the specified certificate file to verify the peer used for fetching remote jsrepo/noderepo files')
|
|
73
73
|
.option('--includeOsv', 'Include OSV advisories in the output')
|
|
74
|
+
.option('--deep', 'Deep scan (slower and experimental)')
|
|
74
75
|
.parse()
|
|
75
76
|
.opts();
|
|
76
77
|
const colorwarn = prg.colors ? ansi_colors_1.default.red : (x) => x;
|
|
77
78
|
const jsrepolocation = (prg.jsrepo ?? "'central'")
|
|
78
79
|
.split(',')
|
|
79
|
-
.map((x) => x === "'central'"
|
|
80
|
+
.map((x) => x === "'central'"
|
|
81
|
+
? 'https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository-v2.json'
|
|
82
|
+
: x);
|
|
80
83
|
const ignorefile = prg.ignoreFile ?? defaultIgnoreFiles.filter((x) => fs_1.default.existsSync(x))[0];
|
|
81
84
|
const scanpath = prg.path ?? prg.jspath ?? '.';
|
|
82
85
|
const log = reporting.open({
|
|
@@ -108,6 +111,7 @@ const config = {
|
|
|
108
111
|
includeOsv: !!prg.includeOsv,
|
|
109
112
|
verbose: !!prg.verbose,
|
|
110
113
|
proxy: prg.proxy,
|
|
114
|
+
deep: !!prg.deep,
|
|
111
115
|
};
|
|
112
116
|
log.info(`retire.js v${retire.version}`);
|
|
113
117
|
function exitWithError(msg) {
|
package/lib/deepscan.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.deepScan = void 0;
|
|
4
|
+
const astronomical_1 = require("astronomical");
|
|
5
|
+
function deepScan(content, repo) {
|
|
6
|
+
const astQueries = {};
|
|
7
|
+
const backMap = {};
|
|
8
|
+
Object.entries(repo).forEach(([name, data]) => {
|
|
9
|
+
data.extractors.ast?.forEach((query, i) => {
|
|
10
|
+
astQueries[`${name}_${i}`] = query;
|
|
11
|
+
backMap[`${name}_${i}`] = name;
|
|
12
|
+
});
|
|
13
|
+
});
|
|
14
|
+
const results = (0, astronomical_1.multiQuery)(content, astQueries);
|
|
15
|
+
const detected = [];
|
|
16
|
+
Object.entries(results).forEach(([key, value]) => {
|
|
17
|
+
value.forEach((match) => {
|
|
18
|
+
const component = backMap[key];
|
|
19
|
+
if (typeof match !== 'string')
|
|
20
|
+
return;
|
|
21
|
+
detected.push({
|
|
22
|
+
version: match,
|
|
23
|
+
component: component,
|
|
24
|
+
npmname: repo[component].npmname,
|
|
25
|
+
basePurl: repo[component].basePurl,
|
|
26
|
+
detection: 'ast',
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
return detected.reduce((acc, cur) => {
|
|
31
|
+
if (acc.some((c) => c.component === cur.component && c.version === cur.version))
|
|
32
|
+
return acc;
|
|
33
|
+
acc.push(cur);
|
|
34
|
+
return acc;
|
|
35
|
+
}, []);
|
|
36
|
+
}
|
|
37
|
+
exports.deepScan = deepScan;
|
package/lib/repo.js
CHANGED
|
@@ -144,6 +144,7 @@ function validateRepository(repo, replacer) {
|
|
|
144
144
|
filecontent: z.array(regexValidator).optional(),
|
|
145
145
|
filecontentreplace: z.array(replaceValidator).optional(),
|
|
146
146
|
hashes: z.record(z.string().regex(/^[a-f0-9]+$/i), versionValidator).optional(),
|
|
147
|
+
ast: z.array(z.string()).optional(),
|
|
147
148
|
})
|
|
148
149
|
.strict(),
|
|
149
150
|
})
|
package/lib/retire.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
var exports = exports || {};
|
|
7
|
-
exports.version = '
|
|
7
|
+
exports.version = '5.0.0';
|
|
8
8
|
|
|
9
9
|
function isDefined(o) {
|
|
10
10
|
return typeof o !== 'undefined';
|
|
@@ -26,7 +26,7 @@ function scan(data, extractor, repo, matcher = simpleMatch) {
|
|
|
26
26
|
if (!isDefined(extractors)) continue;
|
|
27
27
|
for (var i in extractors) {
|
|
28
28
|
var matches = matcher(extractors[i], data);
|
|
29
|
-
matches.forEach(match => {
|
|
29
|
+
matches.forEach((match) => {
|
|
30
30
|
match = match.replace(/(\.|-)min$/, '');
|
|
31
31
|
detected.push({
|
|
32
32
|
version: match,
|
|
@@ -42,20 +42,20 @@ function scan(data, extractor, repo, matcher = simpleMatch) {
|
|
|
42
42
|
}
|
|
43
43
|
|
|
44
44
|
function simpleMatch(regex, data) {
|
|
45
|
-
var re = new RegExp(regex,
|
|
45
|
+
var re = new RegExp(regex, 'g');
|
|
46
46
|
const result = [];
|
|
47
47
|
let match;
|
|
48
|
-
while (match = re.exec(data)) {
|
|
48
|
+
while ((match = re.exec(data))) {
|
|
49
49
|
result.push(match[1]);
|
|
50
50
|
}
|
|
51
51
|
return result;
|
|
52
52
|
}
|
|
53
53
|
function replacementMatch(regex, data) {
|
|
54
54
|
var ar = /^\/(.*[^\\])\/([^\/]+)\/$/.exec(regex);
|
|
55
|
-
var re = new RegExp(ar[1],
|
|
55
|
+
var re = new RegExp(ar[1], 'g');
|
|
56
56
|
const result = [];
|
|
57
57
|
let match;
|
|
58
|
-
while(match = re.exec(data)) {
|
|
58
|
+
while ((match = re.exec(data))) {
|
|
59
59
|
var ver = null;
|
|
60
60
|
if (match) {
|
|
61
61
|
ver = match[0].replace(new RegExp(ar[1]), ar[2]);
|
package/lib/scanner.js
CHANGED
|
@@ -30,6 +30,7 @@ const fs = __importStar(require("fs"));
|
|
|
30
30
|
const crypto = __importStar(require("crypto"));
|
|
31
31
|
const path = __importStar(require("path"));
|
|
32
32
|
const depsdev_1 = require("./depsdev");
|
|
33
|
+
const deepscan_1 = require("./deepscan");
|
|
33
34
|
const events = new events_1.EventEmitter();
|
|
34
35
|
const hash = {
|
|
35
36
|
sha1: (data) => {
|
|
@@ -128,7 +129,11 @@ function scanJsFile(file, repo, options) {
|
|
|
128
129
|
}
|
|
129
130
|
let results = retire.scanFileName(file, repo);
|
|
130
131
|
if (!results || results.length === 0) {
|
|
131
|
-
|
|
132
|
+
const content = fs.readFileSync(file, 'utf-8');
|
|
133
|
+
results = retire.scanFileContent(content, repo, hash);
|
|
134
|
+
if (options.deep) {
|
|
135
|
+
results = results.concat((0, deepscan_1.deepScan)(content, repo));
|
|
136
|
+
}
|
|
132
137
|
}
|
|
133
138
|
emitResults({ file: file, results: results }, options);
|
|
134
139
|
}
|
package/lib/types.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type Repository = Record<string, {
|
|
|
12
12
|
filecontent?: string[];
|
|
13
13
|
filecontentreplace?: string[];
|
|
14
14
|
hashes?: Record<string, string>;
|
|
15
|
+
ast?: string[];
|
|
15
16
|
};
|
|
16
17
|
}>;
|
|
17
18
|
export type Vulnerability = {
|
|
@@ -34,6 +35,7 @@ export type Component = {
|
|
|
34
35
|
npmname?: string;
|
|
35
36
|
version: string;
|
|
36
37
|
vulnerabilities?: Vulnerability[];
|
|
38
|
+
detection?: string;
|
|
37
39
|
};
|
|
38
40
|
export type Finding = {
|
|
39
41
|
results: Component[];
|
|
@@ -86,5 +88,6 @@ export type Options = {
|
|
|
86
88
|
path: string;
|
|
87
89
|
exitwith: number;
|
|
88
90
|
includeOsv?: boolean;
|
|
91
|
+
deep?: boolean;
|
|
89
92
|
};
|
|
90
93
|
export {};
|
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": "
|
|
5
|
+
"version": "5.0.0",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
"main": "./lib/retire.js",
|
|
15
15
|
"dependencies": {
|
|
16
16
|
"ansi-colors": "^4.1.1",
|
|
17
|
+
"astronomical": "^1.0.0",
|
|
17
18
|
"commander": "^10.0.1",
|
|
18
19
|
"proxy-agent": "^6.4.0",
|
|
19
20
|
"uuid": "^9.0.1",
|
|
@@ -23,9 +24,9 @@
|
|
|
23
24
|
"devDependencies": {
|
|
24
25
|
"@types/node": "^18.13.0",
|
|
25
26
|
"@types/uuid": "^9.0.0",
|
|
26
|
-
"@typescript-eslint/eslint-plugin": "^
|
|
27
|
-
"@typescript-eslint/parser": "^
|
|
28
|
-
"chai": "^4.
|
|
27
|
+
"@typescript-eslint/eslint-plugin": "^7.1.1",
|
|
28
|
+
"@typescript-eslint/parser": "^7.1.1",
|
|
29
|
+
"chai": "^4.x.x",
|
|
29
30
|
"eslint": "^8.34.0",
|
|
30
31
|
"eslint-config-prettier": "^9.0.0",
|
|
31
32
|
"jsonschema": "^1.4.1",
|