Parcourir la source

Fixed `Maximum call stack size exceeded` error on large strings when `splitString` option is enabled

sanex3339 il y a 5 ans
Parent
commit
8585a78672

+ 1 - 0
CHANGELOG.md

@@ -6,6 +6,7 @@ v0.24.0
 * Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
 * Now usage of some browser-related options with `target: 'node'` will cause a validation error
 * **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
+* Fixed `Maximum call stack size exceeded` error on large strings when `splitString` option is enabled
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/516
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496

Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/index.browser.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/index.cli.js


Fichier diff supprimé car celui-ci est trop grand
+ 0 - 0
dist/index.js


+ 49 - 4
src/node-transformers/converting-transformers/SplitStringTransformer.ts

@@ -1,6 +1,7 @@
 import { inject, injectable, } from 'inversify';
 import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
+import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
 
 import { IOptions } from '../../interfaces/options/IOptions';
@@ -13,6 +14,7 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeGuards } from '../../node/NodeGuards';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
 import { NodeUtils } from '../../node/NodeUtils';
 
 /**
@@ -20,6 +22,11 @@ import { NodeUtils } from '../../node/NodeUtils';
  */
 @injectable()
 export class SplitStringTransformer extends AbstractNodeTransformer {
+    /**
+     * @type {number}
+     */
+    private static readonly firstPassChunkLength: number = 1000;
+
     /**
      * @type {NodeTransformer[]}
      */
@@ -86,26 +93,64 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
     }
 
     /**
+     * Needs to split string on chunks of length `splitStringsChunkLength` in two pass, because of
+     * `Maximum call stack size exceeded` error in `esrecurse` package
+     *
      * @param {Literal} literalNode
      * @param {Node} parentNode
      * @returns {Node}
      */
     public transformNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): ESTree.Node {
-        if (typeof literalNode.value !== 'string') {
+        if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
             return literalNode;
         }
 
-        if (NodeGuards.isPropertyNode(parentNode) && !parentNode.computed && parentNode.key === literalNode) {
+        // pass #1: split string on a large chunks with length of `firstPassChunkLength`
+        const firstPassChunksNode: ESTree.Node = this.transformLiteralNodeByChunkLength(
+            literalNode,
+            parentNode,
+            SplitStringTransformer.firstPassChunkLength
+        );
+
+        // pass #2: split large chunks on a chunks with length of `splitStringsChunkLength`
+        const secondPassChunksNode: ESTree.Node = estraverse.replace(firstPassChunksNode, {
+            /* tslint:disable:no-shadowed-variable */
+            enter: (node: ESTree.Node, parentNode: ESTree.Node | null) => {
+                if (parentNode && NodeGuards.isLiteralNode(node)) {
+                    return this.transformLiteralNodeByChunkLength(
+                        node,
+                        parentNode,
+                        this.options.splitStringsChunkLength
+                    );
+                }
+            }
+        });
+
+        return secondPassChunksNode;
+    }
+
+    /**
+     * @param {Literal} literalNode
+     * @param {Node} parentNode
+     * @param {number} chunkLength
+     * @returns {Node}
+     */
+    private transformLiteralNodeByChunkLength (
+        literalNode: ESTree.Literal,
+        parentNode: ESTree.Node,
+        chunkLength: number
+    ): ESTree.Node {
+        if (typeof literalNode.value !== 'string') {
             return literalNode;
         }
 
-        if (this.options.splitStringsChunkLength >= literalNode.value.length) {
+        if (chunkLength >= literalNode.value.length) {
             return literalNode;
         }
 
         const stringChunks: string[] = SplitStringTransformer.chunkString(
             literalNode.value,
-            this.options.splitStringsChunkLength
+            chunkLength
         );
 
         const binaryExpressionNode: ESTree.BinaryExpression =

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

@@ -189,7 +189,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #10: Integration with `reservedStrings` option', () => {
+    describe('Variant #11: Integration with `reservedStrings` option', () => {
         it('should correctly ignore strings from `reservedStrings` option', () => {
             const code: string = readFileAsString(__dirname + '/fixtures/ignore-reserved-strings.js');
 
@@ -209,4 +209,24 @@ describe('SplitStringTransformer', () => {
             );
         });
     });
+
+    describe('Variant #12: Large string', () => {
+        it('Should does not throw `Maximum call stack size exceeded` error on a large string', () => {
+            const code: string = `var foo = '${'a'.repeat(10000)}';`;
+
+            const testFunc = () => JavaScriptObfuscator.obfuscate(
+                code,
+                {
+                    ...NO_ADDITIONAL_NODES_PRESET,
+                    splitStrings: true,
+                    splitStringsChunkLength: 2
+                }
+            );
+
+            assert.doesNotThrow(
+                testFunc,
+                Error
+            );
+        });
+    });
 });

Certains fichiers n'ont pas été affichés car il y a eu trop de fichiers modifiés dans ce diff