@nodesecure/js-x-ray 9.0.0 → 9.2.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/LICENSE +21 -21
- package/README.md +9 -209
- package/dist/AstAnalyser.d.ts +64 -0
- package/dist/AstAnalyser.d.ts.map +1 -0
- package/dist/AstAnalyser.js +165 -0
- package/dist/AstAnalyser.js.map +1 -0
- package/dist/Deobfuscator.d.ts +36 -0
- package/dist/Deobfuscator.d.ts.map +1 -0
- package/dist/Deobfuscator.js +154 -0
- package/dist/Deobfuscator.js.map +1 -0
- package/dist/EntryFilesAnalyser.d.ts +20 -0
- package/dist/EntryFilesAnalyser.d.ts.map +1 -0
- package/dist/EntryFilesAnalyser.js +121 -0
- package/dist/EntryFilesAnalyser.js.map +1 -0
- package/dist/JsSourceParser.d.ts +18 -0
- package/dist/JsSourceParser.d.ts.map +1 -0
- package/dist/JsSourceParser.js +38 -0
- package/dist/JsSourceParser.js.map +1 -0
- package/dist/NodeCounter.d.ts +24 -0
- package/dist/NodeCounter.d.ts.map +1 -0
- package/dist/NodeCounter.js +62 -0
- package/dist/NodeCounter.js.map +1 -0
- package/dist/ProbeRunner.d.ts +40 -0
- package/dist/ProbeRunner.d.ts.map +1 -0
- package/dist/ProbeRunner.js +106 -0
- package/dist/ProbeRunner.js.map +1 -0
- package/dist/SourceFile.d.ts +43 -0
- package/dist/SourceFile.d.ts.map +1 -0
- package/dist/SourceFile.js +137 -0
- package/dist/SourceFile.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/obfuscators/freejsobfuscator.d.ts +3 -0
- package/dist/obfuscators/freejsobfuscator.d.ts.map +1 -0
- package/dist/obfuscators/freejsobfuscator.js +10 -0
- package/dist/obfuscators/freejsobfuscator.js.map +1 -0
- package/dist/obfuscators/jjencode.d.ts +3 -0
- package/dist/obfuscators/jjencode.d.ts.map +1 -0
- package/dist/obfuscators/jjencode.js +24 -0
- package/dist/obfuscators/jjencode.js.map +1 -0
- package/dist/obfuscators/jsfuck.d.ts +3 -0
- package/dist/obfuscators/jsfuck.d.ts.map +1 -0
- package/dist/obfuscators/jsfuck.js +13 -0
- package/dist/obfuscators/jsfuck.js.map +1 -0
- package/dist/obfuscators/obfuscator-io.d.ts +3 -0
- package/dist/obfuscators/obfuscator-io.d.ts.map +1 -0
- package/dist/obfuscators/obfuscator-io.js +15 -0
- package/dist/obfuscators/obfuscator-io.js.map +1 -0
- package/dist/obfuscators/trojan-source.d.ts +2 -0
- package/dist/obfuscators/trojan-source.d.ts.map +1 -0
- package/dist/obfuscators/trojan-source.js +27 -0
- package/dist/obfuscators/trojan-source.js.map +1 -0
- package/dist/probes/isArrayExpression.d.ts +21 -0
- package/dist/probes/isArrayExpression.d.ts.map +1 -0
- package/{src → dist}/probes/isArrayExpression.js +10 -15
- package/dist/probes/isArrayExpression.js.map +1 -0
- package/dist/probes/isBinaryExpression.d.ts +21 -0
- package/dist/probes/isBinaryExpression.d.ts.map +1 -0
- package/dist/probes/isBinaryExpression.js +54 -0
- package/dist/probes/isBinaryExpression.js.map +1 -0
- package/dist/probes/isESMExport.d.ts +24 -0
- package/dist/probes/isESMExport.d.ts.map +1 -0
- package/dist/probes/isESMExport.js +30 -0
- package/dist/probes/isESMExport.js.map +1 -0
- package/dist/probes/isFetch.d.ts +14 -0
- package/dist/probes/isFetch.d.ts.map +1 -0
- package/dist/probes/isFetch.js +18 -0
- package/dist/probes/isFetch.js.map +1 -0
- package/dist/probes/isImportDeclaration.d.ts +26 -0
- package/dist/probes/isImportDeclaration.d.ts.map +1 -0
- package/dist/probes/isImportDeclaration.js +38 -0
- package/dist/probes/isImportDeclaration.js.map +1 -0
- package/dist/probes/isLiteral.d.ts +21 -0
- package/dist/probes/isLiteral.d.ts.map +1 -0
- package/dist/probes/isLiteral.js +66 -0
- package/dist/probes/isLiteral.js.map +1 -0
- package/dist/probes/isLiteralRegex.d.ts +20 -0
- package/dist/probes/isLiteralRegex.d.ts.map +1 -0
- package/dist/probes/isLiteralRegex.js +30 -0
- package/dist/probes/isLiteralRegex.js.map +1 -0
- package/dist/probes/isRegexObject.d.ts +22 -0
- package/dist/probes/isRegexObject.d.ts.map +1 -0
- package/dist/probes/isRegexObject.js +50 -0
- package/dist/probes/isRegexObject.js.map +1 -0
- package/dist/probes/isRequire/RequireCallExpressionWalker.d.ts +15 -0
- package/dist/probes/isRequire/RequireCallExpressionWalker.d.ts.map +1 -0
- package/dist/probes/isRequire/RequireCallExpressionWalker.js +95 -0
- package/dist/probes/isRequire/RequireCallExpressionWalker.js.map +1 -0
- package/dist/probes/isRequire/isRequire.d.ts +20 -0
- package/dist/probes/isRequire/isRequire.d.ts.map +1 -0
- package/dist/probes/isRequire/isRequire.js +138 -0
- package/dist/probes/isRequire/isRequire.js.map +1 -0
- package/dist/probes/isSerializeEnv.d.ts +24 -0
- package/dist/probes/isSerializeEnv.d.ts.map +1 -0
- package/dist/probes/isSerializeEnv.js +66 -0
- package/dist/probes/isSerializeEnv.js.map +1 -0
- package/dist/probes/isSyncIO.d.ts +16 -0
- package/dist/probes/isSyncIO.d.ts.map +1 -0
- package/dist/probes/isSyncIO.js +74 -0
- package/dist/probes/isSyncIO.js.map +1 -0
- package/dist/probes/isUnsafeCallee.d.ts +22 -0
- package/dist/probes/isUnsafeCallee.d.ts.map +1 -0
- package/dist/probes/isUnsafeCallee.js +60 -0
- package/dist/probes/isUnsafeCallee.js.map +1 -0
- package/dist/probes/isUnsafeCommand.d.ts +24 -0
- package/dist/probes/isUnsafeCommand.d.ts.map +1 -0
- package/dist/probes/isUnsafeCommand.js +100 -0
- package/dist/probes/isUnsafeCommand.js.map +1 -0
- package/dist/probes/isWeakCrypto.d.ts +16 -0
- package/dist/probes/isWeakCrypto.d.ts.map +1 -0
- package/dist/probes/isWeakCrypto.js +46 -0
- package/dist/probes/isWeakCrypto.js.map +1 -0
- package/dist/types/estree.d.ts +11 -0
- package/dist/types/estree.d.ts.map +1 -0
- package/dist/types/estree.js +15 -0
- package/dist/types/estree.js.map +1 -0
- package/dist/utils/extractNode.d.ts +5 -0
- package/dist/utils/extractNode.d.ts.map +1 -0
- package/dist/utils/extractNode.js +13 -0
- package/dist/utils/extractNode.js.map +1 -0
- package/dist/utils/index.d.ts +5 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +5 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/isOneLineExpressionExport.d.ts +3 -0
- package/dist/utils/isOneLineExpressionExport.d.ts.map +1 -0
- package/dist/utils/isOneLineExpressionExport.js +49 -0
- package/dist/utils/isOneLineExpressionExport.js.map +1 -0
- package/dist/utils/notNullOrUndefined.d.ts +2 -0
- package/dist/utils/notNullOrUndefined.d.ts.map +1 -0
- package/dist/utils/notNullOrUndefined.js +4 -0
- package/dist/utils/notNullOrUndefined.js.map +1 -0
- package/dist/utils/toArrayLocation.d.ts +5 -0
- package/dist/utils/toArrayLocation.d.ts.map +1 -0
- package/dist/utils/toArrayLocation.js +14 -0
- package/dist/utils/toArrayLocation.js.map +1 -0
- package/dist/warnings.d.ts +88 -0
- package/dist/warnings.d.ts.map +1 -0
- package/dist/warnings.js +91 -0
- package/dist/warnings.js.map +1 -0
- package/package.json +59 -77
- package/index.d.ts +0 -46
- package/index.js +0 -4
- package/src/AstAnalyser.js +0 -222
- package/src/Deobfuscator.js +0 -195
- package/src/EntryFilesAnalyser.js +0 -167
- package/src/JsSourceParser.js +0 -57
- package/src/NodeCounter.js +0 -76
- package/src/ProbeRunner.js +0 -144
- package/src/SourceFile.js +0 -147
- package/src/obfuscators/freejsobfuscator.js +0 -9
- package/src/obfuscators/jjencode.js +0 -27
- package/src/obfuscators/jsfuck.js +0 -11
- package/src/obfuscators/obfuscator-io.js +0 -13
- package/src/obfuscators/trojan-source.js +0 -28
- package/src/probes/isBinaryExpression.js +0 -55
- package/src/probes/isESMExport.js +0 -31
- package/src/probes/isFetch.js +0 -19
- package/src/probes/isImportDeclaration.js +0 -33
- package/src/probes/isLiteral.js +0 -70
- package/src/probes/isLiteralRegex.js +0 -31
- package/src/probes/isRegexObject.js +0 -49
- package/src/probes/isRequire/RequireCallExpressionWalker.js +0 -93
- package/src/probes/isRequire/isRequire.js +0 -148
- package/src/probes/isUnsafeCallee.js +0 -35
- package/src/probes/isWeakCrypto.js +0 -37
- package/src/utils/exportAssignmentHasRequireLeave.js +0 -40
- package/src/utils/extractNode.js +0 -14
- package/src/utils/index.js +0 -8
- package/src/utils/isNode.js +0 -5
- package/src/utils/isOneLineExpressionExport.js +0 -24
- package/src/utils/isUnsafeCallee.js +0 -28
- package/src/utils/notNullOrUndefined.js +0 -3
- package/src/utils/rootLocation.js +0 -3
- package/src/utils/toArrayLocation.js +0 -11
- package/src/warnings.js +0 -77
- package/types/api.d.ts +0 -177
- package/types/warnings.d.ts +0 -36
package/src/ProbeRunner.js
DELETED
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// Import Native Dependencies
|
|
2
|
-
import assert from "node:assert";
|
|
3
|
-
|
|
4
|
-
// Import all the probes
|
|
5
|
-
import isUnsafeCallee from "./probes/isUnsafeCallee.js";
|
|
6
|
-
import isLiteral from "./probes/isLiteral.js";
|
|
7
|
-
import isLiteralRegex from "./probes/isLiteralRegex.js";
|
|
8
|
-
import isRegexObject from "./probes/isRegexObject.js";
|
|
9
|
-
import isRequire from "./probes/isRequire/isRequire.js";
|
|
10
|
-
import isImportDeclaration from "./probes/isImportDeclaration.js";
|
|
11
|
-
import isWeakCrypto from "./probes/isWeakCrypto.js";
|
|
12
|
-
import isBinaryExpression from "./probes/isBinaryExpression.js";
|
|
13
|
-
import isArrayExpression from "./probes/isArrayExpression.js";
|
|
14
|
-
import isESMExport from "./probes/isESMExport.js";
|
|
15
|
-
import isFetch from "./probes/isFetch.js";
|
|
16
|
-
|
|
17
|
-
// Import Internal Dependencies
|
|
18
|
-
import { SourceFile } from "./SourceFile.js";
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @typedef {Object} Probe
|
|
22
|
-
* @property {string} name
|
|
23
|
-
* @property {any} validateNode
|
|
24
|
-
* @property {(node: any, options: any) => any} main
|
|
25
|
-
* @property {(options: any) => void} teardown
|
|
26
|
-
* @property {boolean} [breakOnMatch=false]
|
|
27
|
-
* @property {string} [breakGroup]
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
export const ProbeSignals = Object.freeze({
|
|
31
|
-
Break: Symbol.for("breakWalk"),
|
|
32
|
-
Skip: Symbol.for("skipWalk")
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
export class ProbeRunner {
|
|
36
|
-
/**
|
|
37
|
-
* Note:
|
|
38
|
-
* The order of the table has an importance/impact on the correct execution of the probes
|
|
39
|
-
*
|
|
40
|
-
* @type {Probe[]}
|
|
41
|
-
*/
|
|
42
|
-
static Defaults = [
|
|
43
|
-
isFetch,
|
|
44
|
-
isRequire,
|
|
45
|
-
isESMExport,
|
|
46
|
-
isUnsafeCallee,
|
|
47
|
-
isLiteral,
|
|
48
|
-
isLiteralRegex,
|
|
49
|
-
isRegexObject,
|
|
50
|
-
isImportDeclaration,
|
|
51
|
-
isWeakCrypto,
|
|
52
|
-
isBinaryExpression,
|
|
53
|
-
isArrayExpression
|
|
54
|
-
];
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
*
|
|
58
|
-
* @param {!SourceFile} sourceFile
|
|
59
|
-
* @param {Probe[]} [probes=ProbeRunner.Defaults]
|
|
60
|
-
*/
|
|
61
|
-
constructor(
|
|
62
|
-
sourceFile,
|
|
63
|
-
probes = ProbeRunner.Defaults
|
|
64
|
-
) {
|
|
65
|
-
this.sourceFile = sourceFile;
|
|
66
|
-
|
|
67
|
-
for (const probe of probes) {
|
|
68
|
-
assert(
|
|
69
|
-
typeof probe.validateNode === "function" || Array.isArray(probe.validateNode),
|
|
70
|
-
`Invalid probe ${probe.name}: validateNode must be a function or an array of functions`
|
|
71
|
-
);
|
|
72
|
-
assert(
|
|
73
|
-
typeof probe.main === "function",
|
|
74
|
-
`Invalid probe ${probe.name}: main must be a function`
|
|
75
|
-
);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.probes = probes;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* @param {!Probe} probe
|
|
83
|
-
* @param {!any} node
|
|
84
|
-
* @returns {null|void|Symbol}
|
|
85
|
-
*/
|
|
86
|
-
#runProbe(probe, node) {
|
|
87
|
-
const validationFns = Array.isArray(probe.validateNode) ?
|
|
88
|
-
probe.validateNode : [probe.validateNode];
|
|
89
|
-
for (const validateNode of validationFns) {
|
|
90
|
-
const [isMatching, data = null] = validateNode(
|
|
91
|
-
node,
|
|
92
|
-
this.sourceFile
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
if (isMatching) {
|
|
96
|
-
return probe.main(node, {
|
|
97
|
-
sourceFile: this.sourceFile,
|
|
98
|
-
data
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
return null;
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
walk(node) {
|
|
107
|
-
/** @type {Set<string>} */
|
|
108
|
-
const breakGroups = new Set();
|
|
109
|
-
|
|
110
|
-
for (const probe of this.probes) {
|
|
111
|
-
if (breakGroups.has(probe.breakGroup)) {
|
|
112
|
-
continue;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
try {
|
|
116
|
-
const result = this.#runProbe(probe, node);
|
|
117
|
-
if (result === null) {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
if (result === ProbeSignals.Skip) {
|
|
122
|
-
return "skip";
|
|
123
|
-
}
|
|
124
|
-
if (result === ProbeSignals.Break || probe.breakOnMatch) {
|
|
125
|
-
const breakGroup = probe.breakGroup || null;
|
|
126
|
-
|
|
127
|
-
if (breakGroup === null) {
|
|
128
|
-
break;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
breakGroups.add(breakGroup);
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
finally {
|
|
136
|
-
if (probe.teardown) {
|
|
137
|
-
probe.teardown({ sourceFile: this.sourceFile });
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
return null;
|
|
143
|
-
}
|
|
144
|
-
}
|
package/src/SourceFile.js
DELETED
|
@@ -1,147 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import { Utils, Literal } from "@nodesecure/sec-literal";
|
|
3
|
-
import { VariableTracer } from "@nodesecure/estree-ast-utils";
|
|
4
|
-
|
|
5
|
-
// Import Internal Dependencies
|
|
6
|
-
import { rootLocation, toArrayLocation } from "./utils/index.js";
|
|
7
|
-
import { generateWarning } from "./warnings.js";
|
|
8
|
-
import { ProbeRunner } from "./ProbeRunner.js";
|
|
9
|
-
import { Deobfuscator } from "./Deobfuscator.js";
|
|
10
|
-
import * as trojan from "./obfuscators/trojan-source.js";
|
|
11
|
-
|
|
12
|
-
// CONSTANTS
|
|
13
|
-
const kMaximumEncodedLiterals = 10;
|
|
14
|
-
|
|
15
|
-
export class SourceFile {
|
|
16
|
-
inTryStatement = false;
|
|
17
|
-
dependencyAutoWarning = false;
|
|
18
|
-
deobfuscator = new Deobfuscator();
|
|
19
|
-
dependencies = new Map();
|
|
20
|
-
encodedLiterals = new Map();
|
|
21
|
-
warnings = [];
|
|
22
|
-
/** @type {Set<string>} */
|
|
23
|
-
flags = new Set();
|
|
24
|
-
|
|
25
|
-
constructor(sourceCodeString, probesOptions = {}) {
|
|
26
|
-
this.tracer = new VariableTracer()
|
|
27
|
-
.enableDefaultTracing()
|
|
28
|
-
.trace("crypto.createHash", {
|
|
29
|
-
followConsecutiveAssignment: true, moduleName: "crypto"
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
let probes = ProbeRunner.Defaults;
|
|
33
|
-
if (Array.isArray(probesOptions.customProbes) && probesOptions.customProbes.length > 0) {
|
|
34
|
-
probes = probesOptions.skipDefaultProbes === true ? probesOptions.customProbes : [...probes, ...probesOptions.customProbes];
|
|
35
|
-
}
|
|
36
|
-
this.probesRunner = new ProbeRunner(this, probes);
|
|
37
|
-
|
|
38
|
-
if (trojan.verify(sourceCodeString)) {
|
|
39
|
-
this.addWarning("obfuscated-code", "trojan-source");
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
addDependency(name, location = null, unsafe = this.dependencyAutoWarning) {
|
|
44
|
-
if (typeof name !== "string" || name.trim() === "") {
|
|
45
|
-
return;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const dependencyName = name.charAt(name.length - 1) === "/" ?
|
|
49
|
-
name.slice(0, -1) : name;
|
|
50
|
-
this.dependencies.set(dependencyName, {
|
|
51
|
-
unsafe,
|
|
52
|
-
inTry: this.inTryStatement,
|
|
53
|
-
...(location === null ? {} : { location })
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
if (this.dependencyAutoWarning) {
|
|
57
|
-
this.addWarning("unsafe-import", dependencyName, location);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
addWarning(name, value, location = rootLocation()) {
|
|
62
|
-
const isEncodedLiteral = name === "encoded-literal";
|
|
63
|
-
if (isEncodedLiteral) {
|
|
64
|
-
if (this.encodedLiterals.size > kMaximumEncodedLiterals) {
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
if (this.encodedLiterals.has(value)) {
|
|
69
|
-
const index = this.encodedLiterals.get(value);
|
|
70
|
-
this.warnings[index].location.push(toArrayLocation(location));
|
|
71
|
-
|
|
72
|
-
return;
|
|
73
|
-
}
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
this.warnings.push(generateWarning(name, { value, location }));
|
|
77
|
-
if (isEncodedLiteral) {
|
|
78
|
-
this.encodedLiterals.set(value, this.warnings.length - 1);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
analyzeLiteral(node, inArrayExpr = false) {
|
|
83
|
-
if (typeof node.value !== "string" || Utils.isSvg(node)) {
|
|
84
|
-
return;
|
|
85
|
-
}
|
|
86
|
-
this.deobfuscator.analyzeString(node.value);
|
|
87
|
-
|
|
88
|
-
const { hasHexadecimalSequence, hasUnicodeSequence, isBase64 } = Literal.defaultAnalysis(node);
|
|
89
|
-
if ((hasHexadecimalSequence || hasUnicodeSequence) && isBase64) {
|
|
90
|
-
if (inArrayExpr) {
|
|
91
|
-
this.deobfuscator.encodedArrayValue++;
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
this.addWarning("encoded-literal", node.value, node.loc);
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
getResult(isMinified) {
|
|
100
|
-
const obfuscatorName = this.deobfuscator.assertObfuscation(this);
|
|
101
|
-
if (obfuscatorName !== null) {
|
|
102
|
-
this.addWarning("obfuscated-code", obfuscatorName);
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const identifiersLengthArr = this.deobfuscator.identifiers
|
|
106
|
-
.filter((value) => value.type !== "Property" && typeof value.name === "string")
|
|
107
|
-
.map((value) => value.name.length);
|
|
108
|
-
|
|
109
|
-
const [idsLengthAvg, stringScore] = [
|
|
110
|
-
sum(identifiersLengthArr),
|
|
111
|
-
sum(this.deobfuscator.literalScores)
|
|
112
|
-
];
|
|
113
|
-
if (!isMinified && identifiersLengthArr.length > 5 && idsLengthAvg <= 1.5) {
|
|
114
|
-
this.addWarning("short-identifiers", idsLengthAvg);
|
|
115
|
-
}
|
|
116
|
-
if (stringScore >= 3) {
|
|
117
|
-
this.addWarning("suspicious-literal", stringScore);
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (this.encodedLiterals.size > kMaximumEncodedLiterals) {
|
|
121
|
-
this.addWarning("suspicious-file", null);
|
|
122
|
-
this.warnings = this.warnings
|
|
123
|
-
.filter((warning) => warning.kind !== "encoded-literal");
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
return { idsLengthAvg, stringScore, warnings: this.warnings };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
walk(node) {
|
|
130
|
-
this.tracer.walk(node);
|
|
131
|
-
this.deobfuscator.walk(node);
|
|
132
|
-
|
|
133
|
-
// Detect TryStatement and CatchClause to known which dependency is required in a Try {} clause
|
|
134
|
-
if (node.type === "TryStatement" && node.handler) {
|
|
135
|
-
this.inTryStatement = true;
|
|
136
|
-
}
|
|
137
|
-
else if (node.type === "CatchClause") {
|
|
138
|
-
this.inTryStatement = false;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
return this.probesRunner.walk(node);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function sum(arr = []) {
|
|
146
|
-
return arr.length === 0 ? 0 : (arr.reduce((prev, curr) => prev + curr, 0) / arr.length);
|
|
147
|
-
}
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import { Utils } from "@nodesecure/sec-literal";
|
|
3
|
-
|
|
4
|
-
export function verify(identifiers, prefix) {
|
|
5
|
-
const pValue = Object.keys(prefix).pop();
|
|
6
|
-
const regexStr = `^${Utils.escapeRegExp(pValue)}[a-zA-Z]{1,2}[0-9]{0,2}$`;
|
|
7
|
-
|
|
8
|
-
return identifiers.every(({ name }) => new RegExp(regexStr).test(name));
|
|
9
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
// Import Internal Dependencies
|
|
2
|
-
import { notNullOrUndefined } from "../utils/index.js";
|
|
3
|
-
|
|
4
|
-
// CONSTANTS
|
|
5
|
-
const kJJRegularSymbols = new Set(["$", "_"]);
|
|
6
|
-
|
|
7
|
-
export function verify(identifiers, counters) {
|
|
8
|
-
if (counters.VariableDeclarator > 0 || counters.FunctionDeclaration > 0) {
|
|
9
|
-
return false;
|
|
10
|
-
}
|
|
11
|
-
if (counters.AssignmentExpression > counters.Property) {
|
|
12
|
-
return false;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const matchCount = identifiers.filter(({ name }) => {
|
|
16
|
-
if (!notNullOrUndefined(name)) {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
const charsCode = [...new Set([...name])];
|
|
20
|
-
|
|
21
|
-
return charsCode.every((char) => kJJRegularSymbols.has(char));
|
|
22
|
-
}).length;
|
|
23
|
-
const pourcent = ((matchCount / identifiers.length) * 100);
|
|
24
|
-
|
|
25
|
-
return pourcent > 80;
|
|
26
|
-
}
|
|
27
|
-
|
|
@@ -1,11 +0,0 @@
|
|
|
1
|
-
// CONSTANTS
|
|
2
|
-
const kJSFuckMinimumDoubleUnaryExpr = 5;
|
|
3
|
-
|
|
4
|
-
export function verify(counters) {
|
|
5
|
-
const hasZeroAssign = counters.AssignmentExpression === 0
|
|
6
|
-
&& counters.FunctionDeclaration === 0
|
|
7
|
-
&& counters.Property === 0
|
|
8
|
-
&& counters.VariableDeclarator === 0;
|
|
9
|
-
|
|
10
|
-
return hasZeroAssign && counters.DoubleUnaryExpression >= kJSFuckMinimumDoubleUnaryExpr;
|
|
11
|
-
}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
export function verify(deobfuscator, counters) {
|
|
2
|
-
if ((counters.MemberExpression?.false ?? 0) > 0) {
|
|
3
|
-
return false;
|
|
4
|
-
}
|
|
5
|
-
|
|
6
|
-
const hasSomePatterns = counters.DoubleUnaryExpression > 0
|
|
7
|
-
|| deobfuscator.deepBinaryExpression > 0
|
|
8
|
-
|| deobfuscator.encodedArrayValue > 0
|
|
9
|
-
|| deobfuscator.hasDictionaryString;
|
|
10
|
-
|
|
11
|
-
// TODO: hasPrefixedIdentifiers only work for hexadecimal id names generator
|
|
12
|
-
return deobfuscator.hasPrefixedIdentifiers && hasSomePatterns;
|
|
13
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Dangerous Unicode control characters that can be used by hackers
|
|
3
|
-
* to perform trojan source.
|
|
4
|
-
*/
|
|
5
|
-
const kUnsafeUnicodeControlCharacters = [
|
|
6
|
-
"\u202A",
|
|
7
|
-
"\u202B",
|
|
8
|
-
"\u202D",
|
|
9
|
-
"\u202E",
|
|
10
|
-
"\u202C",
|
|
11
|
-
"\u2066",
|
|
12
|
-
"\u2067",
|
|
13
|
-
"\u2068",
|
|
14
|
-
"\u2069",
|
|
15
|
-
"\u200E",
|
|
16
|
-
"\u200F",
|
|
17
|
-
"\u061C"
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
export function verify(sourceString) {
|
|
21
|
-
for (const unsafeCharacter of kUnsafeUnicodeControlCharacters) {
|
|
22
|
-
if (sourceString.includes(unsafeCharacter)) {
|
|
23
|
-
return true;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
return false;
|
|
28
|
-
}
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Search for BinaryExpression AST Node.
|
|
3
|
-
*
|
|
4
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#binaryexpression
|
|
5
|
-
* @example
|
|
6
|
-
* 5 + 5 + 10
|
|
7
|
-
*/
|
|
8
|
-
function validateNode(node) {
|
|
9
|
-
return [
|
|
10
|
-
node.type === "BinaryExpression"
|
|
11
|
-
];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function main(node, options) {
|
|
15
|
-
const { sourceFile } = options;
|
|
16
|
-
|
|
17
|
-
const [binaryExprDeepness, hasUnaryExpression] = walkBinaryExpression(node);
|
|
18
|
-
if (binaryExprDeepness >= 3 && hasUnaryExpression) {
|
|
19
|
-
sourceFile.deobfuscator.deepBinaryExpression++;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* @description Look for suspicious BinaryExpression (read the Obfuscator.io section of the linked G.Doc)
|
|
25
|
-
* @see https://docs.google.com/document/d/11ZrfW0bDQ-kd7Gr_Ixqyk8p3TGvxckmhFH3Z8dFoPhY/edit?usp=sharing
|
|
26
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#unaryexpression
|
|
27
|
-
* @example
|
|
28
|
-
* 0x1*-0x12df+-0x1fb9*-0x1+0x2*-0x66d
|
|
29
|
-
*/
|
|
30
|
-
function walkBinaryExpression(expr, level = 1) {
|
|
31
|
-
const [lt, rt] = [expr.left.type, expr.right.type];
|
|
32
|
-
let hasUnaryExpression = lt === "UnaryExpression" || rt === "UnaryExpression";
|
|
33
|
-
let currentLevel = lt === "BinaryExpression" || rt === "BinaryExpression" ? level + 1 : level;
|
|
34
|
-
|
|
35
|
-
for (const currExpr of [expr.left, expr.right]) {
|
|
36
|
-
if (currExpr.type === "BinaryExpression") {
|
|
37
|
-
const [deepLevel, deepHasUnaryExpression] = walkBinaryExpression(currExpr, currentLevel);
|
|
38
|
-
if (deepLevel > currentLevel) {
|
|
39
|
-
currentLevel = deepLevel;
|
|
40
|
-
}
|
|
41
|
-
if (!hasUnaryExpression && deepHasUnaryExpression) {
|
|
42
|
-
hasUnaryExpression = true;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
return [currentLevel, hasUnaryExpression];
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export default {
|
|
51
|
-
name: "isBinaryExpression",
|
|
52
|
-
validateNode,
|
|
53
|
-
main,
|
|
54
|
-
breakOnMatch: false
|
|
55
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Search for ESM Export
|
|
3
|
-
*
|
|
4
|
-
* @example
|
|
5
|
-
* export { bar } from "./foo.js";
|
|
6
|
-
* export * from "./bar.js";
|
|
7
|
-
*/
|
|
8
|
-
function validateNode(node) {
|
|
9
|
-
return [
|
|
10
|
-
/**
|
|
11
|
-
* We must be sure that the source property is a Literal to not fall in a trap
|
|
12
|
-
* export const foo = "bar";
|
|
13
|
-
*/
|
|
14
|
-
(node.type === "ExportNamedDeclaration" && node.source?.type === "Literal") ||
|
|
15
|
-
node.type === "ExportAllDeclaration"
|
|
16
|
-
];
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
function main(node, { sourceFile }) {
|
|
20
|
-
sourceFile.addDependency(
|
|
21
|
-
node.source.value,
|
|
22
|
-
node.loc
|
|
23
|
-
);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default {
|
|
27
|
-
name: "isESMExport",
|
|
28
|
-
validateNode,
|
|
29
|
-
main,
|
|
30
|
-
breakOnMatch: true
|
|
31
|
-
};
|
package/src/probes/isFetch.js
DELETED
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import { getCallExpressionIdentifier } from "@nodesecure/estree-ast-utils";
|
|
3
|
-
|
|
4
|
-
function validateNode(node) {
|
|
5
|
-
const id = getCallExpressionIdentifier(node);
|
|
6
|
-
|
|
7
|
-
return [id === "fetch"];
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
function main(_node, { sourceFile }) {
|
|
11
|
-
sourceFile.flags.add("fetch");
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export default {
|
|
15
|
-
name: "isFetch",
|
|
16
|
-
validateNode,
|
|
17
|
-
main,
|
|
18
|
-
breakOnMatch: false
|
|
19
|
-
};
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Search for ESM ImportDeclaration
|
|
3
|
-
* @see https://github.com/estree/estree/blob/master/es2015.md#importdeclaration
|
|
4
|
-
* @example
|
|
5
|
-
* import * as foo from "bar";
|
|
6
|
-
* import fs from "fs";
|
|
7
|
-
* import "make-promises-safe";
|
|
8
|
-
*/
|
|
9
|
-
function validateNode(node) {
|
|
10
|
-
return [
|
|
11
|
-
// Note: the source property is the right-side Literal part of the Import
|
|
12
|
-
["ImportDeclaration", "ImportExpression"].includes(node.type) && node.source.type === "Literal"
|
|
13
|
-
];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function main(node, options) {
|
|
17
|
-
const { sourceFile } = options;
|
|
18
|
-
|
|
19
|
-
// Searching for dangerous import "data:text/javascript;..." statement.
|
|
20
|
-
// see: https://2ality.com/2019/10/eval-via-import.html
|
|
21
|
-
if (node.source.value.startsWith("data:text/javascript")) {
|
|
22
|
-
sourceFile.addWarning("unsafe-import", node.source.value, node.loc);
|
|
23
|
-
}
|
|
24
|
-
sourceFile.addDependency(node.source.value, node.loc);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default {
|
|
28
|
-
name: "isImportDeclaration",
|
|
29
|
-
validateNode,
|
|
30
|
-
main,
|
|
31
|
-
breakOnMatch: true,
|
|
32
|
-
breakGroup: "import"
|
|
33
|
-
};
|
package/src/probes/isLiteral.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
1
|
-
// Import Node.js Dependencies
|
|
2
|
-
import { builtinModules } from "module";
|
|
3
|
-
|
|
4
|
-
// Import Third-party Dependencies
|
|
5
|
-
import { Hex } from "@nodesecure/sec-literal";
|
|
6
|
-
|
|
7
|
-
const kMapRegexIps = Object.freeze({
|
|
8
|
-
// eslint-disable-next-line @stylistic/max-len
|
|
9
|
-
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]*)?$/,
|
|
10
|
-
regexIPv6: /^(https?:\/\/)(\[[0-9A-Fa-f:]+\])(?::\d{1,5})?(\/[^\s]*)?$/
|
|
11
|
-
});
|
|
12
|
-
|
|
13
|
-
// CONSTANTS
|
|
14
|
-
const kNodeDeps = new Set(builtinModules);
|
|
15
|
-
const kShadyLinkRegExps = [
|
|
16
|
-
kMapRegexIps.regexIPv4,
|
|
17
|
-
kMapRegexIps.regexIPv6,
|
|
18
|
-
/(http[s]?:\/\/(bit\.ly|ipinfo\.io|httpbin\.org).*)$/,
|
|
19
|
-
/(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))$/
|
|
20
|
-
];
|
|
21
|
-
/**
|
|
22
|
-
* @description Search for Literal AST Node
|
|
23
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#literal
|
|
24
|
-
* @example
|
|
25
|
-
* "foobar"
|
|
26
|
-
*/
|
|
27
|
-
function validateNode(node) {
|
|
28
|
-
return [
|
|
29
|
-
node.type === "Literal" && typeof node.value === "string"
|
|
30
|
-
];
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function main(node, options) {
|
|
34
|
-
const { sourceFile } = options;
|
|
35
|
-
|
|
36
|
-
// We are searching for value obfuscated as hex of a minimum length of 4.
|
|
37
|
-
if (/^[0-9A-Fa-f]{4,}$/g.test(node.value)) {
|
|
38
|
-
const value = Buffer.from(node.value, "hex").toString();
|
|
39
|
-
sourceFile.deobfuscator.analyzeString(value);
|
|
40
|
-
|
|
41
|
-
// If the value we are retrieving is the name of a Node.js dependency,
|
|
42
|
-
// then we add it to the dependencies list and we throw an unsafe-import at the current location.
|
|
43
|
-
if (kNodeDeps.has(value)) {
|
|
44
|
-
sourceFile.addDependency(value, node.loc);
|
|
45
|
-
sourceFile.addWarning("unsafe-import", null, node.loc);
|
|
46
|
-
}
|
|
47
|
-
else if (value === "require" || !Hex.isSafe(node.value)) {
|
|
48
|
-
sourceFile.addWarning("encoded-literal", node.value, node.loc);
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// Else we are checking all other string with our suspect method
|
|
52
|
-
else {
|
|
53
|
-
for (const regex of kShadyLinkRegExps) {
|
|
54
|
-
if (regex.test(node.value)) {
|
|
55
|
-
sourceFile.addWarning("shady-link", node.value, node.loc);
|
|
56
|
-
|
|
57
|
-
return;
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
sourceFile.analyzeLiteral(node);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export default {
|
|
66
|
-
name: "isLiteral",
|
|
67
|
-
validateNode,
|
|
68
|
-
main,
|
|
69
|
-
breakOnMatch: false
|
|
70
|
-
};
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
// Require Third-party Dependencies
|
|
2
|
-
import { isLiteralRegex } from "@nodesecure/estree-ast-utils";
|
|
3
|
-
import safeRegex from "safe-regex";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @description Search for RegExpLiteral AST Node
|
|
7
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#regexpliteral
|
|
8
|
-
* @example
|
|
9
|
-
* /hello/
|
|
10
|
-
*/
|
|
11
|
-
function validateNode(node) {
|
|
12
|
-
return [
|
|
13
|
-
isLiteralRegex(node)
|
|
14
|
-
];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function main(node, options) {
|
|
18
|
-
const { sourceFile } = options;
|
|
19
|
-
|
|
20
|
-
// We use the safe-regex package to detect whether or not regex is safe!
|
|
21
|
-
if (!safeRegex(node.regex.pattern)) {
|
|
22
|
-
sourceFile.addWarning("unsafe-regex", node.regex.pattern, node.loc);
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export default {
|
|
27
|
-
name: "isLiteralRegex",
|
|
28
|
-
validateNode,
|
|
29
|
-
main,
|
|
30
|
-
breakOnMatch: false
|
|
31
|
-
};
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import { isLiteralRegex } from "@nodesecure/estree-ast-utils";
|
|
3
|
-
import safeRegex from "safe-regex";
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* @description Search for Regex Object constructor.
|
|
7
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#newexpression
|
|
8
|
-
* @example
|
|
9
|
-
* new RegExp("...");
|
|
10
|
-
*/
|
|
11
|
-
function validateNode(node) {
|
|
12
|
-
return [
|
|
13
|
-
isRegexConstructor(node) && node.arguments.length > 0
|
|
14
|
-
];
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function main(node, options) {
|
|
18
|
-
const { sourceFile } = options;
|
|
19
|
-
|
|
20
|
-
const arg = node.arguments[0];
|
|
21
|
-
/**
|
|
22
|
-
* Note: RegExp Object can contain a RegExpLiteral
|
|
23
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#regexpliteral
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* new RegExp(/^foo/)
|
|
27
|
-
*/
|
|
28
|
-
const pattern = isLiteralRegex(arg) ? arg.regex.pattern : arg.value;
|
|
29
|
-
|
|
30
|
-
// We use the safe-regex package to detect whether or not regex is safe!
|
|
31
|
-
if (!safeRegex(pattern)) {
|
|
32
|
-
sourceFile.addWarning("unsafe-regex", pattern, node.loc);
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function isRegexConstructor(node) {
|
|
37
|
-
if (node.type !== "NewExpression" || node.callee.type !== "Identifier") {
|
|
38
|
-
return false;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
return node.callee.name === "RegExp";
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export default {
|
|
45
|
-
name: "isRegexObject",
|
|
46
|
-
validateNode,
|
|
47
|
-
main,
|
|
48
|
-
breakOnMatch: false
|
|
49
|
-
};
|