@nodesecure/js-x-ray 4.2.1 → 4.3.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/index.js +98 -93
- package/package.json +8 -8
- package/src/Analysis.js +152 -152
package/index.js
CHANGED
|
@@ -1,93 +1,98 @@
|
|
|
1
|
-
// Import Node.js Dependencies
|
|
2
|
-
import fs from "fs/promises";
|
|
3
|
-
import path from "path";
|
|
4
|
-
|
|
5
|
-
// Import Third-party Dependencies
|
|
6
|
-
import { walk } from "estree-walker";
|
|
7
|
-
import * as meriyah from "meriyah";
|
|
8
|
-
import isMinified from "is-minified-code";
|
|
9
|
-
|
|
10
|
-
// Import Internal Dependencies
|
|
11
|
-
import Analysis from "./src/Analysis.js";
|
|
12
|
-
|
|
13
|
-
export function runASTAnalysis(str, options = Object.create(null)) {
|
|
14
|
-
const { module = true, isMinified = false } = options;
|
|
15
|
-
|
|
16
|
-
// Note: if the file start with a shebang then we remove it because 'parseScript' may fail to parse it.
|
|
17
|
-
// Example: #!/usr/bin/env node
|
|
18
|
-
const strToAnalyze = str.charAt(0) === "#" ? str.slice(str.indexOf("\n")) : str;
|
|
19
|
-
const
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
if (
|
|
36
|
-
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
1
|
+
// Import Node.js Dependencies
|
|
2
|
+
import fs from "fs/promises";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
// Import Third-party Dependencies
|
|
6
|
+
import { walk } from "estree-walker";
|
|
7
|
+
import * as meriyah from "meriyah";
|
|
8
|
+
import isMinified from "is-minified-code";
|
|
9
|
+
|
|
10
|
+
// Import Internal Dependencies
|
|
11
|
+
import Analysis from "./src/Analysis.js";
|
|
12
|
+
|
|
13
|
+
export function runASTAnalysis(str, options = Object.create(null)) {
|
|
14
|
+
const { module = true, isMinified = false } = options;
|
|
15
|
+
|
|
16
|
+
// Note: if the file start with a shebang then we remove it because 'parseScript' may fail to parse it.
|
|
17
|
+
// Example: #!/usr/bin/env node
|
|
18
|
+
const strToAnalyze = str.charAt(0) === "#" ? str.slice(str.indexOf("\n")) : str;
|
|
19
|
+
const isEcmaScriptModule = Boolean(module);
|
|
20
|
+
const { body } = meriyah.parseScript(strToAnalyze, {
|
|
21
|
+
next: true,
|
|
22
|
+
loc: true,
|
|
23
|
+
raw: true,
|
|
24
|
+
module: isEcmaScriptModule,
|
|
25
|
+
globalReturn: !isEcmaScriptModule
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
const sastAnalysis = new Analysis();
|
|
29
|
+
sastAnalysis.analyzeSourceString(str);
|
|
30
|
+
|
|
31
|
+
// we walk each AST Nodes, this is a purely synchronous I/O
|
|
32
|
+
walk(body, {
|
|
33
|
+
enter(node) {
|
|
34
|
+
// Skip the root of the AST.
|
|
35
|
+
if (Array.isArray(node)) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const action = sastAnalysis.walk(node);
|
|
40
|
+
if (action === "skip") {
|
|
41
|
+
this.skip();
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const dependencies = sastAnalysis.dependencies;
|
|
47
|
+
const { idsLengthAvg, stringScore, warnings } = sastAnalysis.getResult(isMinified);
|
|
48
|
+
const isOneLineRequire = body.length <= 1 && dependencies.size <= 1;
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
dependencies, warnings, idsLengthAvg, stringScore, isOneLineRequire
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function runASTAnalysisOnFile(pathToFile, options = {}) {
|
|
56
|
+
try {
|
|
57
|
+
const { packageName = null, module = true } = options;
|
|
58
|
+
const str = await fs.readFile(pathToFile, "utf-8");
|
|
59
|
+
|
|
60
|
+
const isMin = pathToFile.includes(".min") || isMinified(str);
|
|
61
|
+
const data = runASTAnalysis(str, {
|
|
62
|
+
isMinified: isMin,
|
|
63
|
+
module: path.extname(pathToFile) === ".mjs" ? true : module
|
|
64
|
+
});
|
|
65
|
+
if (packageName !== null) {
|
|
66
|
+
data.dependencies.removeByName(packageName);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
ok: true,
|
|
71
|
+
dependencies: data.dependencies,
|
|
72
|
+
warnings: data.warnings,
|
|
73
|
+
isMinified: !data.isOneLineRequire && isMin
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
return {
|
|
78
|
+
ok: false,
|
|
79
|
+
warnings: [
|
|
80
|
+
{ kind: "parsing-error", value: error.message, location: [[0, 0], [0, 0]] }
|
|
81
|
+
]
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export const CONSTANTS = {
|
|
87
|
+
Warnings: Object.freeze({
|
|
88
|
+
parsingError: "ast-error",
|
|
89
|
+
unsafeImport: "unsafe-import",
|
|
90
|
+
unsafeRegex: "unsafe-regex",
|
|
91
|
+
unsafeStmt: "unsafe-stmt",
|
|
92
|
+
unsafeAssign: "unsafe-assign",
|
|
93
|
+
encodedLiteral: "encoded-literal",
|
|
94
|
+
shortIdentifiers: "short-identifiers",
|
|
95
|
+
suspiciousLiteral: "suspicious-literal",
|
|
96
|
+
obfuscatedCode: "obfuscated-code"
|
|
97
|
+
})
|
|
98
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nodesecure/js-x-ray",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.3.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
|
|
40
|
+
"@nodesecure/sec-literal": "^1.1.0",
|
|
41
41
|
"estree-walker": "^3.0.1",
|
|
42
42
|
"is-minified-code": "^2.0.0",
|
|
43
|
-
"meriyah": "^4.2.
|
|
43
|
+
"meriyah": "^4.2.1",
|
|
44
44
|
"safe-regex": "^2.1.1"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
47
|
"@nodesecure/eslint-config": "^1.3.1",
|
|
48
48
|
"@slimio/is": "^1.5.1",
|
|
49
|
-
"@small-tech/esm-tape-runner": "^
|
|
49
|
+
"@small-tech/esm-tape-runner": "^2.0.0",
|
|
50
50
|
"@small-tech/tap-monkey": "^1.3.0",
|
|
51
|
-
"@types/node": "^17.0.
|
|
51
|
+
"@types/node": "^17.0.23",
|
|
52
52
|
"cross-env": "^7.0.3",
|
|
53
|
-
"eslint": "^8.
|
|
54
|
-
"pkg-ok": "^
|
|
55
|
-
"tape": "^5.5.
|
|
53
|
+
"eslint": "^8.12.0",
|
|
54
|
+
"pkg-ok": "^3.0.0",
|
|
55
|
+
"tape": "^5.5.2"
|
|
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;
|