8000 Fixed `Maximum call stack size exceeded` error on large strings when … · sec-js/javascript-obfuscator@8585a78 · GitHub
[go: up one dir, main page]

Skip to content

Commit 8585a78

Browse files
author
sanex3339
committed
Fixed Maximum call stack size exceeded error on large strings when splitString option is enabled
1 parent d23eb3e commit 8585a78

File tree

6 files changed

+78
-12
lines changed

6 files changed

+78
-12
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ v0.24.0
66
* Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
77
* Now usage of some browser-related options with `target: 'node'` will cause a validation error
88
* **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
9+
* Fixed `Maximum call stack size exceeded` error on large strings when `splitString` option is enabled
910
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
1011
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
1112
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496

dist/index.browser.js

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.cli.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/node-transformers/converting-transformers/SplitStringTransformer.ts

Lines changed: 49 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { inject, injectable, } from 'inversify';
22
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
33

4+
import * as estraverse from 'estraverse';
45
import * as ESTree from 'estree';
56

67
import { IOptions } from '../../interfaces/options/IOptions';
@@ -13,13 +14,19 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
1314
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
1415
import { NodeFactory } from '../../node/NodeFactory';
1516
import { NodeGuards } from '../../node/NodeGuards';
17+
import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
1618
import { NodeUtils } from '../../node/NodeUtils';
1719

1820
/**
1921
* Splits strings into parts
2022
*/
2123
@injectable()
2224
export class SplitStringTransformer extends AbstractNodeTransformer {
25+
/**
26+
* @type {number}
27+
*/
28+
private static readonly firstPassChunkLength: number = 1000;
29+
2330
/**
2431
* @type {NodeTransformer[]}
2532
*/
@@ -86,26 +93,64 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
8693
}
8794

8895
/**
96+
* Needs to split string on chunks of length `splitStringsChunkLength` in two pass, because of
97+
* `Maximum call stack size exceeded` error in `esrecurse` package
98+
*
8999
* @param {Literal} literalNode
90100
* @param {Node} parentNode
91101
* @returns {Node}
92102
*/
93103
public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
94-
if (typeof literalNode.value !== 'string') {
104+
if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
95105
return literalNode;
96106
}
97107

98-
if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed && parentNode.key === literalNode) {
108+
// pass #1: split string on a large chunks with length of `firstPassChunkLength`
109+
const firstPassChunksNode: ESTree.Node = this.transformLiteralNodeByChunkLength(
110+
literalNode,
111+
parentNode,
112+
SplitStringTransformer.firstPassChunkLength
113+
);
114+
115+
// pass #2: split large chunks on a chunks with length of `splitStringsChunkLength`
116+
const secondPassChunksNode: ESTree.Node = estraverse.replace(firstPassChunksNode, {
117+
/* tslint:disable:no-shadowed-variable */
118+
enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
119+
if (parentNode && NodeGuards.isLiteralNode(node)) {
120+
return this.transformLiteralNodeByChunkLength(
121+
node,
122+
parentNode,
123+
this.options.splitStringsChunkLength
124+
);
125+
}
126+
}
127+
});
128+
129+
return secondPassChunksNode;
130+
}
131+
132+
/**
133+
* @param {Literal} literalNode
134+
* @param {Node} parentNode
135+
* @param {number} chunkLength
136+
* @returns {Node}
137+
*/
138+
private transformLiteralNodeByChunkLength (
139+
literalNode: ESTree.Literal,
140+
parentNode: ESTree.Node,
141+
chunkLength: number
142+
): ESTree.Node {
143+
if (typeof literalNode.value !== 'string') {
99144
return literalNode;
100145
}
101146

102-
if (this.options.splitStringsChunkLength >= literalNode.value.length) {
147+
if (chunkLength >= literalNode.value.length) {
103148
return literalNode;
104149
}
105150

106151
const stringChunks: string[] = SplitStringTransformer.chunkString(
107152
literalNode.value,
108-
this.options.splitStringsChunkLength
153+
chunkLength
109154
);
110155

111156
const binaryExpressionNode: ESTree.BinaryExpression =

test/functional-tests/node-transformers/converting-transformers/split-string-transformer/SplitStringTransformer.spec.ts

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -189,7 +189,7 @@ describe('SplitStringTransformer', () => {
189189
});
190190
});
191191

192-
describe('Variant #10: Integration with `reservedStrings` option', () => {
192+
describe('Variant #11: Integration with `reservedStrings` option', () => {
193193
it('should correctly ignore strings from `reservedStrings` option', () => {
194194
const code: string = readFileAsString(__dirname + '/fixtures/ignore-reserved-strings.js');
195195

@@ -209,4 +209,24 @@ describe('SplitStringTransformer', () => {
209209
);
210210
});
211211
});
212+
213+
describe('Variant #12: Large string', () => {
214+
it('Should does not throw `Maximum call stack size exceeded` error on a large string', () => {
215+
const code: string = `var foo = '${'a'.repeat(10000)}';`;
216+
217+
const testFunc = () => JavaScriptObfuscator.obfuscate(
218+
code,
219+
{
220+
...NO_ADDITIONAL_NODES_PRESET,
221+
splitStrings: true,
222+
splitStringsChunkLength: 2
223+
}
224+
);
225+
226+
assert.doesNotThrow(
227+
testFunc,
228+
Error
229+
);
230+
});
231+
});
212232
});

0 commit comments

Comments
 (0)
0