@nodesecure/js-x-ray 6.3.0 → 7.1.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.
Files changed (52) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +106 -18
  3. package/index.d.ts +16 -3
  4. package/index.js +37 -134
  5. package/package.json +9 -9
  6. package/src/AstAnalyser.js +137 -0
  7. package/src/Deobfuscator.js +192 -0
  8. package/src/EntryFilesAnalyser.js +99 -0
  9. package/src/JsSourceParser.js +57 -0
  10. package/src/NodeCounter.js +76 -0
  11. package/src/ProbeRunner.js +140 -0
  12. package/src/{Analysis.js → SourceFile.js} +55 -68
  13. package/src/obfuscators/freejsobfuscator.js +9 -9
  14. package/src/obfuscators/jjencode.js +7 -7
  15. package/src/obfuscators/jsfuck.js +6 -6
  16. package/src/obfuscators/obfuscator-io.js +7 -7
  17. package/src/obfuscators/trojan-source.js +28 -28
  18. package/src/probes/isArrayExpression.js +32 -30
  19. package/src/probes/isBinaryExpression.js +5 -3
  20. package/src/probes/isImportDeclaration.js +8 -5
  21. package/src/probes/isLiteral.js +17 -9
  22. package/src/probes/isLiteralRegex.js +5 -3
  23. package/src/probes/isRegexObject.js +5 -3
  24. package/src/probes/isRequire/RequireCallExpressionWalker.js +93 -0
  25. package/src/probes/isRequire/isRequire.js +142 -0
  26. package/src/probes/isUnsafeCallee.js +15 -5
  27. package/src/probes/isWeakCrypto.js +5 -3
  28. package/src/utils/exportAssignmentHasRequireLeave.js +40 -0
  29. package/src/utils/extractNode.js +14 -0
  30. package/src/utils/index.js +8 -0
  31. package/src/utils/isNode.js +5 -0
  32. package/src/utils/isOneLineExpressionExport.js +18 -0
  33. package/src/utils/isUnsafeCallee.js +28 -0
  34. package/src/utils/notNullOrUndefined.js +3 -0
  35. package/src/utils/rootLocation.js +3 -0
  36. package/src/utils/toArrayLocation.js +11 -0
  37. package/src/warnings.js +1 -1
  38. package/types/api.d.ts +79 -18
  39. package/src/ASTDeps.js +0 -63
  40. package/src/obfuscators/index.js +0 -69
  41. package/src/probes/index.js +0 -70
  42. package/src/probes/isAssignmentExpression.js +0 -29
  43. package/src/probes/isClassDeclaration.js +0 -25
  44. package/src/probes/isFunction.js +0 -38
  45. package/src/probes/isMemberExpression.js +0 -16
  46. package/src/probes/isMethodDefinition.js +0 -25
  47. package/src/probes/isObjectExpression.js +0 -29
  48. package/src/probes/isRequire.js +0 -164
  49. package/src/probes/isUnaryExpression.js +0 -26
  50. package/src/probes/isVariableDeclaration.js +0 -30
  51. package/src/utils.js +0 -48
  52. package/types/astdeps.d.ts +0 -34
@@ -0,0 +1,142 @@
1
+ /* eslint-disable consistent-return */
2
+
3
+ import {
4
+ concatBinaryExpression,
5
+ arrayExpressionToString,
6
+ getCallExpressionIdentifier,
7
+ getCallExpressionArguments
8
+ } from "@nodesecure/estree-ast-utils";
9
+ import { ProbeSignals } from "../../ProbeRunner.js";
10
+ import { RequireCallExpressionWalker } from "./RequireCallExpressionWalker.js";
11
+
12
+ function validateNodeRequire(node, { tracer }) {
13
+ const id = getCallExpressionIdentifier(node, {
14
+ resolveCallExpression: false
15
+ });
16
+ if (id === null) {
17
+ return [false];
18
+ }
19
+
20
+ const data = tracer.getDataFromIdentifier(id);
21
+
22
+ return [
23
+ data !== null && data.name === "require",
24
+ id ?? void 0
25
+ ];
26
+ }
27
+
28
+ function validateNodeEvalRequire(node) {
29
+ const id = getCallExpressionIdentifier(node);
30
+
31
+ if (id !== "eval") {
32
+ return [false];
33
+ }
34
+ if (node.callee.type !== "CallExpression") {
35
+ return [false];
36
+ }
37
+
38
+ const args = getCallExpressionArguments(node.callee);
39
+
40
+ return [
41
+ args.length > 0 && args.at(0) === "require",
42
+ id
43
+ ];
44
+ }
45
+
46
+ function teardown({ sourceFile }) {
47
+ sourceFile.dependencyAutoWarning = false;
48
+ }
49
+
50
+ function main(node, options) {
51
+ const { sourceFile, data: calleeName } = options;
52
+ const { tracer } = sourceFile;
53
+
54
+ if (node.arguments.length === 0) {
55
+ return;
56
+ }
57
+ const arg = node.arguments.at(0);
58
+
59
+ if (calleeName === "eval") {
60
+ sourceFile.dependencyAutoWarning = true;
61
+ }
62
+
63
+ switch (arg.type) {
64
+ // const foo = "http"; require(foo);
65
+ case "Identifier":
66
+ if (sourceFile.tracer.literalIdentifiers.has(arg.name)) {
67
+ sourceFile.addDependency(
68
+ sourceFile.tracer.literalIdentifiers.get(arg.name),
69
+ node.loc
70
+ );
71
+ }
72
+ else {
73
+ sourceFile.addWarning("unsafe-import", null, node.loc);
74
+ }
75
+ break;
76
+
77
+ // require("http")
78
+ case "Literal":
79
+ sourceFile.addDependency(arg.value, node.loc);
80
+ break;
81
+
82
+ // require(["ht", "tp"])
83
+ case "ArrayExpression": {
84
+ const value = [...arrayExpressionToString(arg, { tracer })]
85
+ .join("")
86
+ .trim();
87
+
88
+ if (value === "") {
89
+ sourceFile.addWarning("unsafe-import", null, node.loc);
90
+ }
91
+ else {
92
+ sourceFile.addDependency(value, node.loc);
93
+ }
94
+ break;
95
+ }
96
+
97
+ // require("ht" + "tp");
98
+ case "BinaryExpression": {
99
+ if (arg.operator !== "+") {
100
+ sourceFile.addWarning("unsafe-import", null, node.loc);
101
+ break;
102
+ }
103
+
104
+ try {
105
+ const iter = concatBinaryExpression(arg, {
106
+ tracer, stopOnUnsupportedNode: true
107
+ });
108
+
109
+ sourceFile.addDependency([...iter].join(""), node.loc);
110
+ }
111
+ catch {
112
+ sourceFile.addWarning("unsafe-import", null, node.loc);
113
+ }
114
+ break;
115
+ }
116
+
117
+ // require(Buffer.from("...", "hex").toString());
118
+ case "CallExpression": {
119
+ const walker = new RequireCallExpressionWalker(tracer);
120
+ const { dependencies, triggerWarning } = walker.walk(arg);
121
+ dependencies.forEach((depName) => sourceFile.addDependency(depName, node.loc, true));
122
+
123
+ if (triggerWarning) {
124
+ sourceFile.addWarning("unsafe-import", null, node.loc);
125
+ }
126
+
127
+ // We skip walking the tree to avoid anymore warnings...
128
+ return ProbeSignals.Skip;
129
+ }
130
+
131
+ default:
132
+ sourceFile.addWarning("unsafe-import", null, node.loc);
133
+ }
134
+ }
135
+
136
+ export default {
137
+ name: "isRequire",
138
+ validateNode: [validateNodeRequire, validateNodeEvalRequire],
139
+ main,
140
+ breakOnMatch: true,
141
+ breakGroup: "import"
142
+ };
@@ -1,5 +1,6 @@
1
1
  // Import Internal Dependencies
2
- import { isUnsafeCallee } from "../utils.js";
2
+ import { isUnsafeCallee } from "../utils/index.js";
3
+ import { ProbeSignals } from "../ProbeRunner.js";
3
4
 
4
5
  /**
5
6
  * @description Detect unsafe statement
@@ -12,14 +13,23 @@ function validateNode(node) {
12
13
  }
13
14
 
14
15
  function main(node, options) {
15
- const { analysis, data: calleeName } = options;
16
+ const { sourceFile, data: calleeName } = options;
16
17
 
17
- analysis.addWarning("unsafe-stmt", calleeName, node.loc);
18
+ if (
19
+ calleeName === "Function" &&
20
+ node.callee.arguments.length > 0 &&
21
+ node.callee.arguments[0].value === "return this"
22
+ ) {
23
+ return ProbeSignals.Skip;
24
+ }
25
+ sourceFile.addWarning("unsafe-stmt", calleeName, node.loc);
18
26
 
19
- return Symbol.for("skipWalk");
27
+ return ProbeSignals.Skip;
20
28
  }
21
29
 
22
30
  export default {
23
31
  name: "isUnsafeCallee",
24
- validateNode, main, breakOnMatch: false
32
+ validateNode,
33
+ main,
34
+ breakOnMatch: false
25
35
  };
@@ -21,15 +21,17 @@ function validateNode(node, { tracer }) {
21
21
  return [data !== null && data.identifierOrMemberExpr === "crypto.createHash"];
22
22
  }
23
23
 
24
- function main(node, { analysis }) {
24
+ function main(node, { sourceFile }) {
25
25
  const arg = node.arguments.at(0);
26
26
 
27
27
  if (kWeakAlgorithms.has(arg.value)) {
28
- analysis.addWarning("weak-crypto", arg.value, node.loc);
28
+ sourceFile.addWarning("weak-crypto", arg.value, node.loc);
29
29
  }
30
30
  }
31
31
 
32
32
  export default {
33
33
  name: "isWeakCrypto",
34
- validateNode, main, breakOnMatch: false
34
+ validateNode,
35
+ main,
36
+ breakOnMatch: false
35
37
  };
@@ -0,0 +1,40 @@
1
+ // Import Third-party Dependencies
2
+ import {
3
+ getCallExpressionIdentifier
4
+ } from "@nodesecure/estree-ast-utils";
5
+
6
+ export function exportAssignmentHasRequireLeave(expr) {
7
+ if (expr.type === "LogicalExpression") {
8
+ return atLeastOneBranchHasRequireLeave(expr.left, expr.right);
9
+ }
10
+
11
+ if (expr.type === "ConditionalExpression") {
12
+ return atLeastOneBranchHasRequireLeave(expr.consequent, expr.alternate);
13
+ }
14
+
15
+ if (expr.type === "CallExpression") {
16
+ return getCallExpressionIdentifier(expr) === "require";
17
+ }
18
+
19
+ if (expr.type === "MemberExpression") {
20
+ let rootMember = expr.object;
21
+ while (rootMember.type === "MemberExpression") {
22
+ rootMember = rootMember.object;
23
+ }
24
+
25
+ if (rootMember.type !== "CallExpression") {
26
+ return false;
27
+ }
28
+
29
+ return getCallExpressionIdentifier(rootMember) === "require";
30
+ }
31
+
32
+ return false;
33
+ }
34
+
35
+ function atLeastOneBranchHasRequireLeave(left, right) {
36
+ return [
37
+ exportAssignmentHasRequireLeave(left),
38
+ exportAssignmentHasRequireLeave(right)
39
+ ].some((hasRequire) => hasRequire);
40
+ }
@@ -0,0 +1,14 @@
1
+ // Import Internal Dependencies
2
+ import { notNullOrUndefined } from "./notNullOrUndefined.js";
3
+
4
+ export function extractNode(expectedType) {
5
+ return (callback, nodes) => {
6
+ const finalNodes = Array.isArray(nodes) ? nodes : [nodes];
7
+
8
+ for (const node of finalNodes) {
9
+ if (notNullOrUndefined(node) && node.type === expectedType) {
10
+ callback(node);
11
+ }
12
+ }
13
+ };
14
+ }
@@ -0,0 +1,8 @@
1
+ export * from "./exportAssignmentHasRequireLeave.js";
2
+ export * from "./extractNode.js";
3
+ export * from "./isOneLineExpressionExport.js";
4
+ export * from "./isUnsafeCallee.js";
5
+ export * from "./notNullOrUndefined.js";
6
+ export * from "./rootLocation.js";
7
+ export * from "./toArrayLocation.js";
8
+ export * from "./isNode.js";
@@ -0,0 +1,5 @@
1
+ export function isNode(value) {
2
+ return (
3
+ value !== null && typeof value === "object" && "type" in value && typeof value.type === "string"
4
+ );
5
+ }
@@ -0,0 +1,18 @@
1
+ // Import Internal Dependencies
2
+ import { exportAssignmentHasRequireLeave } from "./exportAssignmentHasRequireLeave.js";
3
+
4
+ export function isOneLineExpressionExport(body) {
5
+ if (body.length > 1) {
6
+ return false;
7
+ }
8
+
9
+ if (body[0].type !== "ExpressionStatement") {
10
+ return false;
11
+ }
12
+
13
+ if (body[0].expression.type !== "AssignmentExpression") {
14
+ return false;
15
+ }
16
+
17
+ return exportAssignmentHasRequireLeave(body[0].expression.right);
18
+ }
@@ -0,0 +1,28 @@
1
+ // Import Third-party Dependencies
2
+ import { getCallExpressionIdentifier } from "@nodesecure/estree-ast-utils";
3
+
4
+ function isEvalCallee(node) {
5
+ const identifier = getCallExpressionIdentifier(node, {
6
+ resolveCallExpression: false
7
+ });
8
+
9
+ return identifier === "eval";
10
+ }
11
+
12
+ function isFunctionCallee(node) {
13
+ const identifier = getCallExpressionIdentifier(node);
14
+
15
+ return identifier === "Function" && node.callee.type === "CallExpression";
16
+ }
17
+
18
+ export function isUnsafeCallee(node) {
19
+ if (isEvalCallee(node)) {
20
+ return [true, "eval"];
21
+ }
22
+
23
+ if (isFunctionCallee(node)) {
24
+ return [true, "Function"];
25
+ }
26
+
27
+ return [false, null];
28
+ }
@@ -0,0 +1,3 @@
1
+ export function notNullOrUndefined(value) {
2
+ return value !== null && value !== void 0;
3
+ }
@@ -0,0 +1,3 @@
1
+ export function rootLocation() {
2
+ return { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } };
3
+ }
@@ -0,0 +1,11 @@
1
+ // Import Internal Dependencies
2
+ import { rootLocation } from "./rootLocation.js";
3
+
4
+ export function toArrayLocation(location = rootLocation()) {
5
+ const { start, end = start } = location;
6
+
7
+ return [
8
+ [start.line || 0, start.column || 0],
9
+ [end.line || 0, end.column || 0]
10
+ ];
11
+ }
package/src/warnings.js CHANGED
@@ -1,5 +1,5 @@
1
1
  // Import Internal Dependencies
2
- import * as utils from "./utils.js";
2
+ import * as utils from "./utils/index.js";
3
3
 
4
4
  export const warnings = Object.freeze({
5
5
  "parsing-error": {
package/types/api.d.ts CHANGED
@@ -1,7 +1,12 @@
1
- import { ASTDeps } from "./astdeps.js";
2
1
  import { Warning } from "./warnings.js";
2
+ import { Statement } from "meriyah/dist/src/estree.js";
3
3
 
4
4
  export {
5
+ AstAnalyser,
6
+ AstAnalyserOptions,
7
+ EntryFilesAnalyser,
8
+ EntryFilesAnalyserOptions,
9
+ SourceParser,
5
10
  runASTAnalysis,
6
11
  runASTAnalysisOnFile,
7
12
 
@@ -9,7 +14,27 @@ export {
9
14
  RuntimeFileOptions,
10
15
 
11
16
  Report,
12
- ReportOnFile
17
+ ReportOnFile,
18
+
19
+ SourceLocation,
20
+ Dependency
21
+ }
22
+
23
+ interface SourceLocation {
24
+ start: {
25
+ line: number;
26
+ column: number;
27
+ };
28
+ end: {
29
+ line: number;
30
+ column: number;
31
+ }
32
+ }
33
+
34
+ interface Dependency {
35
+ unsafe: boolean;
36
+ inTry: boolean;
37
+ location?: null | SourceLocation;
13
38
  }
14
39
 
15
40
  interface RuntimeOptions {
@@ -20,42 +45,78 @@ interface RuntimeOptions {
20
45
  /**
21
46
  * @default false
22
47
  */
23
- isMinified?: boolean;
48
+ removeHTMLComments?: boolean;
24
49
  /**
25
50
  * @default false
26
51
  */
27
- removeHTMLComments?: boolean;
52
+ isMinified?: boolean;
28
53
  }
29
54
 
30
- interface Report {
31
- dependencies: ASTDeps;
32
- warnings: Warning[];
33
- idsLengthAvg: number;
34
- stringScore: number;
35
- isOneLineRequire: boolean;
55
+ interface RuntimeFileOptions extends Omit<RuntimeOptions, "isMinified"> {
56
+ packageName?: string;
36
57
  }
37
58
 
38
- interface RuntimeFileOptions {
39
- packageName?: string;
59
+ interface AstAnalyserOptions {
40
60
  /**
41
- * @default true
61
+ * @default JsSourceParser
42
62
  */
43
- module?: boolean;
63
+ customParser?: SourceParser;
64
+ /**
65
+ * @default []
66
+ */
67
+ customProbes?: Probe[];
44
68
  /**
45
69
  * @default false
46
70
  */
47
- removeHTMLComments?: boolean;
71
+ skipDefaultProbes?: boolean;
72
+ }
73
+
74
+ interface Probe {
75
+ validateNode: Function | Function[];
76
+ main: Function;
77
+ }
78
+
79
+ interface Report {
80
+ dependencies: Map<string, Dependency>;
81
+ warnings: Warning[];
82
+ idsLengthAvg: number;
83
+ stringScore: number;
84
+ isOneLineRequire: boolean;
48
85
  }
49
86
 
50
87
  type ReportOnFile = {
51
88
  ok: true,
52
89
  warnings: Warning[];
53
- dependencies: ASTDeps;
90
+ dependencies: Map<string, Dependency>;
54
91
  isMinified: boolean;
55
92
  } | {
56
93
  ok: false,
57
94
  warnings: Warning[];
58
95
  }
59
96
 
60
- declare function runASTAnalysis(str: string, options?: RuntimeOptions): Report;
61
- declare function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
97
+ interface SourceParser {
98
+ parse(source: string, options: unknown): Statement[];
99
+ }
100
+
101
+ declare class AstAnalyser {
102
+ constructor(options?: AstAnalyserOptions);
103
+ analyse: (str: string, options?: RuntimeOptions) => Report;
104
+ analyzeFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
105
+ }
106
+
107
+ interface EntryFilesAnalyserOptions {
108
+ astAnalyzer?: AstAnalyser;
109
+ loadExtensions?: (defaults: string[]) => string[];
110
+ }
111
+
112
+ declare class EntryFilesAnalyser {
113
+ constructor(options?: EntryFilesAnalyserOptions);
114
+
115
+ /**
116
+ * Asynchronously analyze a set of entry files yielding analysis reports.
117
+ */
118
+ analyse(entryFiles: (string | URL)[]): AsyncGenerator<ReportOnFile & { url: string }>;
119
+ }
120
+
121
+ declare function runASTAnalysis(str: string, options?: RuntimeOptions & AstAnalyserOptions): Report;
122
+ declare function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions & AstAnalyserOptions): Promise<ReportOnFile>;
package/src/ASTDeps.js DELETED
@@ -1,63 +0,0 @@
1
-
2
- export default class ASTDeps {
3
- #inTry = false;
4
- dependencies = Object.create(null);
5
-
6
- get isInTryStmt() {
7
- return this.#inTry;
8
- }
9
-
10
- set isInTryStmt(value) {
11
- if (typeof value !== "boolean") {
12
- throw new TypeError("value must be a boolean!");
13
- }
14
-
15
- this.#inTry = value;
16
- }
17
-
18
- removeByName(name) {
19
- if (Reflect.has(this.dependencies, name)) {
20
- delete this.dependencies[name];
21
- }
22
- }
23
-
24
- add(depName, location = null, unsafe = false) {
25
- if (typeof depName !== "string" || depName.trim() === "") {
26
- return;
27
- }
28
-
29
- const cleanDepName = depName.charAt(depName.length - 1) === "/" ? depName.slice(0, -1) : depName;
30
- const dep = {
31
- unsafe,
32
- inTry: this.isInTryStmt
33
- };
34
- if (location !== null) {
35
- dep.location = location;
36
- }
37
- this.dependencies[cleanDepName] = dep;
38
- }
39
-
40
- has(depName) {
41
- if (depName.trim() === "") {
42
- return false;
43
- }
44
-
45
- return Reflect.has(this.dependencies, depName);
46
- }
47
-
48
- get size() {
49
- return Object.keys(this.dependencies).length;
50
- }
51
-
52
- * getDependenciesInTryStatement() {
53
- for (const [depName, props] of Object.entries(this.dependencies)) {
54
- if (props.inTry === true && props.unsafe === false) {
55
- yield depName;
56
- }
57
- }
58
- }
59
-
60
- * [Symbol.iterator]() {
61
- yield* Object.keys(this.dependencies);
62
- }
63
- }
@@ -1,69 +0,0 @@
1
- // Import Third-party Dependencies
2
- import { Patterns } from "@nodesecure/sec-literal";
3
-
4
- // Import Internal Dependencies
5
- import * as jjencode from "./jjencode.js";
6
- import * as jsfuck from "./jsfuck.js";
7
- import * as freejsobfuscator from "./freejsobfuscator.js";
8
- import * as obfuscatorio from "./obfuscator-io.js";
9
- import * as trojan from "./trojan-source.js";
10
-
11
- // CONSTANTS
12
- const kMinimumIdsCount = 5;
13
-
14
- export function isObfuscatedCode(analysis) {
15
- let encoderName = null;
16
-
17
- if (jsfuck.verify(analysis)) {
18
- encoderName = "jsfuck";
19
- }
20
- else if (jjencode.verify(analysis)) {
21
- encoderName = "jjencode";
22
- }
23
- else if (analysis.counter.morseLiteral >= 36) {
24
- encoderName = "morse";
25
- }
26
- else {
27
- // TODO: also implement Dictionnary checkup
28
- const identifiers = analysis.identifiersName
29
- .map((value) => value?.name ?? null)
30
- .filter((name) => typeof name === "string");
31
-
32
- const { prefix, oneTimeOccurence } = Patterns.commonHexadecimalPrefix(
33
- identifiers
34
- );
35
- const uPrefixNames = new Set(Object.keys(prefix));
36
-
37
- if (analysis.counter.identifiers > kMinimumIdsCount && uPrefixNames.size > 0) {
38
- analysis.hasPrefixedIdentifiers = calcAvgPrefixedIdentifiers(analysis, prefix) > 80;
39
- }
40
-
41
- if (uPrefixNames.size === 1 && freejsobfuscator.verify(analysis, prefix)) {
42
- encoderName = "freejsobfuscator";
43
- }
44
- else if (obfuscatorio.verify(analysis)) {
45
- encoderName = "obfuscator.io";
46
- }
47
- // else if ((analysis.counter.identifiers > (kMinimumIdsCount * 3) && analysis.hasPrefixedIdentifiers)
48
- // && (oneTimeOccurence <= 3 || analysis.counter.encodedArrayValue > 0)) {
49
- // encoderName = "unknown";
50
- // }
51
- }
52
-
53
- return [encoderName !== null, encoderName];
54
- }
55
-
56
- export function hasTrojanSource(sourceString) {
57
- return trojan.verify(sourceString);
58
- }
59
-
60
- function calcAvgPrefixedIdentifiers(analysis, prefix) {
61
- const valuesArr = Object.values(prefix).slice().sort((left, right) => left - right);
62
- if (valuesArr.length === 0) {
63
- return 0;
64
- }
65
- const nbOfPrefixedIds = valuesArr.length === 1 ? valuesArr.pop() : (valuesArr.pop() + valuesArr.pop());
66
- const maxIds = analysis.counter.identifiers - analysis.idtypes.property;
67
-
68
- return ((nbOfPrefixedIds / maxIds) * 100);
69
- }
@@ -1,70 +0,0 @@
1
- // Import all the probes
2
- import isUnsafeCallee from "./isUnsafeCallee.js";
3
- import isLiteral from "./isLiteral.js";
4
- import isLiteralRegex from "./isLiteralRegex.js";
5
- import isRegexObject from "./isRegexObject.js";
6
- import isVariableDeclaration from "./isVariableDeclaration.js";
7
- import isRequire from "./isRequire.js";
8
- import isImportDeclaration from "./isImportDeclaration.js";
9
- import isMemberExpression from "./isMemberExpression.js";
10
- import isArrayExpression from "./isArrayExpression.js";
11
- import isFunction from "./isFunction.js";
12
- import isAssignmentExpression from "./isAssignmentExpression.js";
13
- import isObjectExpression from "./isObjectExpression.js";
14
- import isUnaryExpression from "./isUnaryExpression.js";
15
- import isWeakCrypto from "./isWeakCrypto.js";
16
- import isClassDeclaration from "./isClassDeclaration.js";
17
- import isMethodDefinition from "./isMethodDefinition.js";
18
-
19
- // CONSTANTS
20
- const kListOfProbes = [
21
- isUnsafeCallee,
22
- isLiteral,
23
- isLiteralRegex,
24
- isRegexObject,
25
- isVariableDeclaration,
26
- isRequire,
27
- isImportDeclaration,
28
- isMemberExpression,
29
- isAssignmentExpression,
30
- isObjectExpression,
31
- isArrayExpression,
32
- isFunction,
33
- isUnaryExpression,
34
- isWeakCrypto,
35
- isClassDeclaration,
36
- isMethodDefinition
37
- ];
38
-
39
- const kSymBreak = Symbol.for("breakWalk");
40
- const kSymSkip = Symbol.for("skipWalk");
41
-
42
- export function runOnProbes(node, analysis) {
43
- const breakedGroups = new Set();
44
-
45
- for (const probe of kListOfProbes) {
46
- if (breakedGroups.has(probe.breakGroup)) {
47
- continue;
48
- }
49
-
50
- const [isMatching, data = null] = probe.validateNode(node, analysis);
51
- if (isMatching) {
52
- const result = probe.main(node, { analysis, data });
53
-
54
- if (result === kSymSkip) {
55
- return "skip";
56
- }
57
- if (result === kSymBreak || probe.breakOnMatch) {
58
- const breakGroup = probe.breakGroup || null;
59
- if (breakGroup === null) {
60
- break;
61
- }
62
- else {
63
- breakedGroups.add(breakGroup);
64
- }
65
- }
66
- }
67
- }
68
-
69
- return null;
70
- }