@nodesecure/js-x-ray 4.2.0 → 4.4.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 (5) hide show
  1. package/README.md +32 -0
  2. package/index.d.ts +124 -92
  3. package/index.js +46 -14
  4. package/package.json +10 -10
  5. package/src/Analysis.js +152 -152
package/README.md CHANGED
@@ -73,6 +73,38 @@ The analysis will return: `http` (in try), `crypto`, `util` and `fs`.
73
73
 
74
74
  > ⚠️ There is also a lot of suspicious code example in the root cases directory. Feel free to try the tool on these files.
75
75
 
76
+ ## Warnings
77
+
78
+ This section describes how use `warnings` export.
79
+
80
+ The structure of the `warnings` is as follows:
81
+ ```
82
+ /**
83
+ * @property {object} warnings - The default values for Constants.
84
+ * @property {string} warnings[name] - The default warning name (parsingError, unsafeImport etc...).
85
+ * @property {string} warnings[name].i18n - i18n token.
86
+ * @property {string} warnings[name].code - Used to perform unit tests.
87
+ */
88
+
89
+ export const warnings = Object.freeze({
90
+ parsingError: {
91
+ i18n: "sast_warnings.ast_error"
92
+ code: "ast-error",
93
+ },
94
+ ...otherWarnings
95
+ });
96
+ ```
97
+
98
+ We make a call to `i18n` through the package `NodeSecure/i18n` to get the translation.
99
+
100
+ ```
101
+ import * as jsxray from "@nodesecure/js-x-ray";
102
+ import * as i18n from "@nodesecure/i18n";
103
+
104
+ console.log(i18n.getToken(jsxray.warnings.parsingError.i18n));
105
+
106
+ ```
107
+
76
108
  ## Warnings Legends (v2.0+)
77
109
 
78
110
  > Node-secure versions equal or lower than 0.7.0 are no longer compatible with the warnings table below.
package/index.d.ts CHANGED
@@ -1,92 +1,124 @@
1
- /// <reference types="meriyah"/>
2
-
3
- import { SourceLocation } from "meriyah/dist/estree";
4
-
5
- declare class ASTDeps {
6
- constructor();
7
- removeByName(name: string): void;
8
- add(depName: string): void;
9
- getDependenciesInTryStatement(): IterableIterator<string>;
10
-
11
- public isInTryStmt: boolean;
12
- public dependencies: Record<string, JSXRay.Dependency>;
13
- public readonly size: number;
14
- }
15
-
16
- declare namespace JSXRay {
17
- type kindWithValue = "parsing-error"
18
- | "encoded-literal"
19
- | "unsafe-regex"
20
- | "unsafe-stmt"
21
- | "unsafe-assign"
22
- | "short-identifiers"
23
- | "suspicious-literal"
24
- | "obfuscated-code";
25
-
26
- type WarningLocation = [[number, number], [number, number]];
27
- interface BaseWarning {
28
- kind: "unsafe-import" | kindWithValue;
29
- file?: string;
30
- value: string;
31
- location: WarningLocation | WarningLocation[];
32
- }
33
-
34
- type Warning<T extends BaseWarning> = T extends { kind: kindWithValue } ? T : Omit<T, "value">;
35
-
36
- interface Report {
37
- dependencies: ASTDeps;
38
- warnings: Warning<BaseWarning>[];
39
- idsLengthAvg: number;
40
- stringScore: number;
41
- isOneLineRequire: boolean;
42
- }
43
-
44
- interface Dependency {
45
- unsafe: boolean;
46
- inTry: boolean;
47
- location?: SourceLocation;
48
- };
49
-
50
- interface WarningsNames {
51
- parsingError: "parsing-error",
52
- unsafeImport: "unsafe-import",
53
- unsafeStmt: "unsafe-stmt",
54
- unsafeRegex: "unsafe-regex",
55
- unsafeAssign: "unsafe-assign",
56
- encodedLiteral: "encoded-literal",
57
- shortIdentifiers: "short-identifiers",
58
- suspiciousLiteral: "suspicious-literal",
59
- obfuscatedCode: "obfuscated-code"
60
- }
61
-
62
- interface RuntimeOptions {
63
- module?: boolean;
64
- isMinified?: boolean;
65
- }
66
-
67
- export function runASTAnalysis(str: string, options?: RuntimeOptions): Report;
68
-
69
- export type ReportOnFile = {
70
- ok: true,
71
- warnings: Warning<BaseWarning>[];
72
- dependencies: ASTDeps;
73
- isMinified: boolean;
74
- } | {
75
- ok: false,
76
- warnings: Warning<BaseWarning>[];
77
- }
78
-
79
- export interface RuntimeFileOptions {
80
- packageName?: string;
81
- module?: boolean;
82
- }
83
-
84
- export function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
85
-
86
- export namespace CONSTANTS {
87
- export const Warnings: WarningsNames;
88
- }
89
- }
90
-
91
- export = JSXRay;
92
- export as namespace JSXRay;
1
+ declare class ASTDeps {
2
+ constructor();
3
+ removeByName(name: string): void;
4
+ add(depName: string): void;
5
+ getDependenciesInTryStatement(): IterableIterator<string>;
6
+
7
+ public isInTryStmt: boolean;
8
+ public dependencies: Record<string, JSXRay.Dependency>;
9
+ public readonly size: number;
10
+ }
11
+
12
+ declare namespace JSXRay {
13
+ type kindWithValue = "parsing-error"
14
+ | "encoded-literal"
15
+ | "unsafe-regex"
16
+ | "unsafe-stmt"
17
+ | "unsafe-assign"
18
+ | "short-identifiers"
19
+ | "suspicious-literal"
20
+ | "obfuscated-code";
21
+
22
+ type WarningLocation = [[number, number], [number, number]];
23
+ interface BaseWarning {
24
+ kind: "unsafe-import" | kindWithValue;
25
+ file?: string;
26
+ value: string;
27
+ location: WarningLocation | WarningLocation[];
28
+ }
29
+
30
+ type Warning<T extends BaseWarning> = T extends { kind: kindWithValue } ? T : Omit<T, "value">;
31
+
32
+ interface Report {
33
+ dependencies: ASTDeps;
34
+ warnings: Warning<BaseWarning>[];
35
+ idsLengthAvg: number;
36
+ stringScore: number;
37
+ isOneLineRequire: boolean;
38
+ }
39
+
40
+ interface SourceLocation {
41
+ start: {
42
+ line: number;
43
+ column: number;
44
+ };
45
+ end: {
46
+ line: number;
47
+ column: number;
48
+ }
49
+ }
50
+
51
+ interface Dependency {
52
+ unsafe: boolean;
53
+ inTry: boolean;
54
+ location?: SourceLocation;
55
+ }
56
+
57
+ interface WarningsNames {
58
+ parsingError: {
59
+ code: "ast-error",
60
+ i18n: "sast_warnings.ast_error"
61
+ },
62
+ unsafeImport: {
63
+ code: "unsafe-import",
64
+ i18n: "sast_warnings.unsafe_import"
65
+ },
66
+ unsafeRegex: {
67
+ code: "unsafe-regex",
68
+ i18n: "sast_warnings.unsafe_regex"
69
+ },
70
+ unsafeStmt: {
71
+ code: "unsafe-stmt",
72
+ i18n: "sast_warnings.unsafe_stmt"
73
+ },
74
+ unsafeAssign: {
75
+ code: "unsafe-assign",
76
+ i18n: "sast_warnings.unsafe_assign"
77
+ },
78
+ encodedLiteral: {
79
+ code: "encoded-literal",
80
+ i18n: "sast_warnings.encoded_literal"
81
+ },
82
+ shortIdentifiers: {
83
+ code: "short-identifiers",
84
+ i18n: "sast_warnings.short_identifiers"
85
+ },
86
+ suspiciousLiteral: {
87
+ code: "suspicious-literal",
88
+ i18n: "sast_warnings.suspicious_literal"
89
+ },
90
+ obfuscatedCode: {
91
+ code: "obfuscated-code",
92
+ i18n: "sast_warnings.obfuscated_code"
93
+ }
94
+ }
95
+
96
+ interface RuntimeOptions {
97
+ module?: boolean;
98
+ isMinified?: boolean;
99
+ }
100
+
101
+ export function runASTAnalysis(str: string, options?: RuntimeOptions): Report;
102
+
103
+ export type ReportOnFile = {
104
+ ok: true,
105
+ warnings: Warning<BaseWarning>[];
106
+ dependencies: ASTDeps;
107
+ isMinified: boolean;
108
+ } | {
109
+ ok: false,
110
+ warnings: Warning<BaseWarning>[];
111
+ }
112
+
113
+ export interface RuntimeFileOptions {
114
+ packageName?: string;
115
+ module?: boolean;
116
+ }
117
+
118
+ export function runASTAnalysisOnFile(pathToFile: string, options?: RuntimeFileOptions): Promise<ReportOnFile>;
119
+
120
+ export const warnings: WarningsNames;
121
+ }
122
+
123
+ export = JSXRay;
124
+ export as namespace JSXRay;
package/index.js CHANGED
@@ -16,8 +16,13 @@ export function runASTAnalysis(str, options = Object.create(null)) {
16
16
  // Note: if the file start with a shebang then we remove it because 'parseScript' may fail to parse it.
17
17
  // Example: #!/usr/bin/env node
18
18
  const strToAnalyze = str.charAt(0) === "#" ? str.slice(str.indexOf("\n")) : str;
19
+ const isEcmaScriptModule = Boolean(module);
19
20
  const { body } = meriyah.parseScript(strToAnalyze, {
20
- next: true, loc: true, raw: true, module: Boolean(module)
21
+ next: true,
22
+ loc: true,
23
+ raw: true,
24
+ module: isEcmaScriptModule,
25
+ globalReturn: !isEcmaScriptModule
21
26
  });
22
27
 
23
28
  const sastAnalysis = new Analysis();
@@ -78,16 +83,43 @@ export async function runASTAnalysisOnFile(pathToFile, options = {}) {
78
83
  }
79
84
  }
80
85
 
81
- export const CONSTANTS = {
82
- Warnings: Object.freeze({
83
- parsingError: "ast-error",
84
- unsafeImport: "unsafe-import",
85
- unsafeRegex: "unsafe-regex",
86
- unsafeStmt: "unsafe-stmt",
87
- unsafeAssign: "unsafe-assign",
88
- encodedLiteral: "encoded-literal",
89
- shortIdentifiers: "short-identifiers",
90
- suspiciousLiteral: "suspicious-literal",
91
- obfuscatedCode: "obfuscated-code"
92
- })
93
- };
86
+ export const warnings = Object.freeze({
87
+ parsingError: {
88
+ code: "ast-error",
89
+ i18n: "sast_warnings.ast_error"
90
+ },
91
+ unsafeImport: {
92
+ code: "unsafe-import",
93
+ i18n: "sast_warnings.unsafe_import"
94
+ },
95
+ unsafeRegex: {
96
+ code: "unsafe-regex",
97
+ i18n: "sast_warnings.unsafe_regex"
98
+ },
99
+ unsafeStmt: {
100
+ code: "unsafe-stmt",
101
+ i18n: "sast_warnings.unsafe_stmt"
102
+ },
103
+ unsafeAssign: {
104
+ code: "unsafe-assign",
105
+ i18n: "sast_warnings.unsafe_assign"
106
+ },
107
+ encodedLiteral: {
108
+ code: "encoded-literal",
109
+ i18n: "sast_warnings.encoded_literal"
110
+ },
111
+ shortIdentifiers: {
112
+ code: "short-identifiers",
113
+ i18n: "sast_warnings.short_identifiers"
114
+ },
115
+ suspiciousLiteral: {
116
+ code: "suspicious-literal",
117
+ i18n: "sast_warnings.suspicious_literal"
118
+ },
119
+ obfuscatedCode: {
120
+ code: "obfuscated-code",
121
+ i18n: "sast_warnings.obfuscated_code"
122
+ }
123
+ });
124
+
125
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nodesecure/js-x-ray",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "description": "JavaScript AST XRay analysis",
5
5
  "type": "module",
6
6
  "exports": "./index.js",
@@ -37,21 +37,21 @@
37
37
  },
38
38
  "homepage": "https://github.com/NodeSecure/js-x-ray#readme",
39
39
  "dependencies": {
40
- "@nodesecure/sec-literal": "^1.0.1",
41
- "estree-walker": "^3.0.0",
40
+ "@nodesecure/sec-literal": "^1.1.0",
41
+ "estree-walker": "^3.0.1",
42
42
  "is-minified-code": "^2.0.0",
43
- "meriyah": "^4.2.0",
43
+ "meriyah": "^4.2.1",
44
44
  "safe-regex": "^2.1.1"
45
45
  },
46
46
  "devDependencies": {
47
- "@nodesecure/eslint-config": "^1.3.0",
47
+ "@nodesecure/eslint-config": "^1.3.1",
48
48
  "@slimio/is": "^1.5.1",
49
- "@small-tech/esm-tape-runner": "^1.0.3",
49
+ "@small-tech/esm-tape-runner": "^2.0.0",
50
50
  "@small-tech/tap-monkey": "^1.3.0",
51
- "@types/node": "^16.11.10",
51
+ "@types/node": "^17.0.31",
52
52
  "cross-env": "^7.0.3",
53
- "eslint": "^8.3.0",
54
- "pkg-ok": "^2.3.1",
55
- "tape": "^5.3.2"
53
+ "eslint": "^8.15.0",
54
+ "pkg-ok": "^3.0.0",
55
+ "tape": "^5.5.3"
56
56
  }
57
57
  }
package/src/Analysis.js CHANGED
@@ -1,152 +1,152 @@
1
- // Import Third-party Dependencies
2
- import { Utils, Literal } from "@nodesecure/sec-literal";
3
-
4
- // Import Internal Dependencies
5
- import { rootLocation, toArrayLocation, generateWarning } from "./utils.js";
6
- import { warnings as _warnings, processMainModuleRequire } from "./constants.js";
7
- import ASTDeps from "./ASTDeps.js";
8
- import { isObfuscatedCode, hasTrojanSource } from "./obfuscators/index.js";
9
- import { runOnProbes } from "./probes/index.js";
10
-
11
- // CONSTANTS
12
- const kDictionaryStrParts = [
13
- "abcdefghijklmnopqrstuvwxyz",
14
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
15
- "0123456789"
16
- ];
17
-
18
- const kWarningsNameStr = Object.freeze({
19
- [_warnings.parsingError]: "parsing-error",
20
- [_warnings.unsafeImport]: "unsafe-import",
21
- [_warnings.unsafeRegex]: "unsafe-regex",
22
- [_warnings.unsafeStmt]: "unsafe-stmt",
23
- [_warnings.unsafeAssign]: "unsafe-assign",
24
- [_warnings.encodedLiteral]: "encoded-literal",
25
- [_warnings.shortIdentifiers]: "short-identifiers",
26
- [_warnings.suspiciousLiteral]: "suspicious-literal",
27
- [_warnings.obfuscatedCode]: "obfuscated-code"
28
- });
29
-
30
- export default class Analysis {
31
- hasDictionaryString = false;
32
- hasPrefixedIdentifiers = false;
33
- varkinds = { var: 0, let: 0, const: 0 };
34
- idtypes = { assignExpr: 0, property: 0, variableDeclarator: 0, functionDeclaration: 0 };
35
- counter = {
36
- identifiers: 0,
37
- doubleUnaryArray: 0,
38
- computedMemberExpr: 0,
39
- memberExpr: 0,
40
- deepBinaryExpr: 0,
41
- encodedArrayValue: 0,
42
- morseLiteral: 0
43
- };
44
- identifiersName = [];
45
-
46
- constructor() {
47
- this.dependencies = new ASTDeps();
48
-
49
- this.identifiers = new Map();
50
- this.globalParts = new Map();
51
- this.handledEncodedLiteralValues = new Map();
52
-
53
- this.requireIdentifiers = new Set(["require", processMainModuleRequire]);
54
- this.warnings = [];
55
- this.literalScores = [];
56
- }
57
-
58
- addWarning(symbol, value, location = rootLocation()) {
59
- if (symbol === _warnings.encodedLiteral && this.handledEncodedLiteralValues.has(value)) {
60
- const index = this.handledEncodedLiteralValues.get(value);
61
- this.warnings[index].location.push(toArrayLocation(location));
62
-
63
- return;
64
- }
65
- const warningName = kWarningsNameStr[symbol];
66
- this.warnings.push(generateWarning(warningName, { value, location }));
67
- if (symbol === _warnings.encodedLiteral) {
68
- this.handledEncodedLiteralValues.set(value, this.warnings.length - 1);
69
- }
70
- }
71
-
72
- analyzeSourceString(sourceString) {
73
- if (hasTrojanSource(sourceString)) {
74
- this.addWarning(_warnings.obfuscatedCode, "trojan-source");
75
- }
76
- }
77
-
78
- analyzeString(str) {
79
- const score = Utils.stringSuspicionScore(str);
80
- if (score !== 0) {
81
- this.literalScores.push(score);
82
- }
83
-
84
- if (!this.hasDictionaryString) {
85
- const isDictionaryStr = kDictionaryStrParts.every((word) => str.includes(word));
86
- if (isDictionaryStr) {
87
- this.hasDictionaryString = true;
88
- }
89
- }
90
-
91
- // Searching for morse string like "--.- --.--."
92
- if (Utils.stringCharDiversity(str, ["\n"]) >= 3 && Utils.isMorse(str)) {
93
- this.counter.morseLiteral++;
94
- }
95
- }
96
-
97
- analyzeLiteral(node, inArrayExpr = false) {
98
- if (typeof node.value !== "string" || Utils.isSvg(node)) {
99
- return;
100
- }
101
- this.analyzeString(node.value);
102
-
103
- const { hasHexadecimalSequence, hasUnicodeSequence, isBase64 } = Literal.defaultAnalysis(node);
104
- if ((hasHexadecimalSequence || hasUnicodeSequence) && isBase64) {
105
- if (inArrayExpr) {
106
- this.counter.encodedArrayValue++;
107
- }
108
- else {
109
- this.addWarning(_warnings.encodedLiteral, node.value, node.loc);
110
- }
111
- }
112
- }
113
-
114
- getResult(isMinified) {
115
- this.counter.identifiers = this.identifiersName.length;
116
- const [isObfuscated, kind] = isObfuscatedCode(this);
117
- if (isObfuscated) {
118
- this.addWarning(_warnings.obfuscatedCode, kind || "unknown");
119
- }
120
-
121
- const identifiersLengthArr = this.identifiersName
122
- .filter((value) => value.type !== "property" && typeof value.name === "string").map((value) => value.name.length);
123
-
124
- const [idsLengthAvg, stringScore] = [sum(identifiersLengthArr), sum(this.literalScores)];
125
- if (!isMinified && identifiersLengthArr.length > 5 && idsLengthAvg <= 1.5) {
126
- this.addWarning(_warnings.shortIdentifiers, idsLengthAvg);
127
- }
128
- if (stringScore >= 3) {
129
- this.addWarning(_warnings.suspiciousLiteral, stringScore);
130
- }
131
-
132
- return { idsLengthAvg, stringScore, warnings: this.warnings };
133
- }
134
-
135
- walk(node) {
136
- // Detect TryStatement and CatchClause to known which dependency is required in a Try {} clause
137
- if (node.type === "TryStatement" && typeof node.handler !== "undefined") {
138
- this.dependencies.isInTryStmt = true;
139
- }
140
- else if (node.type === "CatchClause") {
141
- this.dependencies.isInTryStmt = false;
142
- }
143
-
144
- return runOnProbes(node, this);
145
- }
146
- }
147
-
148
- function sum(arr = []) {
149
- return arr.length === 0 ? 0 : (arr.reduce((prev, curr) => prev + curr, 0) / arr.length);
150
- }
151
-
152
- Analysis.Warnings = _warnings;
1
+ // Import Third-party Dependencies
2
+ import { Utils, Literal } from "@nodesecure/sec-literal";
3
+
4
+ // Import Internal Dependencies
5
+ import { rootLocation, toArrayLocation, generateWarning } from "./utils.js";
6
+ import { warnings as _warnings, processMainModuleRequire } from "./constants.js";
7
+ import ASTDeps from "./ASTDeps.js";
8
+ import { isObfuscatedCode, hasTrojanSource } from "./obfuscators/index.js";
9
+ import { runOnProbes } from "./probes/index.js";
10
+
11
+ // CONSTANTS
12
+ const kDictionaryStrParts = [
13
+ "abcdefghijklmnopqrstuvwxyz",
14
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ",
15
+ "0123456789"
16
+ ];
17
+
18
+ const kWarningsNameStr = Object.freeze({
19
+ [_warnings.parsingError]: "parsing-error",
20
+ [_warnings.unsafeImport]: "unsafe-import",
21
+ [_warnings.unsafeRegex]: "unsafe-regex",
22
+ [_warnings.unsafeStmt]: "unsafe-stmt",
23
+ [_warnings.unsafeAssign]: "unsafe-assign",
24
+ [_warnings.encodedLiteral]: "encoded-literal",
25
+ [_warnings.shortIdentifiers]: "short-identifiers",
26
+ [_warnings.suspiciousLiteral]: "suspicious-literal",
27
+ [_warnings.obfuscatedCode]: "obfuscated-code"
28
+ });
29
+
30
+ export default class Analysis {
31
+ hasDictionaryString = false;
32
+ hasPrefixedIdentifiers = false;
33
+ varkinds = { var: 0, let: 0, const: 0 };
34
+ idtypes = { assignExpr: 0, property: 0, variableDeclarator: 0, functionDeclaration: 0 };
35
+ counter = {
36
+ identifiers: 0,
37
+ doubleUnaryArray: 0,
38
+ computedMemberExpr: 0,
39
+ memberExpr: 0,
40
+ deepBinaryExpr: 0,
41
+ encodedArrayValue: 0,
42
+ morseLiteral: 0
43
+ };
44
+ identifiersName = [];
45
+
46
+ constructor() {
47
+ this.dependencies = new ASTDeps();
48
+
49
+ this.identifiers = new Map();
50
+ this.globalParts = new Map();
51
+ this.handledEncodedLiteralValues = new Map();
52
+
53
+ this.requireIdentifiers = new Set(["require", processMainModuleRequire]);
54
+ this.warnings = [];
55
+ this.literalScores = [];
56
+ }
57
+
58
+ addWarning(symbol, value, location = rootLocation()) {
59
+ if (symbol === _warnings.encodedLiteral && this.handledEncodedLiteralValues.has(value)) {
60
+ const index = this.handledEncodedLiteralValues.get(value);
61
+ this.warnings[index].location.push(toArrayLocation(location));
62
+
63
+ return;
64
+ }
65
+ const warningName = kWarningsNameStr[symbol];
66
+ this.warnings.push(generateWarning(warningName, { value, location }));
67
+ if (symbol === _warnings.encodedLiteral) {
68
+ this.handledEncodedLiteralValues.set(value, this.warnings.length - 1);
69
+ }
70
+ }
71
+
72
+ analyzeSourceString(sourceString) {
73
+ if (hasTrojanSource(sourceString)) {
74
+ this.addWarning(_warnings.obfuscatedCode, "trojan-source");
75
+ }
76
+ }
77
+
78
+ analyzeString(str) {
79
+ const score = Utils.stringSuspicionScore(str);
80
+ if (score !== 0) {
81
+ this.literalScores.push(score);
82
+ }
83
+
84
+ if (!this.hasDictionaryString) {
85
+ const isDictionaryStr = kDictionaryStrParts.every((word) => str.includes(word));
86
+ if (isDictionaryStr) {
87
+ this.hasDictionaryString = true;
88
+ }
89
+ }
90
+
91
+ // Searching for morse string like "--.- --.--."
92
+ if (Utils.stringCharDiversity(str, ["\n"]) >= 3 && Utils.isMorse(str)) {
93
+ this.counter.morseLiteral++;
94
+ }
95
+ }
96
+
97
+ analyzeLiteral(node, inArrayExpr = false) {
98
+ if (typeof node.value !== "string" || Utils.isSvg(node)) {
99
+ return;
100
+ }
101
+ this.analyzeString(node.value);
102
+
103
+ const { hasHexadecimalSequence, hasUnicodeSequence, isBase64 } = Literal.defaultAnalysis(node);
104
+ if ((hasHexadecimalSequence || hasUnicodeSequence) && isBase64) {
105
+ if (inArrayExpr) {
106
+ this.counter.encodedArrayValue++;
107
+ }
108
+ else {
109
+ this.addWarning(_warnings.encodedLiteral, node.value, node.loc);
110
+ }
111
+ }
112
+ }
113
+
114
+ getResult(isMinified) {
115
+ this.counter.identifiers = this.identifiersName.length;
116
+ const [isObfuscated, kind] = isObfuscatedCode(this);
117
+ if (isObfuscated) {
118
+ this.addWarning(_warnings.obfuscatedCode, kind || "unknown");
119
+ }
120
+
121
+ const identifiersLengthArr = this.identifiersName
122
+ .filter((value) => value.type !== "property" && typeof value.name === "string").map((value) => value.name.length);
123
+
124
+ const [idsLengthAvg, stringScore] = [sum(identifiersLengthArr), sum(this.literalScores)];
125
+ if (!isMinified && identifiersLengthArr.length > 5 && idsLengthAvg <= 1.5) {
126
+ this.addWarning(_warnings.shortIdentifiers, idsLengthAvg);
127
+ }
128
+ if (stringScore >= 3) {
129
+ this.addWarning(_warnings.suspiciousLiteral, stringScore);
130
+ }
131
+
132
+ return { idsLengthAvg, stringScore, warnings: this.warnings };
133
+ }
134
+
135
+ walk(node) {
136
+ // Detect TryStatement and CatchClause to known which dependency is required in a Try {} clause
137
+ if (node.type === "TryStatement" && typeof node.handler !== "undefined") {
138
+ this.dependencies.isInTryStmt = true;
139
+ }
140
+ else if (node.type === "CatchClause") {
141
+ this.dependencies.isInTryStmt = false;
142
+ }
143
+
144
+ return runOnProbes(node, this);
145
+ }
146
+ }
147
+
148
+ function sum(arr = []) {
149
+ return arr.length === 0 ? 0 : (arr.reduce((prev, curr) => prev + curr, 0) / arr.length);
150
+ }
151
+
152
+ Analysis.Warnings = _warnings;