8000 Fixed cases when dead code is added to the inner code of `eval` expre… · sec-js/javascript-obfuscator@30ba697 · GitHub
[go: up one dir, main page]

Skip to content

Commit 30ba697

Browse files
authored
Fixed cases when dead code is added to the inner code of eval expressions (javascript-obfuscator#1062)
Fixed javascript-obfuscator#1053
1 parent c4dae41 commit 30ba697<
8000
/span>

File tree

9 files changed

+135
-36
lines changed

9 files changed

+135
-36
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Change Log
22

3+
v3.2.7
4+
---
5+
* Fixed cases when dead code is added to the inner code of `eval` expressions. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1053
6+
37
v3.2.6
48
---
59
* Improved integration between `renameProperties` and `controlFlowFlattening` options. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/1053

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "javascript-obfuscator",
3-
"version": "3.2.6",
3+
"version": "3.2.7",
44
"description": "JavaScript obfuscator",
55
"keywords": [
66
"obfuscator",

src/declarations/ESTree.d.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ declare module 'estree' {
1313
ignoredNode?: boolean;
1414
}
1515

16+
export interface FunctionExpressionNodeMetadata extends BaseNodeMetadata {
17+
evalHostNode?: boolean;
18+
}
19+
1620
export interface IdentifierNodeMetadata extends BaseNodeMetadata {
1721
propertyKeyToRenameNode?: boolean
1822
}
@@ -40,6 +44,10 @@ declare module 'estree' {
4044
loc?: acorn.SourceLocation;
4145
}
4246

47+
interface FunctionExpression extends BaseFunction, BaseExpression {
48+
metadata?: FunctionExpressionNodeMetadata;
49+
}
50+
4351
interface Program extends BaseNode {
4452
scope?: eslintScope.Scope | null;
4553
}

src/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
2323
import { BlockStatementDeadCodeInjectionNode } from '../../custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode';
2424
import { NodeFactory } from '../../node/NodeFactory';
2525
import { NodeGuards } from '../../node/NodeGuards';
26+
import { NodeMetadata } from '../../node/NodeMetadata';
2627
import { NodeStatementUtils } from '../../node/NodeStatementUtils';
2728
import { NodeUtils } from '../../node/NodeUtils';
2829

@@ -184,9 +185,18 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
184185

185186
/**
186187
* @param {BlockStatement} blockStatementNode
188+
* @param {Node} parentNode
187189
* @returns {boolean}
188190
*/
189-
private static isValidWrappedBlockStatementNode (blockStatementNode: ESTree.BlockStatement): boolean {
191+
private static isValidWrappedBlockStatementNode (blockStatementNode: ESTree.BlockStatement, parentNode: ESTree.Node): boolean {
192+
/**
193+
* Special case for ignoring all EvalHost nodes that are added by EvalCallExpressionTransformer
194+
* So, all content of eval expressions should not be affected by dead code injection
195+
*/
196+
if (NodeMetadata.isEvalHostNode(parentNode)) {
197+
return false;
198+
}
199+
190200
if (!blockStatementNode.body.length) {
191201
return false;
192202
}
@@ -303,7 +313,7 @@ export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
303313

304314
if (
305315
this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold
306-
|| !DeadCodeInjectionTransformer.isValidWrappedBlockStatementNode(blockStatementNode)
316+
|| !DeadCodeInjectionTransformer.isValidWrappedBlockStatementNode(blockStatementNode, parentNode)
307317
) {
308318
return blockStatementNode;
309319
}

src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts

Lines changed: 30 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { NodeTransformationStage } from '../../enums/node-transformers/NodeTrans
1313
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
1414
import { NodeFactory } from '../../node/NodeFactory';
1515
import { NodeGuards } from '../../node/NodeGuards';
16+
import { NodeMetadata } from '../../node/NodeMetadata';
1617
import { NodeUtils } from '../../node/NodeUtils';
1718
import { StringUtils } from '../../utils/StringUtils';
1819

@@ -27,11 +28,6 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
2728
NodeTransformer.VariablePreserveTransformer
2829
];
2930

30-
/**
31-
* @type {Set <FunctionExpression>}
32-
*/
33-
private readonly evalRootAstHostNodeSet: Set <ESTree.FunctionExpression> = new Set();
34-
3531
/**
3632
* @param {IRandomGenerator} randomGenerator
3733
* @param {IOptions} options
@@ -93,25 +89,16 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
9389
case NodeTransformationStage.Preparing:
9490
return {
9591
enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
96-
if (
97-
parentNode
98-
&& NodeGuards.isCallExpressionNode(node)
99-
&& NodeGuards.isIdentifierNode(node.callee)
100-
&& node.callee.name === 'eval'
101-
) {
92+
if (parentNode) {
10293
return this.transformNode(node, parentNode);
10394
}
10495
}
10596
};
10697

10798
case NodeTransformationStage.Finalizing:
108-
if (!this.evalRootAstHostNodeSet.size) {
109-
return null;
110-
}
111-
11299
return {
113100
leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
114-
if (parentNode && this.isEvalRootAstHostNode(node)) {
101+
if (parentNode) {
115102
return this.restoreNode(node, parentNode);
116103
}
117104
}
@@ -123,22 +110,31 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
123110
}
124111

125112
/**
126-
* @param {CallExpression} callExpressionNode
113+
* @param {Node} node
127114
* @param {Node} parentNode
128115
* @returns {Node}
129116
*/
130-
public transformNode (callExpressionNode: ESTree.CallExpression, parentNode: ESTree.Node F42D ): ESTree.Node {
131-
const callExpressionFirstArgument: ESTree.Expression | ESTree.SpreadElement | undefined = callExpressionNode.arguments[0];
117+
public transformNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
118+
const isEvalCallExpressionNode = parentNode
119+
&& NodeGuards.isCallExpressionNode(node)
120+
&& NodeGuards.isIdentifierNode(node.callee)
121+
&& node.callee.name === 'eval';
122+
123+
if (!isEvalCallExpressionNode) {
124+
return node;
125+
}
126+
127+
const evalCallExpressionFirstArgument: ESTree.Expression | ESTree.SpreadElement | undefined = node.arguments[0];
132128

133-
if (!callExpressionFirstArgument) {
134-
return callExpressionNode;
129+
if (!evalCallExpressionFirstArgument) {
130+
return node;
135131
}
136132

137133
const evalString: string | null = EvalCallExpressionTransformer
138-
.extractEvalStringFromCallExpressionArgument(callExpressionFirstArgument);
134+
.extractEvalStringFromCallExpressionArgument(evalCallExpressionFirstArgument);
139135

140136
if (!evalString) {
141-
return callExpressionNode;
137+
return node;
142138
}
143139

144140
let ast: ESTree.Statement[];
@@ -147,7 +143,7 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
147143
try {
148144
ast = NodeUtils.convertCodeToStructure(evalString);
149145
} catch {
150-
return callExpressionNode;
146+
return node;
151147
}
152148

153149
/**
@@ -157,24 +153,25 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
157153
const evalRootAstHostNode: ESTree.FunctionExpression = NodeFactory
158154
.functionExpressionNode([], NodeFactory.blockStatementNode(ast));
159155

156+
NodeMetadata.set(evalRootAstHostNode, { evalHostNode: true });
157+
160158
NodeUtils.parentizeAst(evalRootAstHostNode);
161159
NodeUtils.parentizeNode(evalRootAstHostNode, parentNode);
162160

163-
/**
164-
* we should store that host node and then extract AST-tree on the `finalizing` stage
165-
*/
166-
this.evalRootAstHostNodeSet.add(evalRootAstHostNode);
167-
168161
return evalRootAstHostNode;
169162
}
170163

171164
/**
172-
* @param {FunctionExpression} evalRootAstHostNode
165+
* @param {Node} node
173166
* @param {Node} parentNode
174167
* @returns {Node}
175168
*/
176-
public restoreNode (evalRootAstHostNode: ESTree.FunctionExpression, parentNode: ESTree.Node): ESTree.Node {
177-
const targetAst: ESTree.Statement[] = evalRootAstHostNode.body.body;
169+
public restoreNode (node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
170+
if (!this.isEvalRootAstHostNode(node)) {
171+
return node;
172+
}
173+
174+
const targetAst: ESTree.Statement[] = node.body.body;
178175
const obfuscatedCode: string = NodeUtils.convertStructureToCode(targetAst);
179176

180177
return NodeFactory.callExpressionNode(
@@ -190,6 +187,6 @@ export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
190187
* @returns {boolean}
191188
*/
192189
private isEvalRootAstHostNode (node: ESTree.Node): node is ESTree.FunctionExpression {
193-
return NodeGuards.isFunctionExpressionNode(node) && this.evalRootAstHostNodeSet.has(node);
190+
return NodeMetadata.isEvalHostNode(node);
194191
}
195192
}

src/node/NodeMetadata.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,14 @@ export class NodeMetadata {
2323
: undefined;
2424
}
2525

26+
/**
27+
* @param {Node} node
28+
* @returns {boolean}
29+
*/
30+
public static isEvalHostNode (node: ESTree.Node): boolean {
31+
return NodeMetadata.get<ESTree.FunctionExpressionNodeMetadata, 'evalHostNode'>(node, 'evalHostNode') === true;
32+
}
33+
2634
/**
2735
* @param {Node} node
2836
* @returns {boolean}

test/functional-tests/node-transformers/dead-code-injection-transformers/DeadCodeInjectionTransformer.spec.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,5 +1090,34 @@ describe('DeadCodeInjectionTransformer', () => {
10901090
assert.equal(matchesLength, expectedMatchesLength);
10911091
});
10921092
});
1093+
1094+
describe('Variant #13 - correct integration with `EvalCallExpressionTransformer`', () => {
1095+
const evalWithDeadCodeRegExp: RegExp = new RegExp(
1096+
`eval\\(\'if *\\(${variableMatch}`,
1097+
'g'
1098+
);
1099+
1100+
let obfuscatedCode: string;
1101+
1102+
before(() => {
1103+
const code: string = readFileAsString(__dirname + '/fixtures/eval-call-expression-transformer-integration.js');
1104+
1105+
obfuscatedCode = JavaScriptObfuscator.obfuscate(
1106+
code,
1107+
{
1108+
...NO_ADDITIONAL_NODES_PRESET,
1109+
deadCodeInjection: true,
1110+
deadCodeInjectionThreshold: 1,
1111+
stringArray: true,
1112+
stringArrayThreshold: 1
1113+
}
1114+
).getObfuscatedCode();
1115+
console.log(obfuscatedCode);
1116+
});
1117+
1118+
it('match #1: shouldn\'t add dead code to the eval call expression', () => {
1119+
assert.notMatch(obfuscatedCode, evalWithDeadCodeRegExp);
1120+
});
1121+
});
10931122
});
10941123
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
(function(){
2+
if (true) {
3+
var foo = function () {
4+
return true;
5+
};
6+
var bar = function () {
7+
return true;
8+
};
9+
var baz = function () {
10+
return true;
11+
};
12+
var bark = function () {
13+
return true;
14+
};
15+
16+
if (true) {
17+
eval('const eval = 1');
18+
}
19+
20+
foo();
21+
bar();
22+
baz();
23+
bark();
24+
}
25+
})();

test/unit-tests/node/node-metadata/NodeMetadata.spec.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,24 @@ describe('NodeMetadata', () => {
5050
});
5151
B9BE });
5252

53+
describe('isEvalHostNode', () => {
54+
const expectedValue: boolean = true;
55+
56+
let node: ESTree.FunctionExpression,
57+
value: boolean | undefined;
58+
59+
before(() => {
60+
node = NodeFactory.functionExpressionNode([], NodeFactory.blockStatementNode([]));
61+
node.metadata = {};
62+
node.metadata.evalHostNode = true;
63+
value = NodeMetadata.isEvalHostNode(node);
64+
});
65+
66+
it('should return metadata value', () => {
67+
assert.equal(value, expectedValue);
68+
});
69+
});
70+
5371
describe('isForceTransformNode', () => {
5472
const expectedValue: boolean = true;
5573

0 commit comments

Comments
 (0)
0