@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.
- package/LICENSE +1 -1
- package/README.md +106 -18
- package/index.d.ts +16 -3
- package/index.js +37 -134
- package/package.json +9 -9
- package/src/AstAnalyser.js +137 -0
- package/src/Deobfuscator.js +192 -0
- package/src/EntryFilesAnalyser.js +99 -0
- package/src/JsSourceParser.js +57 -0
- package/src/NodeCounter.js +76 -0
- package/src/ProbeRunner.js +140 -0
- package/src/{Analysis.js → SourceFile.js} +55 -68
- package/src/obfuscators/freejsobfuscator.js +9 -9
- package/src/obfuscators/jjencode.js +7 -7
- package/src/obfuscators/jsfuck.js +6 -6
- package/src/obfuscators/obfuscator-io.js +7 -7
- package/src/obfuscators/trojan-source.js +28 -28
- package/src/probes/isArrayExpression.js +32 -30
- package/src/probes/isBinaryExpression.js +5 -3
- package/src/probes/isImportDeclaration.js +8 -5
- package/src/probes/isLiteral.js +17 -9
- package/src/probes/isLiteralRegex.js +5 -3
- package/src/probes/isRegexObject.js +5 -3
- package/src/probes/isRequire/RequireCallExpressionWalker.js +93 -0
- package/src/probes/isRequire/isRequire.js +142 -0
- package/src/probes/isUnsafeCallee.js +15 -5
- package/src/probes/isWeakCrypto.js +5 -3
- package/src/utils/exportAssignmentHasRequireLeave.js +40 -0
- package/src/utils/extractNode.js +14 -0
- package/src/utils/index.js +8 -0
- package/src/utils/isNode.js +5 -0
- package/src/utils/isOneLineExpressionExport.js +18 -0
- package/src/utils/isUnsafeCallee.js +28 -0
- package/src/utils/notNullOrUndefined.js +3 -0
- package/src/utils/rootLocation.js +3 -0
- package/src/utils/toArrayLocation.js +11 -0
- package/src/warnings.js +1 -1
- package/types/api.d.ts +79 -18
- package/src/ASTDeps.js +0 -63
- package/src/obfuscators/index.js +0 -69
- package/src/probes/index.js +0 -70
- package/src/probes/isAssignmentExpression.js +0 -29
- package/src/probes/isClassDeclaration.js +0 -25
- package/src/probes/isFunction.js +0 -38
- package/src/probes/isMemberExpression.js +0 -16
- package/src/probes/isMethodDefinition.js +0 -25
- package/src/probes/isObjectExpression.js +0 -29
- package/src/probes/isRequire.js +0 -164
- package/src/probes/isUnaryExpression.js +0 -26
- package/src/probes/isVariableDeclaration.js +0 -30
- package/src/utils.js +0 -48
- 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 {
|
|
16
|
+
const { sourceFile, data: calleeName } = options;
|
|
16
17
|
|
|
17
|
-
|
|
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
|
|
27
|
+
return ProbeSignals.Skip;
|
|
20
28
|
}
|
|
21
29
|
|
|
22
30
|
export default {
|
|
23
31
|
name: "isUnsafeCallee",
|
|
24
|
-
validateNode,
|
|
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, {
|
|
24
|
+
function main(node, { sourceFile }) {
|
|
25
25
|
const arg = node.arguments.at(0);
|
|
26
26
|
|
|
27
27
|
if (kWeakAlgorithms.has(arg.value)) {
|
|
28
|
-
|
|
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,
|
|
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,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,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
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
|
-
|
|
48
|
+
removeHTMLComments?: boolean;
|
|
24
49
|
/**
|
|
25
50
|
* @default false
|
|
26
51
|
*/
|
|
27
|
-
|
|
52
|
+
isMinified?: boolean;
|
|
28
53
|
}
|
|
29
54
|
|
|
30
|
-
interface
|
|
31
|
-
|
|
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
|
|
39
|
-
packageName?: string;
|
|
59
|
+
interface AstAnalyserOptions {
|
|
40
60
|
/**
|
|
41
|
-
* @default
|
|
61
|
+
* @default JsSourceParser
|
|
42
62
|
*/
|
|
43
|
-
|
|
63
|
+
customParser?: SourceParser;
|
|
64
|
+
/**
|
|
65
|
+
* @default []
|
|
66
|
+
*/
|
|
67
|
+
customProbes?: Probe[];
|
|
44
68
|
/**
|
|
45
69
|
* @default false
|
|
46
70
|
*/
|
|
47
|
-
|
|
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:
|
|
90
|
+
dependencies: Map<string, Dependency>;
|
|
54
91
|
isMinified: boolean;
|
|
55
92
|
} | {
|
|
56
93
|
ok: false,
|
|
57
94
|
warnings: Warning[];
|
|
58
95
|
}
|
|
59
96
|
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
}
|
package/src/obfuscators/index.js
DELETED
|
@@ -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
|
-
}
|
package/src/probes/index.js
DELETED
|
@@ -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
|
-
}
|