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 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
- Usage
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 both
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'" ? 'https://raw.githubusercontent.com/RetireJS/retire.js/master/repository/jsrepository.json' : x);
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) {
@@ -0,0 +1,2 @@
1
+ import { Component, Repository } from './types';
2
+ export declare function deepScan(content: string, repo: Repository): Component[];
@@ -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 = '4.4.2';
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, "g");
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], "g");
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
- results = retire.scanFileContent(fs.readFileSync(file, 'utf-8'), repo, hash);
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": "4.4.2",
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": "^6.11.0",
27
- "@typescript-eslint/parser": "^6.11.0",
28
- "chai": "^4.3.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",