@nodesecure/js-x-ray 6.2.1 → 7.0.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 +121 -22
- package/index.d.ts +8 -3
- package/index.js +37 -134
- package/package.json +12 -3
- package/src/AstAnalyser.js +137 -0
- package/src/Deobfuscator.js +192 -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 +7 -4
- package/src/probes/isLiteral.js +10 -8
- 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 +62 -18
- package/types/warnings.d.ts +6 -5
- 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 -33
package/src/probes/isRequire.js
DELETED
|
@@ -1,164 +0,0 @@
|
|
|
1
|
-
/* eslint-disable consistent-return */
|
|
2
|
-
|
|
3
|
-
// Import Third-party Dependencies
|
|
4
|
-
import { Hex } from "@nodesecure/sec-literal";
|
|
5
|
-
import { walk } from "estree-walker";
|
|
6
|
-
import {
|
|
7
|
-
concatBinaryExpression,
|
|
8
|
-
arrayExpressionToString,
|
|
9
|
-
getMemberExpressionIdentifier,
|
|
10
|
-
getCallExpressionIdentifier,
|
|
11
|
-
getCallExpressionArguments
|
|
12
|
-
} from "@nodesecure/estree-ast-utils";
|
|
13
|
-
|
|
14
|
-
function validateNode(node, { tracer }) {
|
|
15
|
-
const id = getCallExpressionIdentifier(node);
|
|
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
|
-
data?.identifierOrMemberExpr ?? void 0
|
|
25
|
-
];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function main(node, options) {
|
|
29
|
-
const { analysis } = options;
|
|
30
|
-
const { tracer } = analysis;
|
|
31
|
-
|
|
32
|
-
if (node.arguments.length === 0) {
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
const arg = node.arguments.at(0);
|
|
36
|
-
|
|
37
|
-
switch (arg.type) {
|
|
38
|
-
// const foo = "http"; require(foo);
|
|
39
|
-
case "Identifier":
|
|
40
|
-
if (analysis.tracer.literalIdentifiers.has(arg.name)) {
|
|
41
|
-
analysis.dependencies.add(
|
|
42
|
-
analysis.tracer.literalIdentifiers.get(arg.name),
|
|
43
|
-
node.loc
|
|
44
|
-
);
|
|
45
|
-
}
|
|
46
|
-
else {
|
|
47
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
48
|
-
}
|
|
49
|
-
break;
|
|
50
|
-
|
|
51
|
-
// require("http")
|
|
52
|
-
case "Literal":
|
|
53
|
-
analysis.dependencies.add(arg.value, node.loc);
|
|
54
|
-
break;
|
|
55
|
-
|
|
56
|
-
// require(["ht", "tp"])
|
|
57
|
-
case "ArrayExpression": {
|
|
58
|
-
const value = [...arrayExpressionToString(arg, { tracer })]
|
|
59
|
-
.join("")
|
|
60
|
-
.trim();
|
|
61
|
-
|
|
62
|
-
if (value === "") {
|
|
63
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
64
|
-
}
|
|
65
|
-
else {
|
|
66
|
-
analysis.dependencies.add(value, node.loc);
|
|
67
|
-
}
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// require("ht" + "tp");
|
|
72
|
-
case "BinaryExpression": {
|
|
73
|
-
if (arg.operator !== "+") {
|
|
74
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
75
|
-
break;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
try {
|
|
79
|
-
const iter = concatBinaryExpression(arg, {
|
|
80
|
-
tracer, stopOnUnsupportedNode: true
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
analysis.dependencies.add([...iter].join(""), node.loc);
|
|
84
|
-
}
|
|
85
|
-
catch {
|
|
86
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
87
|
-
}
|
|
88
|
-
break;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
// require(Buffer.from("...", "hex").toString());
|
|
92
|
-
case "CallExpression": {
|
|
93
|
-
walkRequireCallExpression(arg, tracer)
|
|
94
|
-
.forEach((depName) => analysis.dependencies.add(depName, node.loc, true));
|
|
95
|
-
|
|
96
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
97
|
-
|
|
98
|
-
// We skip walking the tree to avoid anymore warnings...
|
|
99
|
-
return Symbol.for("skipWalk");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
default:
|
|
103
|
-
analysis.addWarning("unsafe-import", null, node.loc);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
function walkRequireCallExpression(nodeToWalk, tracer) {
|
|
108
|
-
const dependencies = new Set();
|
|
109
|
-
|
|
110
|
-
walk(nodeToWalk, {
|
|
111
|
-
enter(node) {
|
|
112
|
-
if (node.type !== "CallExpression" || node.arguments.length === 0) {
|
|
113
|
-
return;
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
const rootArgument = node.arguments.at(0);
|
|
117
|
-
if (rootArgument.type === "Literal" && Hex.isHex(rootArgument.value)) {
|
|
118
|
-
dependencies.add(Buffer.from(rootArgument.value, "hex").toString());
|
|
119
|
-
|
|
120
|
-
return this.skip();
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
const fullName = node.callee.type === "MemberExpression" ?
|
|
124
|
-
[...getMemberExpressionIdentifier(node.callee)].join(".") :
|
|
125
|
-
node.callee.name;
|
|
126
|
-
const tracedFullName = tracer.getDataFromIdentifier(fullName)?.identifierOrMemberExpr ?? fullName;
|
|
127
|
-
|
|
128
|
-
switch (tracedFullName) {
|
|
129
|
-
case "atob": {
|
|
130
|
-
const nodeArguments = getCallExpressionArguments(node, { tracer });
|
|
131
|
-
if (nodeArguments !== null) {
|
|
132
|
-
dependencies.add(
|
|
133
|
-
Buffer.from(nodeArguments.at(0), "base64").toString()
|
|
134
|
-
);
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
break;
|
|
138
|
-
}
|
|
139
|
-
case "Buffer.from": {
|
|
140
|
-
const [element] = node.arguments;
|
|
141
|
-
|
|
142
|
-
if (element.type === "ArrayExpression") {
|
|
143
|
-
const depName = [...arrayExpressionToString(element)].join("").trim();
|
|
144
|
-
dependencies.add(depName);
|
|
145
|
-
}
|
|
146
|
-
break;
|
|
147
|
-
}
|
|
148
|
-
case "require.resolve": {
|
|
149
|
-
if (rootArgument.type === "Literal") {
|
|
150
|
-
dependencies.add(rootArgument.value);
|
|
151
|
-
}
|
|
152
|
-
break;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
return [...dependencies];
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export default {
|
|
162
|
-
name: "isRequire",
|
|
163
|
-
validateNode, main, breakOnMatch: true, breakGroup: "import"
|
|
164
|
-
};
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @description Search for UnaryExpression AST Node
|
|
3
|
-
* @see https://github.com/estree/estree/blob/master/es5.md#unaryexpression
|
|
4
|
-
* @example
|
|
5
|
-
* -2
|
|
6
|
-
*/
|
|
7
|
-
function validateNode(node) {
|
|
8
|
-
return [
|
|
9
|
-
node.type === "UnaryExpression"
|
|
10
|
-
];
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function main(node, options) {
|
|
14
|
-
const { analysis } = options;
|
|
15
|
-
|
|
16
|
-
// Example: !![]
|
|
17
|
-
// See: https://docs.google.com/document/d/11ZrfW0bDQ-kd7Gr_Ixqyk8p3TGvxckmhFH3Z8dFoPhY/edit#
|
|
18
|
-
if (node.argument.type === "UnaryExpression" && node.argument.argument.type === "ArrayExpression") {
|
|
19
|
-
analysis.counter.doubleUnaryArray++;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
export default {
|
|
24
|
-
name: "isUnaryExpression",
|
|
25
|
-
validateNode, main, breakOnMatch: false
|
|
26
|
-
};
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import {
|
|
3
|
-
getVariableDeclarationIdentifiers
|
|
4
|
-
} from "@nodesecure/estree-ast-utils";
|
|
5
|
-
|
|
6
|
-
// In case we are matching a Variable declaration, we have to save the identifier
|
|
7
|
-
// This allow the AST Analysis to retrieve required dependency when the stmt is mixed with variables.
|
|
8
|
-
function validateNode(node) {
|
|
9
|
-
return [
|
|
10
|
-
node.type === "VariableDeclaration"
|
|
11
|
-
];
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
function main(mainNode, options) {
|
|
15
|
-
const { analysis } = options;
|
|
16
|
-
|
|
17
|
-
analysis.varkinds[mainNode.kind]++;
|
|
18
|
-
|
|
19
|
-
for (const node of mainNode.declarations) {
|
|
20
|
-
analysis.idtypes.variableDeclarator++;
|
|
21
|
-
for (const { name } of getVariableDeclarationIdentifiers(node.id)) {
|
|
22
|
-
analysis.identifiersName.push({ name, type: "variableDeclarator" });
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export default {
|
|
28
|
-
name: "isVariableDeclaration",
|
|
29
|
-
validateNode, main, breakOnMatch: false
|
|
30
|
-
};
|
package/src/utils.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
// Import Third-party Dependencies
|
|
2
|
-
import {
|
|
3
|
-
getCallExpressionIdentifier
|
|
4
|
-
} from "@nodesecure/estree-ast-utils";
|
|
5
|
-
|
|
6
|
-
export function notNullOrUndefined(value) {
|
|
7
|
-
return value !== null && value !== void 0;
|
|
8
|
-
}
|
|
9
|
-
|
|
10
|
-
export function isUnsafeCallee(node) {
|
|
11
|
-
const identifier = getCallExpressionIdentifier(node);
|
|
12
|
-
|
|
13
|
-
// For Function we are looking for this: `Function("...")();`
|
|
14
|
-
// A double CallExpression
|
|
15
|
-
return [
|
|
16
|
-
identifier === "eval" || (identifier === "Function" && node.callee.type === "CallExpression"),
|
|
17
|
-
identifier
|
|
18
|
-
];
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export function rootLocation() {
|
|
22
|
-
return { start: { line: 0, column: 0 }, end: { line: 0, column: 0 } };
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export function toArrayLocation(location = rootLocation()) {
|
|
26
|
-
const { start, end = start } = location;
|
|
27
|
-
|
|
28
|
-
return [
|
|
29
|
-
[start.line || 0, start.column || 0],
|
|
30
|
-
[end.line || 0, end.column || 0]
|
|
31
|
-
];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function extractNode(expectedType) {
|
|
35
|
-
return (callback, nodes) => {
|
|
36
|
-
const finalNodes = Array.isArray(nodes) ? nodes : [nodes];
|
|
37
|
-
|
|
38
|
-
for (const node of finalNodes) {
|
|
39
|
-
if (notNullOrUndefined(node) && node.type === expectedType) {
|
|
40
|
-
callback(node);
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function removeHTMLComment(str) {
|
|
47
|
-
return str.replace(/<!--[\s\S]*?(?:-->)/g, "");
|
|
48
|
-
}
|
package/types/astdeps.d.ts
DELETED
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
export {
|
|
2
|
-
ASTDeps,
|
|
3
|
-
SourceLocation,
|
|
4
|
-
Dependency
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
interface SourceLocation {
|
|
8
|
-
start: {
|
|
9
|
-
line: number;
|
|
10
|
-
column: number;
|
|
11
|
-
};
|
|
12
|
-
end: {
|
|
13
|
-
line: number;
|
|
14
|
-
column: number;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
interface Dependency {
|
|
19
|
-
unsafe: boolean;
|
|
20
|
-
inTry: boolean;
|
|
21
|
-
location?: SourceLocation;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
declare class ASTDeps {
|
|
25
|
-
constructor();
|
|
26
|
-
removeByName(name: string): void;
|
|
27
|
-
add(depName: string): void;
|
|
28
|
-
getDependenciesInTryStatement(): IterableIterator<string>;
|
|
29
|
-
|
|
30
|
-
public isInTryStmt: boolean;
|
|
31
|
-
public dependencies: Record<string, Dependency>;
|
|
32
|
-
public readonly size: number;
|
|
33
|
-
}
|