@nodesecure/js-x-ray 7.0.0 → 7.1.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/README.md +20 -6
- package/index.d.ts +10 -0
- package/index.js +4 -1
- package/package.json +2 -2
- package/src/AstAnalyser.js +137 -137
- package/src/EntryFilesAnalyser.js +99 -0
- package/src/probes/isImportDeclaration.js +1 -1
- package/src/probes/isLiteral.js +7 -1
- package/types/api.d.ts +23 -1
package/README.md
CHANGED
|
@@ -67,11 +67,13 @@ require(Buffer.from("6673", "hex").toString());
|
|
|
67
67
|
|
|
68
68
|
Then use `js-x-ray` to run an analysis of the JavaScript code:
|
|
69
69
|
```js
|
|
70
|
-
import {
|
|
70
|
+
import { AstAnalyser } from "@nodesecure/js-x-ray";
|
|
71
71
|
import { readFileSync } from "node:fs";
|
|
72
72
|
|
|
73
|
-
const
|
|
74
|
-
|
|
73
|
+
const scanner = new AstAnalyser();
|
|
74
|
+
|
|
75
|
+
const { warnings, dependencies } = await scanner.analyseFile(
|
|
76
|
+
"./file.js"
|
|
75
77
|
);
|
|
76
78
|
|
|
77
79
|
console.log(dependencies);
|
|
@@ -174,11 +176,16 @@ You can pass an array of probes to the `runASTAnalysis/runASTAnalysisOnFile` fun
|
|
|
174
176
|
Here using the example probe upper:
|
|
175
177
|
|
|
176
178
|
```ts
|
|
177
|
-
import {
|
|
179
|
+
import { AstAnalyser } from "@nodesecure/js-x-ray";
|
|
178
180
|
|
|
179
181
|
// add your customProbes here (see example above)
|
|
180
182
|
|
|
181
|
-
const
|
|
183
|
+
const scanner = new AstAnalyser({
|
|
184
|
+
customProbes,
|
|
185
|
+
skipDefaultProbes: true
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
const result = scanner.analyse("const danger = 'danger';");
|
|
182
189
|
|
|
183
190
|
console.log(result);
|
|
184
191
|
```
|
|
@@ -202,6 +209,12 @@ Congrats, you have created your first custom probe! 🎉
|
|
|
202
209
|
> Check the types in [index.d.ts](index.d.ts) and [types/api.d.ts](types/api.d.ts) for more details about the `options`
|
|
203
210
|
|
|
204
211
|
## API
|
|
212
|
+
|
|
213
|
+
- [AstAnalyser](./docs/api/AstAnalyser.md)
|
|
214
|
+
- [EntryFilesAnalyser](./docs/api/EntryFilesAnalyser.md)
|
|
215
|
+
|
|
216
|
+
Legacy APIs waiting to be deprecated;
|
|
217
|
+
|
|
205
218
|
<details>
|
|
206
219
|
<summary>runASTAnalysis(str: string, options?: RuntimeOptions & AstAnalyserOptions): Report</summary>
|
|
207
220
|
|
|
@@ -290,7 +303,7 @@ $ yarn add @nodesecure/estree-ast-util
|
|
|
290
303
|
## Contributors ✨
|
|
291
304
|
|
|
292
305
|
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
293
|
-
[](#contributors-)
|
|
294
307
|
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
295
308
|
|
|
296
309
|
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
@@ -321,6 +334,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
|
|
321
334
|
<tr>
|
|
322
335
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/FredGuiou"><img src="https://avatars.githubusercontent.com/u/99122562?v=4?s=100" width="100px;" alt="FredGuiou"/><br /><sub><b>FredGuiou</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=FredGuiou" title="Documentation">📖</a> <a href="https://github.com/NodeSecure/js-x-ray/commits?author=FredGuiou" title="Code">💻</a></td>
|
|
323
336
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/madina0801"><img src="https://avatars.githubusercontent.com/u/101329759?v=4?s=100" width="100px;" alt="Madina"/><br /><sub><b>Madina</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=madina0801" title="Code">💻</a></td>
|
|
337
|
+
<td align="center" valign="top" width="14.28%"><a href="https://github.com/sairuss7"><img src="https://avatars.githubusercontent.com/u/87803528?v=4?s=100" width="100px;" alt="SairussDev"/><br /><sub><b>SairussDev</b></sub></a><br /><a href="https://github.com/NodeSecure/js-x-ray/commits?author=sairuss7" title="Code">💻</a></td>
|
|
324
338
|
</tr>
|
|
325
339
|
</tbody>
|
|
326
340
|
</table>
|
package/index.d.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AstAnalyser,
|
|
3
|
+
AstAnalyserOptions,
|
|
4
|
+
|
|
5
|
+
EntryFilesAnalyser,
|
|
6
|
+
EntryFilesAnalyserOptions,
|
|
7
|
+
|
|
3
8
|
SourceParser,
|
|
9
|
+
JsSourceParser,
|
|
4
10
|
runASTAnalysis,
|
|
5
11
|
runASTAnalysisOnFile,
|
|
6
12
|
Report,
|
|
@@ -23,6 +29,10 @@ declare const warnings: Record<WarningName, Pick<WarningDefault, "experimental"
|
|
|
23
29
|
export {
|
|
24
30
|
warnings,
|
|
25
31
|
AstAnalyser,
|
|
32
|
+
AstAnalyserOptions,
|
|
33
|
+
EntryFilesAnalyser,
|
|
34
|
+
EntryFilesAnalyserOptions,
|
|
35
|
+
JsSourceParser,
|
|
26
36
|
SourceParser,
|
|
27
37
|
runASTAnalysis,
|
|
28
38
|
runASTAnalysisOnFile,
|
package/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { warnings } from "./src/warnings.js";
|
|
3
3
|
import { JsSourceParser } from "./src/JsSourceParser.js";
|
|
4
4
|
import { AstAnalyser } from "./src/AstAnalyser.js";
|
|
5
|
+
import { EntryFilesAnalyser } from "./src/EntryFilesAnalyser.js";
|
|
5
6
|
|
|
6
7
|
function runASTAnalysis(
|
|
7
8
|
str,
|
|
@@ -28,8 +29,8 @@ async function runASTAnalysisOnFile(
|
|
|
28
29
|
options = {}
|
|
29
30
|
) {
|
|
30
31
|
const {
|
|
31
|
-
customParser = new JsSourceParser(),
|
|
32
32
|
customProbes = [],
|
|
33
|
+
customParser = new JsSourceParser(),
|
|
33
34
|
skipDefaultProbes = false,
|
|
34
35
|
...opts
|
|
35
36
|
} = options;
|
|
@@ -46,6 +47,8 @@ async function runASTAnalysisOnFile(
|
|
|
46
47
|
export {
|
|
47
48
|
warnings,
|
|
48
49
|
AstAnalyser,
|
|
50
|
+
EntryFilesAnalyser,
|
|
51
|
+
JsSourceParser,
|
|
49
52
|
runASTAnalysis,
|
|
50
53
|
runASTAnalysisOnFile
|
|
51
54
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodesecure/js-x-ray",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.1.1",
|
|
4
4
|
"description": "JavaScript AST XRay analysis",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": "./index.js",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"@types/node": "^20.6.2",
|
|
59
59
|
"c8": "^9.0.0",
|
|
60
60
|
"cross-env": "^7.0.3",
|
|
61
|
-
"eslint": "^
|
|
61
|
+
"eslint": "^9.0.0",
|
|
62
62
|
"glob": "^10.3.4",
|
|
63
63
|
"iterator-matcher": "^2.1.0",
|
|
64
64
|
"pkg-ok": "^3.0.0"
|
package/src/AstAnalyser.js
CHANGED
|
@@ -1,137 +1,137 @@
|
|
|
1
|
-
// Import Node.js Dependencies
|
|
2
|
-
import fs from "node:fs/promises";
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
|
|
5
|
-
// Import Third-party Dependencies
|
|
6
|
-
import { walk } from "estree-walker";
|
|
7
|
-
import isMinified from "is-minified-code";
|
|
8
|
-
|
|
9
|
-
// Import Internal Dependencies
|
|
10
|
-
import { SourceFile } from "./SourceFile.js";
|
|
11
|
-
import { isOneLineExpressionExport } from "./utils/index.js";
|
|
12
|
-
import { JsSourceParser } from "./JsSourceParser.js";
|
|
13
|
-
|
|
14
|
-
export class AstAnalyser {
|
|
15
|
-
/**
|
|
16
|
-
* @constructor
|
|
17
|
-
* @param {object} [options={}]
|
|
18
|
-
* @param {SourceParser} [options.customParser]
|
|
19
|
-
* @param {Array<object>} [options.customProbes]
|
|
20
|
-
* @param {boolean} [options.skipDefaultProbes=false]
|
|
21
|
-
*/
|
|
22
|
-
constructor(options = {}) {
|
|
23
|
-
this.parser = options.customParser ?? new JsSourceParser();
|
|
24
|
-
this.probesOptions = {
|
|
25
|
-
customProbes: options.customProbes ?? [],
|
|
26
|
-
skipDefaultProbes: options.skipDefaultProbes ?? false
|
|
27
|
-
};
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
analyse(str, options = Object.create(null)) {
|
|
31
|
-
const {
|
|
32
|
-
isMinified = false,
|
|
33
|
-
module = true,
|
|
34
|
-
removeHTMLComments = false
|
|
35
|
-
} = options;
|
|
36
|
-
|
|
37
|
-
const body = this.parser.parse(this.prepareSource(str, { removeHTMLComments }), {
|
|
38
|
-
isEcmaScriptModule: Boolean(module)
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
const source = new SourceFile(str, this.probesOptions);
|
|
42
|
-
|
|
43
|
-
// we walk each AST Nodes, this is a purely synchronous I/O
|
|
44
|
-
walk(body, {
|
|
45
|
-
enter(node) {
|
|
46
|
-
// Skip the root of the AST.
|
|
47
|
-
if (Array.isArray(node)) {
|
|
48
|
-
return;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
const action = source.walk(node);
|
|
52
|
-
if (action === "skip") {
|
|
53
|
-
this.skip();
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
return {
|
|
59
|
-
...source.getResult(isMinified),
|
|
60
|
-
dependencies: source.dependencies,
|
|
61
|
-
isOneLineRequire: isOneLineExpressionExport(body)
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
async analyseFile(
|
|
66
|
-
pathToFile,
|
|
67
|
-
options = {}
|
|
68
|
-
) {
|
|
69
|
-
try {
|
|
70
|
-
const {
|
|
71
|
-
packageName = null,
|
|
72
|
-
module = true,
|
|
73
|
-
removeHTMLComments = false
|
|
74
|
-
} = options;
|
|
75
|
-
|
|
76
|
-
const str = await fs.readFile(pathToFile, "utf-8");
|
|
77
|
-
const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile;
|
|
78
|
-
|
|
79
|
-
const isMin = filePathString.includes(".min") || isMinified(str);
|
|
80
|
-
const data = this.analyse(str, {
|
|
81
|
-
isMinified: isMin,
|
|
82
|
-
module: path.extname(filePathString) === ".mjs" ? true : module,
|
|
83
|
-
removeHTMLComments
|
|
84
|
-
});
|
|
85
|
-
|
|
86
|
-
if (packageName !== null) {
|
|
87
|
-
data.dependencies.delete(packageName);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
ok: true,
|
|
92
|
-
dependencies: data.dependencies,
|
|
93
|
-
warnings: data.warnings,
|
|
94
|
-
isMinified: !data.isOneLineRequire && isMin
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
catch (error) {
|
|
98
|
-
return {
|
|
99
|
-
ok: false,
|
|
100
|
-
warnings: [
|
|
101
|
-
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
|
|
102
|
-
]
|
|
103
|
-
};
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* @param {!string} source
|
|
109
|
-
* @param {object} options
|
|
110
|
-
* @param {boolean} [options.removeHTMLComments=false]
|
|
111
|
-
*/
|
|
112
|
-
prepareSource(source, options = {}) {
|
|
113
|
-
if (typeof source !== "string") {
|
|
114
|
-
throw new TypeError("source must be a string");
|
|
115
|
-
}
|
|
116
|
-
const { removeHTMLComments = false } = options;
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* if the file start with a shebang then we remove it because meriyah.parseScript fail to parse it.
|
|
120
|
-
* @example
|
|
121
|
-
* #!/usr/bin/env node
|
|
122
|
-
*/
|
|
123
|
-
const rawNoShebang = source.startsWith("#") ?
|
|
124
|
-
source.slice(source.indexOf("\n") + 1) : source;
|
|
125
|
-
|
|
126
|
-
return removeHTMLComments ?
|
|
127
|
-
this.#removeHTMLComment(rawNoShebang) : rawNoShebang;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
/**
|
|
131
|
-
* @param {!string} str
|
|
132
|
-
* @returns {string}
|
|
133
|
-
*/
|
|
134
|
-
#removeHTMLComment(str) {
|
|
135
|
-
return str.replaceAll(/<!--[\s\S]*?(?:-->)/g, "");
|
|
136
|
-
}
|
|
137
|
-
}
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
// Import Third-party Dependencies
|
|
6
|
+
import { walk } from "estree-walker";
|
|
7
|
+
import isMinified from "is-minified-code";
|
|
8
|
+
|
|
9
|
+
// Import Internal Dependencies
|
|
10
|
+
import { SourceFile } from "./SourceFile.js";
|
|
11
|
+
import { isOneLineExpressionExport } from "./utils/index.js";
|
|
12
|
+
import { JsSourceParser } from "./JsSourceParser.js";
|
|
13
|
+
|
|
14
|
+
export class AstAnalyser {
|
|
15
|
+
/**
|
|
16
|
+
* @constructor
|
|
17
|
+
* @param {object} [options={}]
|
|
18
|
+
* @param {SourceParser} [options.customParser]
|
|
19
|
+
* @param {Array<object>} [options.customProbes]
|
|
20
|
+
* @param {boolean} [options.skipDefaultProbes=false]
|
|
21
|
+
*/
|
|
22
|
+
constructor(options = {}) {
|
|
23
|
+
this.parser = options.customParser ?? new JsSourceParser();
|
|
24
|
+
this.probesOptions = {
|
|
25
|
+
customProbes: options.customProbes ?? [],
|
|
26
|
+
skipDefaultProbes: options.skipDefaultProbes ?? false
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
analyse(str, options = Object.create(null)) {
|
|
31
|
+
const {
|
|
32
|
+
isMinified = false,
|
|
33
|
+
module = true,
|
|
34
|
+
removeHTMLComments = false
|
|
35
|
+
} = options;
|
|
36
|
+
|
|
37
|
+
const body = this.parser.parse(this.prepareSource(str, { removeHTMLComments }), {
|
|
38
|
+
isEcmaScriptModule: Boolean(module)
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const source = new SourceFile(str, this.probesOptions);
|
|
42
|
+
|
|
43
|
+
// we walk each AST Nodes, this is a purely synchronous I/O
|
|
44
|
+
walk(body, {
|
|
45
|
+
enter(node) {
|
|
46
|
+
// Skip the root of the AST.
|
|
47
|
+
if (Array.isArray(node)) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const action = source.walk(node);
|
|
52
|
+
if (action === "skip") {
|
|
53
|
+
this.skip();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
return {
|
|
59
|
+
...source.getResult(isMinified),
|
|
60
|
+
dependencies: source.dependencies,
|
|
61
|
+
isOneLineRequire: isOneLineExpressionExport(body)
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
async analyseFile(
|
|
66
|
+
pathToFile,
|
|
67
|
+
options = {}
|
|
68
|
+
) {
|
|
69
|
+
try {
|
|
70
|
+
const {
|
|
71
|
+
packageName = null,
|
|
72
|
+
module = true,
|
|
73
|
+
removeHTMLComments = false
|
|
74
|
+
} = options;
|
|
75
|
+
|
|
76
|
+
const str = await fs.readFile(pathToFile, "utf-8");
|
|
77
|
+
const filePathString = pathToFile instanceof URL ? pathToFile.href : pathToFile;
|
|
78
|
+
|
|
79
|
+
const isMin = filePathString.includes(".min") || isMinified(str);
|
|
80
|
+
const data = this.analyse(str, {
|
|
81
|
+
isMinified: isMin,
|
|
82
|
+
module: path.extname(filePathString) === ".mjs" ? true : module,
|
|
83
|
+
removeHTMLComments
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
if (packageName !== null) {
|
|
87
|
+
data.dependencies.delete(packageName);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
ok: true,
|
|
92
|
+
dependencies: data.dependencies,
|
|
93
|
+
warnings: data.warnings,
|
|
94
|
+
isMinified: !data.isOneLineRequire && isMin
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
catch (error) {
|
|
98
|
+
return {
|
|
99
|
+
ok: false,
|
|
100
|
+
warnings: [
|
|
101
|
+
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* @param {!string} source
|
|
109
|
+
* @param {object} options
|
|
110
|
+
* @param {boolean} [options.removeHTMLComments=false]
|
|
111
|
+
*/
|
|
112
|
+
prepareSource(source, options = {}) {
|
|
113
|
+
if (typeof source !== "string") {
|
|
114
|
+
throw new TypeError("source must be a string");
|
|
115
|
+
}
|
|
116
|
+
const { removeHTMLComments = false } = options;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* if the file start with a shebang then we remove it because meriyah.parseScript fail to parse it.
|
|
120
|
+
* @example
|
|
121
|
+
* #!/usr/bin/env node
|
|
122
|
+
*/
|
|
123
|
+
const rawNoShebang = source.startsWith("#") ?
|
|
124
|
+
source.slice(source.indexOf("\n") + 1) : source;
|
|
125
|
+
|
|
126
|
+
return removeHTMLComments ?
|
|
127
|
+
this.#removeHTMLComment(rawNoShebang) : rawNoShebang;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {!string} str
|
|
132
|
+
* @returns {string}
|
|
133
|
+
*/
|
|
134
|
+
#removeHTMLComment(str) {
|
|
135
|
+
return str.replaceAll(/<!--[\s\S]*?(?:-->)/g, "");
|
|
136
|
+
}
|
|
137
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import fs from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
// Import Internal Dependencies
|
|
6
|
+
import { AstAnalyser } from "./AstAnalyser.js";
|
|
7
|
+
|
|
8
|
+
const kDefaultExtensions = ["js", "cjs", "mjs", "node"];
|
|
9
|
+
|
|
10
|
+
export class EntryFilesAnalyser {
|
|
11
|
+
/**
|
|
12
|
+
* @constructor
|
|
13
|
+
* @param {object} [options={}]
|
|
14
|
+
* @param {AstAnalyser} [options.astAnalyzer=new AstAnalyser()]
|
|
15
|
+
* @param {function} [options.loadExtensions]
|
|
16
|
+
*/
|
|
17
|
+
constructor(options = {}) {
|
|
18
|
+
this.astAnalyzer = options.astAnalyzer ?? new AstAnalyser();
|
|
19
|
+
this.allowedExtensions = options.loadExtensions
|
|
20
|
+
? options.loadExtensions(kDefaultExtensions)
|
|
21
|
+
: kDefaultExtensions;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Asynchronously analyze a set of entry files yielding analysis reports.
|
|
26
|
+
*
|
|
27
|
+
* @param {(string | URL)[]} entryFiles
|
|
28
|
+
* @yields {Object} - Yields an object containing the analysis report for each file.
|
|
29
|
+
*/
|
|
30
|
+
async* analyse(entryFiles) {
|
|
31
|
+
this.analyzedDeps = new Set();
|
|
32
|
+
|
|
33
|
+
for (const file of entryFiles) {
|
|
34
|
+
yield* this.#analyzeFile(file);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async* #analyzeFile(file) {
|
|
39
|
+
const filePath = file instanceof URL ? file.pathname : file;
|
|
40
|
+
const report = await this.astAnalyzer.analyseFile(file);
|
|
41
|
+
|
|
42
|
+
yield { url: filePath, ...report };
|
|
43
|
+
|
|
44
|
+
if (!report.ok) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
yield* this.#analyzeDeps(report.dependencies, path.dirname(filePath));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
async* #analyzeDeps(deps, basePath) {
|
|
52
|
+
for (const [name] of deps) {
|
|
53
|
+
const depPath = await this.#getInternalDepPath(name, basePath);
|
|
54
|
+
if (depPath && !this.analyzedDeps.has(depPath)) {
|
|
55
|
+
this.analyzedDeps.add(depPath);
|
|
56
|
+
|
|
57
|
+
yield* this.#analyzeFile(depPath);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async #getInternalDepPath(name, basePath) {
|
|
63
|
+
const depPath = path.join(basePath, name);
|
|
64
|
+
const existingExt = path.extname(name);
|
|
65
|
+
if (existingExt !== "") {
|
|
66
|
+
if (!this.allowedExtensions.includes(existingExt.slice(1))) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (await this.#fileExists(depPath)) {
|
|
71
|
+
return depPath;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
for (const ext of this.allowedExtensions) {
|
|
76
|
+
const depPathWithExt = `${depPath}.${ext}`;
|
|
77
|
+
if (await this.#fileExists(depPathWithExt)) {
|
|
78
|
+
return depPathWithExt;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async #fileExists(path) {
|
|
86
|
+
try {
|
|
87
|
+
await fs.access(path, fs.constants.F_OK);
|
|
88
|
+
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
if (error.code !== "ENOENT") {
|
|
93
|
+
throw error;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return false;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
function validateNode(node) {
|
|
10
10
|
return [
|
|
11
11
|
// Note: the source property is the right-side Literal part of the Import
|
|
12
|
-
node.type
|
|
12
|
+
["ImportDeclaration", "ImportExpression"].includes(node.type) && node.source.type === "Literal"
|
|
13
13
|
];
|
|
14
14
|
}
|
|
15
15
|
|
package/src/probes/isLiteral.js
CHANGED
|
@@ -4,13 +4,19 @@ import { builtinModules } from "repl";
|
|
|
4
4
|
// Import Third-party Dependencies
|
|
5
5
|
import { Hex } from "@nodesecure/sec-literal";
|
|
6
6
|
|
|
7
|
+
const kMapRegexIps = Object.freeze({
|
|
8
|
+
regexIPv4: /^(https?:\/\/)(?!127\.)(?!.*:(?:0{1,3}|25[6-9])\.)(?!.*:(?:25[6-9])\.(?:0{1,3}|25[6-9])\.)(?!.*:(?:25[6-9])\.(?:25[6-9])\.(?:0{1,3}|25[6-9])\.)(?!.*:(?:25[6-9])\.(?:25[6-9])\.(?:25[6-9])\.(?:0{1,3}|25[6-9]))((?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])\.){3}(?:\d{1,2}|1\d{2}|2[0-4]\d|25[0-5])(?::\d{1,5})?(\/[^\s]*)?$/,
|
|
9
|
+
regexIPv6: /^(https?:\/\/)(\[[0-9A-Fa-f:]+\])(?::\d{1,5})?(\/[^\s]*)?$/
|
|
10
|
+
});
|
|
11
|
+
|
|
7
12
|
// CONSTANTS
|
|
8
13
|
const kNodeDeps = new Set(builtinModules);
|
|
9
14
|
const kShadyLinkRegExps = [
|
|
15
|
+
kMapRegexIps.regexIPv4,
|
|
16
|
+
kMapRegexIps.regexIPv6,
|
|
10
17
|
/(http[s]?:\/\/bit\.ly.*)$/,
|
|
11
18
|
/(http[s]?:\/\/.*\.(link|xyz|tk|ml|ga|cf|gq|pw|top|club|mw|bd|ke|am|sbs|date|quest|cd|bid|cd|ws|icu|cam|uno|email|stream))$/
|
|
12
19
|
];
|
|
13
|
-
|
|
14
20
|
/**
|
|
15
21
|
* @description Search for Literal AST Node
|
|
16
22
|
* @see https://github.com/estree/estree/blob/master/es5.md#literal
|
package/types/api.d.ts
CHANGED
|
@@ -3,6 +3,12 @@ import { Statement } from "meriyah/dist/src/estree.js";
|
|
|
3
3
|
|
|
4
4
|
export {
|
|
5
5
|
AstAnalyser,
|
|
6
|
+
AstAnalyserOptions,
|
|
7
|
+
|
|
8
|
+
EntryFilesAnalyser,
|
|
9
|
+
EntryFilesAnalyserOptions,
|
|
10
|
+
|
|
11
|
+
JsSourceParser,
|
|
6
12
|
SourceParser,
|
|
7
13
|
runASTAnalysis,
|
|
8
14
|
runASTAnalysisOnFile,
|
|
@@ -98,8 +104,24 @@ interface SourceParser {
|
|
|
98
104
|
declare class AstAnalyser {
|
|
99
105
|
constructor(options?: AstAnalyserOptions);
|
|
100
106
|
analyse: (str: string, options?: RuntimeOptions) => Report;
|
|
101
|
-
|
|
107
|
+
analyseFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface EntryFilesAnalyserOptions {
|
|
111
|
+
astAnalyzer?: AstAnalyser;
|
|
112
|
+
loadExtensions?: (defaults: string[]) => string[];
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
declare class EntryFilesAnalyser {
|
|
116
|
+
constructor(options?: EntryFilesAnalyserOptions);
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Asynchronously analyze a set of entry files yielding analysis reports.
|
|
120
|
+
*/
|
|
121
|
+
analyse(entryFiles: (string | URL)[]): AsyncGenerator<ReportOnFile & { url: string }>;
|
|
102
122
|
}
|
|
103
123
|
|
|
124
|
+
declare class JsSourceParser implements SourceParser {}
|
|
125
|
+
|
|
104
126
|
declare function runASTAnalysis(str: string, options?: RuntimeOptions & AstAnalyserOptions): Report;
|
|
105
127
|
declare function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions & AstAnalyserOptions): Promise<ReportOnFile>;
|