Jelajahi Sumber

Merge pull request #295 from javascript-obfuscator/reserved-strings-option

`reservedStrings` option
Timofey Kachalov 6 tahun lalu
induk
melakukan
605f25075a

+ 4 - 0
CHANGELOG.md

@@ -1,5 +1,9 @@
 Change Log
 ===
+v0.18.0
+---
+* **New option:** `reservedStrings` disables transformation of string literals, which being matched by passed RegExp patterns
+
 v0.17.1
 ---
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/293

+ 18 - 0
README.md

@@ -297,6 +297,7 @@ Following options are available for the JS Obfuscator:
     log: false,
     renameGlobals: false,
     reservedNames: [],
+    reservedStrings: [],
     rotateStringArray: true,
     seed: 0,
     selfDefending: false,
@@ -336,6 +337,7 @@ Following options are available for the JS Obfuscator:
     --log <boolean>
     --rename-globals <boolean>
     --reserved-names '<list>' (comma separated)
+    --reserved-strings '<list>' (comma separated)
     --rotate-string-array <boolean>
     --seed <number>
     --self-defending <boolean>
@@ -633,6 +635,22 @@ Example:
 	}
 ```
 
+### `reservedStrings`
+Type: `string[]` Default: `[]`
+
+Disables transformation of string literals, which being matched by passed RegExp patterns.
+
+Example:
+```ts
+	{
+		reservedStrings: [
+			'react-native',
+			'\.\/src\/test',
+			'some-string_\d'
+		]
+	}
+```
+
 ### `rotateStringArray`
 Type: `boolean` Default: `true`
 

File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.browser.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.cli.js


File diff ditekan karena terlalu besar
+ 0 - 0
dist/index.js


+ 1 - 1
package.json

@@ -1,6 +1,6 @@
 {
   "name": "javascript-obfuscator",
-  "version": "0.17.1",
+  "version": "0.18.0",
   "description": "JavaScript obfuscator",
   "keywords": [
     "obfuscator",

+ 5 - 0
src/cli/JavaScriptObfuscatorCLI.ts

@@ -268,6 +268,11 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 'Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns (comma separated)',
                 ArraySanitizer
             )
+            .option(
+                '--reserved-strings <list> (comma separated, without whitespaces)',
+                'Disables transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
+                ArraySanitizer
+            )
             .option(
                 '--rename-globals <boolean>', 'Allows to enable obfuscation of global variable and function names with declaration.',
                 BooleanSanitizer

+ 1 - 1
src/cli/sanitizers/ArraySanitizer.ts

@@ -6,7 +6,7 @@ import { TCLISanitizer } from '../../types/cli/TCLISanitizer';
  */
 export const ArraySanitizer: TCLISanitizer <string[]> = (value: string): string[] => {
     if (/,$/.test(value)) {
-        throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --option-name 'value1, value2'`);
+        throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --option-name 'value1','value2'`);
     }
 
     return value.split(',').map((string: string) => string.trim());

+ 1 - 0
src/interfaces/options/IOptions.d.ts

@@ -20,6 +20,7 @@ export interface IOptions {
     readonly log: boolean;
     readonly renameGlobals: boolean;
     readonly reservedNames: string[];
+    readonly reservedStrings: string[];
     readonly rotateStringArray: boolean;
     readonly seed: number;
     readonly selfDefending: boolean;

+ 23 - 7
src/node-transformers/obfuscating-transformers/LiteralTransformer.ts

@@ -15,6 +15,7 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
 import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
 import { NodeGuards } from '../../node/NodeGuards';
 import { NodeMetadata } from '../../node/NodeMetadata';
+import { NodeUtils } from '../../node/NodeUtils';
 
 @injectable()
 export class LiteralTransformer extends AbstractNodeTransformer {
@@ -69,22 +70,37 @@ export class LiteralTransformer extends AbstractNodeTransformer {
             return literalNode;
         }
 
+        let newLiteralNode: ESTree.Node;
+
         switch (typeof literalNode.value) {
             case 'boolean':
-                return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.BooleanLiteralObfuscatingReplacer)
-                    .replace(<boolean>literalNode.value);
+                newLiteralNode = this.literalObfuscatingReplacerFactory(
+                    LiteralObfuscatingReplacer.BooleanLiteralObfuscatingReplacer
+                ).replace(<boolean>literalNode.value);
+
+                break;
 
             case 'number':
-                return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.NumberLiteralObfuscatingReplacer)
-                    .replace(<number>literalNode.value);
+                newLiteralNode = this.literalObfuscatingReplacerFactory(
+                    LiteralObfuscatingReplacer.NumberLiteralObfuscatingReplacer
+                ).replace(<number>literalNode.value);
+
+                break;
 
             case 'string':
-                return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.StringLiteralObfuscatingReplacer)
-                    .replace(<string>literalNode.value);
+                newLiteralNode = this.literalObfuscatingReplacerFactory(
+                    LiteralObfuscatingReplacer.StringLiteralObfuscatingReplacer
+                ).replace(<string>literalNode.value);
+
+                break;
 
             default:
-                return literalNode;
+                newLiteralNode = literalNode;
         }
+
+        NodeUtils.parentizeNode(newLiteralNode, parentNode);
+
+        return newLiteralNode;
     }
 
     /**

+ 4 - 0
src/node-transformers/obfuscating-transformers/obfuscating-replacers/identifier-obfuscating-replacers/BaseIdentifierObfuscatingReplacer.ts

@@ -107,6 +107,10 @@ export class BaseIdentifierObfuscatingReplacer extends AbstractObfuscatingReplac
      * @returns {boolean}
      */
     private isReservedName (name: string): boolean {
+        if (!this.options.reservedStrings.length) {
+            return false;
+        }
+
         return this.options.reservedNames
             .some((reservedName: string) => {
                 return new RegExp(reservedName, 'g').exec(name) !== null;

+ 19 - 0
src/node-transformers/obfuscating-transformers/obfuscating-replacers/literal-obfuscating-replacers/StringLiteralObfuscatingReplacer.ts

@@ -133,6 +133,10 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
      * @returns {Node}
      */
     public replace (nodeValue: string): ESTree.Node {
+        if (this.isReservedString(nodeValue)) {
+            return NodeFactory.literalNode(nodeValue);
+        }
+
         const useStringArray: boolean = this.canUseStringArray(nodeValue);
         const cacheKey: string = `${nodeValue}-${String(useStringArray)}`;
         const useCacheValue: boolean = this.nodesCache.has(cacheKey) && this.options.stringArrayEncoding !== StringArrayEncoding.Rc4;
@@ -263,4 +267,19 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
             callExpressionArgs
         );
     }
+
+    /**
+     * @param {string} value
+     * @returns {boolean}
+     */
+    private isReservedString (value: string): boolean {
+        if (!this.options.reservedStrings.length) {
+            return false;
+        }
+
+        return this.options.reservedStrings
+            .some((reservedString: string) => {
+                return new RegExp(reservedString, 'g').exec(value) !== null;
+            });
+    }
 }

+ 10 - 0
src/options/Options.ts

@@ -146,6 +146,16 @@ export class Options implements IOptions {
     })
     public readonly reservedNames!: string[];
 
+    /**
+     * @type {string[]}
+     */
+    @IsArray()
+    @ArrayUnique()
+    @IsString({
+        each: true
+    })
+    public readonly reservedStrings!: string[];
+
     /**
      * @type {boolean}
      */

+ 1 - 0
src/options/presets/Default.ts

@@ -22,6 +22,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     log: false,
     renameGlobals: false,
     reservedNames: [],
+    reservedStrings: [],
     rotateStringArray: true,
     seed: 0,
     selfDefending: false,

+ 1 - 0
src/options/presets/NoCustomNodes.ts

@@ -21,6 +21,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     log: false,
     renameGlobals: false,
     reservedNames: [],
+    reservedStrings: [],
     rotateStringArray: false,
     seed: 0,
     selfDefending: false,

+ 155 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/LiteralTransformer.spec.ts

@@ -315,6 +315,161 @@ describe('LiteralTransformer', () => {
                 assert.match(obfuscatedCode, regExp);
             });
         });
+
+        describe('Variant #12: `reservedNames` option is enabled', () => {
+            describe('Variant #1: base `reservedStrings` values', () => {
+                describe('Variant #1: single reserved string value', () => {
+                    const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
+                    const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
+                        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                reservedStrings: ['foo']
+                            }
+                        );
+
+                        obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    });
+
+                    it('match #1: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp1);
+                    });
+
+                    it('match #2: should transform non-reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp2);
+                    });
+                });
+
+                describe('Variant #2: two reserved string values', () => {
+                    const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
+                    const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
+                        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                reservedStrings: ['foo', 'bar']
+                            }
+                        );
+
+                        obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    });
+
+                    it('match #1: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp1);
+                    });
+
+                    it('match #2: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp2);
+                    });
+                });
+            });
+
+            describe('Variant #2: RegExp `reservedStrings` values', () => {
+                describe('Variant #1: single reserved string value', () => {
+                    const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
+                    const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
+                        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                reservedStrings: ['ar$']
+                            }
+                        );
+
+                        obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    });
+
+                    it('match #1: should transform non-reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp1);
+                    });
+
+                    it('match #2: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp2);
+                    });
+                });
+
+                describe('Variant #2: two reserved string values', () => {
+                    const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
+                    const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
+
+                    let obfuscatedCode: string;
+
+                    before(() => {
+                        const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
+                        const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                            code,
+                            {
+                                ...NO_ADDITIONAL_NODES_PRESET,
+                                stringArray: true,
+                                stringArrayThreshold: 1,
+                                reservedStrings: ['^fo', '.ar']
+                            }
+                        );
+
+                        obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                    });
+
+                    it('match #1: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp1);
+                    });
+
+                    it('match #2: should ignore reserved strings', () => {
+                        assert.match(obfuscatedCode, stringLiteralRegExp2);
+                    });
+                });
+            });
+
+            describe('Variant #3: `unicodeEscapeSequence` option is enabled', () => {
+                const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
+                const stringLiteralRegExp2: RegExp = /const bar *= *'\\x62\\x61\\x72';/;
+
+                let obfuscatedCode: string;
+
+                before(() => {
+                    const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
+                    const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
+                        code,
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            reservedStrings: ['foo'],
+                            unicodeEscapeSequence: true
+                        }
+                    );
+
+                    obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                });
+
+                it('match #1: should ignore reserved strings', () => {
+                    assert.match(obfuscatedCode, stringLiteralRegExp1);
+                });
+
+                it('match #2: should transform non-reserved strings', () => {
+                    assert.match(obfuscatedCode, stringLiteralRegExp2);
+                });
+            });
+        });
     });
 
     describe('transformation of literal node with boolean value', () => {

+ 2 - 0
test/functional-tests/node-transformers/obfuscating-transformers/literal-transformer/fixtures/reserved-strings-option.js

@@ -0,0 +1,2 @@
+const foo = 'foo';
+const bar = 'bar';

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini