Prechádzať zdrojové kódy

Merge pull request #768 from javascript-obfuscator/uri-malformed-error

Fixed `URI-malformed` when `splitStrings` and `stringArrayEncoding` options are enabled
Timofey Kachalov 4 rokov pred
rodič
commit
252b901b69

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 
+v2.4.2
+---
+* Fixed `URI-malformed` when `splitStrings` and `stringArrayEncoding` options are enabled. https://github.com/javascript-obfuscator/javascript-obfuscator/issues/530
+
 v2.4.1
 ---
 * Small release with some README.md improvements that allow to use it on [obfuscator.io](https://obfuscator.io)

Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
dist/index.browser.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
dist/index.cli.js


Rozdielové dáta súboru neboli zobrazené, pretože súbor je príliš veľký
+ 0 - 0
dist/index.js


+ 6 - 5
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "2.4.1",
+  "version": "2.4.2",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",
@@ -23,7 +23,7 @@
   "dependencies": {
     "@gradecam/tsenum": "1.2.0",
     "@nuxtjs/opencollective": "0.2.2",
-    "acorn": "8.0.1",
+    "acorn": "8.0.2",
     "chalk": "4.1.0",
     "chance": "1.1.7",
     "class-validator": "0.12.2",
@@ -41,6 +41,7 @@
     "reflect-metadata": "0.1.13",
     "source-map-support": "0.5.19",
     "string-template": "1.0.0",
+    "stringz": "2.1.0",
     "tslib": "2.0.1"
   },
   "devDependencies": {
@@ -57,7 +58,7 @@
     "@types/multimatch": "4.0.0",
     "@types/node": "14.11.2",
     "@types/rimraf": "3.0.0",
-    "@types/sinon": "9.0.6",
+    "@types/sinon": "9.0.7",
     "@types/string-template": "1.0.2",
     "@types/webpack-env": "1.15.3",
     "@typescript-eslint/eslint-plugin": "4.3.0",
@@ -67,7 +68,7 @@
     "coveralls": "3.1.0",
     "eslint": "7.10.0",
     "eslint-plugin-import": "2.22.1",
-    "eslint-plugin-jsdoc": "30.6.1",
+    "eslint-plugin-jsdoc": "30.6.2",
     "eslint-plugin-no-null": "1.0.2",
     "eslint-plugin-prefer-arrow": "1.2.2",
     "eslint-plugin-unicorn": "22.0.0",
@@ -78,7 +79,7 @@
     "pjson": "1.0.9",
     "pre-commit": "1.2.2",
     "rimraf": "3.0.2",
-    "sinon": "9.0.3",
+    "sinon": "9.1.0",
     "threads": "1.6.3",
     "ts-loader": "8.0.4",
     "ts-node": "9.0.0",

+ 1 - 1
src/constants/EcmaVersion.ts

@@ -1,3 +1,3 @@
 import * as acorn from 'acorn';
 
-export const ecmaVersion: acorn.Options['ecmaVersion'] = 11;
+export const ecmaVersion: acorn.Options['ecmaVersion'] & number = 11;

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

@@ -3,6 +3,7 @@ import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
 
 import * as estraverse from 'estraverse';
 import * as ESTree from 'estree';
+import * as stringz from 'stringz';
 
 import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
@@ -48,11 +49,16 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
 
     /**
      * @param {string} string
+     * @param {number} stringLength
      * @param {number} chunkSize
      * @returns {string[]}
      */
-    private static chunkString (string: string, chunkSize: number): string[] {
-        const chunksCount: number = Math.ceil(string.length / chunkSize);
+    private static chunkString (
+        string: string,
+        stringLength: number,
+        chunkSize: number
+    ): string[] {
+        const chunksCount: number = Math.ceil(stringLength / chunkSize);
         const chunks: string[] = [];
 
         let nextChunkStartIndex: number = 0;
@@ -62,7 +68,7 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
             chunkIndex < chunksCount;
             ++chunkIndex, nextChunkStartIndex += chunkSize
         ) {
-            chunks[chunkIndex] = string.substr(nextChunkStartIndex, chunkSize);
+            chunks[chunkIndex] = stringz.substr(string, nextChunkStartIndex, chunkSize);
         }
 
         return chunks;
@@ -144,12 +150,15 @@ export class SplitStringTransformer extends AbstractNodeTransformer {
             return literalNode;
         }
 
-        if (chunkLength >= literalNode.value.length) {
+        const valueLength: number = stringz.length(literalNode.value);
+
+        if (chunkLength >= valueLength) {
             return literalNode;
         }
 
         const stringChunks: string[] = SplitStringTransformer.chunkString(
             literalNode.value,
+            valueLength,
             chunkLength
         );
 

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

@@ -1,5 +1,7 @@
 import { assert } from 'chai';
 
+import { StringArrayEncoding } from '../../../../../src/enums/node-transformers/string-array-transformers/StringArrayEncoding';
+
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 import { readFileAsString } from '../../../../helpers/readFileAsString';
@@ -166,7 +168,7 @@ describe('SplitStringTransformer', () => {
     describe('Variant #10: string with emoji', () => {
         describe('Variant #1: single emoji', () => {
             it('should correctly split string with emoji', () => {
-                const regExp: RegExp = /^'a' *\+ *'b' *\+ *'\ud83d' *\+ *'\udc4b' *\+ *'\ud83c' *\+ *'\udffc' *\+ *'c' *\+ *'d';$/;
+                const regExp: RegExp = /^'a' *\+ *'b' *\+ *'👋🏼' *\+ *'c' *\+ *'d';$/;
 
                 const code: string = readFileAsString(__dirname + '/fixtures/string-with-emoji-1.js');
 
@@ -204,7 +206,7 @@ describe('SplitStringTransformer', () => {
 
         describe('Variant #2: multiple emoji', () => {
             it('should correctly split string with emoji', () => {
-                const regExp: RegExp = /^'a' *\+ *'b' *\+ *'\ud83d' *\+ *'\ude34' *\+ *'\ud83d' *\+ *'\ude04' *\+ *'c' *\+ *'d';$/;
+                const regExp: RegExp = /^'a' *\+ *'b' *\+ *'😴' *\+ *'😄' *\+ *'c' *\+ *'d';$/;
 
                 const code: string = readFileAsString(__dirname + '/fixtures/string-with-emoji-2.js');
 
@@ -239,9 +241,79 @@ describe('SplitStringTransformer', () => {
                 assert.equal(resultString, expectedResultString);
             });
         });
+
+        describe('Variant #3: correct split emoji', () => {
+            it('should correctly split string with emoji', () => {
+                const regExp: RegExp = /^'ab👋🏼' *\+ *'cd';$/;
+
+                const code: string = readFileAsString(__dirname + '/fixtures/string-with-emoji-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        splitStrings: true,
+                        splitStringsChunkLength: 3
+                    }
+                ).getObfuscatedCode();
+
+                assert.match(obfuscatedCode,  regExp);
+            });
+
+            it('should correctly evaluate splitted string with emoji', () => {
+                const expectedResultString: string = 'ab👋🏼cd';
+
+                const code: string = readFileAsString(__dirname + '/fixtures/string-with-emoji-1.js');
+
+                obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    code,
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        splitStrings: true,
+                        splitStringsChunkLength: 3
+                    }
+                ).getObfuscatedCode();
+
+                const resultString: string = eval(obfuscatedCode);
+
+                assert.equal(resultString, expectedResultString);
+            });
+        });
+    });
+
+    describe('Variant #11: Integration with `stringArrayEncoding` option', () => {
+        describe('Variant #1: base64 encoding', () => {
+            describe('Variant #1: string with emoji', () => {
+                describe('Variant #1: prevent URI-malformed error', () => {
+                    it('should correctly evaluate splitted string with emoji', () => {
+                        const expectedResultString: string = 'ab👋🏼cd';
+
+                        const code: string = readFileAsString(__dirname + '/fixtures/string-with-emoji-1.js');
+
+                        obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                splitStrings: true,
+                                splitStringsChunkLength: 3,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                stringArrayEncoding: [
+                                    StringArrayEncoding.Base64
+                                ]
+                            }
+                        ).getObfuscatedCode();
+
+                        const resultString: string = eval(obfuscatedCode);
+
+                        assert.equal(resultString, expectedResultString);
+                    });
+                });
+            });
+        });
     });
 
-    describe('Variant #11: Integration with `transformObjectKeys` option', () => {
+    describe('Variant #12: Integration with `transformObjectKeys` option', () => {
         it('should correctly transform string when `transformObjectKeys` option is enabled', () => {
             const regExp: RegExp = new RegExp(`` +
                 `var _0x[a-f0-9]{4,6} *= *{};` +
@@ -267,7 +339,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #12: Integration with `reservedStrings` option', () => {
+    describe('Variant #13: Integration with `reservedStrings` option', () => {
         it('should correctly ignore strings from `reservedStrings` option', () => {
             const code: string = readFileAsString(__dirname + '/fixtures/ignore-reserved-strings.js');
 
@@ -288,7 +360,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #13: Large string', () => {
+    describe('Variant #14: 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)}';`;
 
@@ -308,7 +380,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #14: import declaration source literal', () => {
+    describe('Variant #15: import declaration source literal', () => {
         const importDeclarationRegExp: RegExp = /import *{ *bar *} *from *'foo';/;
 
         let obfuscatedCode: string;
@@ -331,7 +403,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #15: export all declaration source literal', () => {
+    describe('Variant #16: export all declaration source literal', () => {
         const exportAllDeclarationRegExp: RegExp = /export *\* *from *'foo';/;
 
         let obfuscatedCode: string;
@@ -354,7 +426,7 @@ describe('SplitStringTransformer', () => {
         });
     });
 
-    describe('Variant #16: export named declaration source literal', () => {
+    describe('Variant #17: export named declaration source literal', () => {
         const exportNamedDeclarationRegExp: RegExp = /export *{ *bar *} *from *'foo';/;
 
         let obfuscatedCode: string;

+ 28 - 16
yarn.lock

@@ -534,10 +534,10 @@
     "@types/glob" "*"
     "@types/node" "*"
 
-"@types/[email protected].6":
-  version "9.0.6"
-  resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.6.tgz#fb4b6883fe0417e6a1ac5d9753bdcb7016dd4dd0"
-  integrity sha512-j3GK0fiHgn8fe7sqOpInMjm0A2Tary1NBZ8gbI/sZ0C0JxYeO+nh8H0/pW/0l94vNWcH1FnZOZu/cOvIfNZTrg==
+"@types/[email protected].7":
+  version "9.0.7"
+  resolved "https://registry.yarnpkg.com/@types/sinon/-/sinon-9.0.7.tgz#c277e19cf9eb0c71106e785650f1e5c299262302"
+  integrity sha512-uyFiy2gp4P/FK9pmU3WIbT5ZzH54hCswwRkQFhxX7xl8jzhW3g+xOkVqk5YP4cIO//Few8UDAX0MtzFpqBEqwA==
   dependencies:
     "@types/sinonjs__fake-timers" "*"
 
@@ -791,10 +791,10 @@ acorn-jsx@^5.2.0:
   resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.2.0.tgz#4c66069173d6fdd68ed85239fc256226182b2ebe"
   integrity sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==
 
[email protected].1:
-  version "8.0.1"
-  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.1.tgz#d7e8eca9b71d5840db0e7e415b3b2b20e250f938"
-  integrity sha512-dmKn4pqZ29iQl2Pvze1zTrps2luvls2PBY//neO2WJ0s10B3AxJXshN+Ph7B4GrhfGhHXrl4dnUwyNNXQcnWGQ==
[email protected].2:
+  version "8.0.2"
+  resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.0.2.tgz#f7503a253311d4af42332bc188d5713edb2e030a"
+  integrity sha512-t0Dw7AOyeKs4nez4dhzkBDHB28ICo1pxk3UFsLfsCHOkLW+CwbAZJPMa0vBbq0Mqsslhb7n/7H4qB5txaVQ4ew==
 
 acorn@^6.4.1:
   version "6.4.1"
@@ -1383,6 +1383,11 @@ [email protected]:
   resolved "https://registry.yarnpkg.com/chance/-/chance-1.1.7.tgz#e99dde5ac16681af787b5ba94c8277c090d6cfe8"
   integrity sha512-bua/2cZEfzS6qPm0vi3JEvGNbriDLcMj9lKxCQOjUcCJRcyjA7umP0zZm6bKWWlBN04vA0L99QGH/CZQawr0eg==
 
+char-regex@^1.0.2:
+  version "1.0.2"
+  resolved "https://registry.yarnpkg.com/char-regex/-/char-regex-1.0.2.tgz#d744358226217f981ed58f479b1d6bcc29545dcf"
+  integrity sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==
+
 [email protected]:
   version "0.0.2"
   resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
@@ -2138,10 +2143,10 @@ [email protected]:
     resolve "^1.17.0"
     tsconfig-paths "^3.9.0"
 
[email protected].1:
-  version "30.6.1"
-  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.6.1.tgz#eb4124511efb9109eb58bfdcec420eb28a894d31"
-  integrity sha512-qUinrghaxyzcg+FDSt9msv6d898PsI92idiNRZ334qbruhkS6NwVmzSbfpmaij6Xo3czYK42Q2JClvnIiPWpTg==
[email protected].2:
+  version "30.6.2"
+  resolved "https://registry.yarnpkg.com/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-30.6.2.tgz#46f1f449dbf53445da2e8231e6954e4484ab5e04"
+  integrity sha512-LlRdsSQBSPsI3MvhWoGc+Ev3PfFRBk41wwkmbOgC7KP7WQlbeWPpASF5Vdv17XEZ7J+xvPB3KCMyR//6Dbjnnw==
   dependencies:
     comment-parser "^0.7.6"
     debug "^4.1.1"
@@ -5014,10 +5019,10 @@ signal-exit@^3.0.2:
   resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d"
   integrity sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=
 
-sinon@9.0.3:
-  version "9.0.3"
-  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.0.3.tgz#bffc3ec28c936332cd2a41833547b9eed201ecff"
-  integrity sha512-IKo9MIM111+smz9JGwLmw5U1075n1YXeAq8YeSFlndCLhAL5KGn6bLgu7b/4AYHTV/LcEMcRm2wU2YiL55/6Pg==
+sinon@9.1.0:
+  version "9.1.0"
+  resolved "https://registry.yarnpkg.com/sinon/-/sinon-9.1.0.tgz#4afc90707c8e360fe051398eed2d3b197980ffc3"
+  integrity sha512-9zQShgaeylYH6qtsnNXlTvv0FGTTckuDfHBi+qhgj5PvW2r2WslHZpgc3uy3e/ZAoPkqaOASPi+juU6EdYRYxA==
   dependencies:
     "@sinonjs/commons" "^1.7.2"
     "@sinonjs/fake-timers" "^6.0.1"
@@ -5307,6 +5312,13 @@ string_decoder@~1.1.1:
   dependencies:
     safe-buffer "~5.1.0"
 
[email protected]:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/stringz/-/stringz-2.1.0.tgz#5896b4713eac31157556040fb90258fb02c1630c"
+  integrity sha512-KlywLT+MZ+v0IRepfMxRtnSvDCMc3nR1qqCs3m/qIbSOWkNZYT8XHQA31rS3TnKp0c5xjZu3M4GY/2aRKSi/6A==
+  dependencies:
+    char-regex "^1.0.2"
+
 strip-ansi@^4.0.0:
   version "4.0.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"

Niektoré súbory nie sú zobrazené, pretože je v týchto rozdielových dátach zmenené mnoho súborov