Browse Source

Merge pull request #509 from javascript-obfuscator/target-node-disable-unused-features

IsAllowedForObfuscationTarget validator
Timofey Kachalov 5 years ago
parent
commit
50f0bc3305

+ 1 - 0
CHANGELOG.md

@@ -4,6 +4,7 @@ v0.24.0
 ---
 * **Internal refactoring:** completely new mechanism to rename variable names
 * 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 to validation error
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496
 
 v0.23.2

+ 11 - 1
README.md

@@ -589,6 +589,8 @@ Disables the use of `console.log`, `console.info`, `console.error`, `console.war
 ### `domainLock`
 Type: `string[]` Default: `[]`
 
+##### :warning: This option does not allowed to use with `target: 'node'`
+
 Locks the obfuscated source code so it only runs on specific domains and/or sub-domains. This makes really hard for someone just copy and paste your source code and run elsewhere.
 
 ##### Multiple domains and sub-domains
@@ -703,6 +705,8 @@ Randomly shuffles the `stringArray` array items.
 ### `sourceMap`
 Type: `boolean` Default: `false`
 
+##### :warning: This option does not allowed to use with `target: 'node'`
+
 Enables source map generation for obfuscated code.
 
 Source maps can be useful to help you debug your obfuscated JavaScript source code. If you want or need to debug in production, you can upload the separate source map file to a secret location and then point your browser there. 
@@ -710,6 +714,8 @@ Source maps can be useful to help you debug your obfuscated JavaScript source co
 ### `sourceMapBaseUrl`
 Type: `string` Default: ``
 
+##### :warning: This option does not allowed to use with `target: 'node'`
+
 Sets base url to the source map import url when [`sourceMapMode: 'separate'`](#sourcemapmode).
  
 CLI example:
@@ -725,6 +731,8 @@ Result:
 ### `sourceMapFileName`
 Type: `string` Default: ``
 
+##### :warning: This option does not allowed to use with `target: 'node'`
+
 Sets file name for output source map when `sourceMapMode: 'separate'`.
 
 CLI example:
@@ -740,6 +748,8 @@ Result:
 ### `sourceMapMode`
 Type: `string` Default: `separate`
 
+##### :warning: This option does not allowed to use with `target: 'node'`
+
 Specifies source map generation mode:
 * `inline` - emit a single file with source maps instead of having a separate file;
 * `separate` - generates corresponding '.map' file with source map. In case you run obfuscator through CLI - adds link to source map file to the end of file with obfuscated code `//# sourceMappingUrl=file.js.map`.
@@ -808,7 +818,7 @@ Available values:
 * `browser-no-eval`;
 * `node`.
 
-Currently output code for `browser` and `node` targets is identical.
+Currently output code for `browser` and `node` targets is identical, but some browser-specific options are not allowed to use with `node` target.
 Output code for `browser-no-eval` target is not using `eval`.
 
 ### `transformObjectKeys`

File diff suppressed because it is too large
+ 0 - 0
dist/index.browser.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 1 - 0
package.json

@@ -33,6 +33,7 @@
     "eslint-scope": "5.0.0",
     "estraverse": "4.3.0",
     "eventemitter3": "4.0.0",
+    "fast-deep-equal": "3.1.1",
     "inversify": "5.0.1",
     "js-string-escape": "1.0.1",
     "md5": "2.2.1",

+ 2 - 1
src/enums/StringSeparator.ts

@@ -1,3 +1,4 @@
 export enum StringSeparator {
-    Dot = '.'
+    Dot = '.',
+    Comma = ',',
 }

+ 21 - 0
src/options/Options.ts

@@ -34,6 +34,7 @@ import { StringArrayEncoding } from '../enums/StringArrayEncoding';
 import { DEFAULT_PRESET } from './presets/Default';
 
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
+import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 
 @injectable()
 export class Options implements IOptions {
@@ -104,6 +105,10 @@ export class Options implements IOptions {
     @IsString({
         each: true
     })
+    @IsAllowedForObfuscationTargets([
+        ObfuscationTarget.Browser,
+        ObfuscationTarget.BrowserNoEval,
+    ])
     public readonly domainLock!: string[];
 
     /**
@@ -198,6 +203,10 @@ export class Options implements IOptions {
      * @type {boolean}
      */
     @IsBoolean()
+    @IsAllowedForObfuscationTargets([
+        ObfuscationTarget.Browser,
+        ObfuscationTarget.BrowserNoEval,
+    ])
     public readonly sourceMap!: boolean;
 
     /**
@@ -210,18 +219,30 @@ export class Options implements IOptions {
         require_tld: false,
         require_valid_protocol: true
     })
+    @IsAllowedForObfuscationTargets([
+        ObfuscationTarget.Browser,
+        ObfuscationTarget.BrowserNoEval,
+    ])
     public readonly sourceMapBaseUrl!: string;
 
     /**
      * @type {string}
      */
     @IsString()
+    @IsAllowedForObfuscationTargets([
+        ObfuscationTarget.Browser,
+        ObfuscationTarget.BrowserNoEval,
+    ])
     public readonly sourceMapFileName!: string;
 
     /**
      * @type {SourceMapMode}
      */
     @IsIn([SourceMapMode.Inline, SourceMapMode.Separate])
+    @IsAllowedForObfuscationTargets([
+        ObfuscationTarget.Browser,
+        ObfuscationTarget.BrowserNoEval,
+    ])
     public readonly sourceMapMode!: TypeFromEnum<typeof SourceMapMode>;
 
     /**

+ 54 - 0
src/options/validators/IsAllowedForObfuscationTargets.ts

@@ -0,0 +1,54 @@
+import { registerDecorator, ValidationArguments, ValidationOptions } from 'class-validator';
+import { TypeFromEnum } from '@gradecam/tsenum';
+import equal from 'fast-deep-equal';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
+import { StringSeparator } from '../../enums/StringSeparator';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+/**
+ * @param {TypeFromEnum<typeof ObfuscationTarget>[]} obfuscationTargets
+ * @param {ValidationOptions} validationOptions
+ * @returns {(options: IOptions, propertyName: keyof IOptions) => void}
+ */
+export function IsAllowedForObfuscationTargets (
+    obfuscationTargets: TypeFromEnum<typeof ObfuscationTarget>[],
+    validationOptions?: ValidationOptions
+): (options: IOptions, propertyName: keyof IOptions) => void {
+    return (optionsObject: IOptions, propertyName: keyof IOptions) => {
+        registerDecorator({
+            propertyName,
+            constraints: [obfuscationTargets],
+            name: 'IsAllowedForObfuscationTargets',
+            options: validationOptions,
+            target: optionsObject.constructor,
+            validator: {
+                /**
+                 * @param value
+                 * @param {ValidationArguments} validationArguments
+                 * @returns {boolean}
+                 */
+                validate (value: IOptions[keyof IOptions], validationArguments: ValidationArguments): boolean {
+                    const options: IOptions = <IOptions>validationArguments.object;
+                    const defaultValue: IOptions[keyof IOptions] | undefined = DEFAULT_PRESET[propertyName];
+                    const isDefaultValue: boolean = equal(value, defaultValue);
+
+                    return isDefaultValue || obfuscationTargets.includes(options.target);
+                },
+
+                /**
+                 * @param {ValidationArguments} validationArguments
+                 * @returns {string}
+                 */
+                defaultMessage (validationArguments: ValidationArguments): string {
+                    const requiredObfuscationTargetsString: string = obfuscationTargets.join(`${StringSeparator.Comma} `);
+
+                    return `This option allowed only for obfuscation targets: ${requiredObfuscationTargetsString}`;
+                }
+            }
+        });
+    };
+}

+ 3 - 4
test/dev/dev.ts

@@ -1,7 +1,7 @@
 'use strict';
 
 import { NO_ADDITIONAL_NODES_PRESET } from '../../src/options/presets/NoCustomNodes';
-import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
+import { ObfuscationTarget } from '../../src/enums/ObfuscationTarget';
 
 (function () {
     const JavaScriptObfuscator: any = require('../../index');
@@ -14,9 +14,8 @@ import { IdentifierNamesGenerator } from '../../src/enums/generators/identifier-
         `,
         {
             ...NO_ADDITIONAL_NODES_PRESET,
-            compact: false,
-            renameGlobals: true,
-            identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
+            sourceMap: true,
+            target: ObfuscationTarget.Browser
         }
     ).getObfuscatedCode();
 

+ 72 - 0
test/functional-tests/options/domain-lock/Validation.spec.ts

@@ -0,0 +1,72 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
+
+describe('`domainLock` validation', () => {
+    describe('IsAllowedForObfuscationTarget', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: obfuscation target: `browser`', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLock: ['www.example.com'],
+                            target: ObfuscationTarget.Browser
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `browser`', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: obfuscation target: `node` and default value', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLock: [],
+                            target: ObfuscationTarget.Node
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `node` and value is default', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            const expectedError: string = 'This option allowed only for obfuscation targets';
+
+            let testFunc: () => string;
+
+            beforeEach(() => {
+                testFunc = () => JavaScriptObfuscator.obfuscate(
+                    '',
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        domainLock: ['www.example.com'],
+                        target: ObfuscationTarget.Node
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not pass validation when obfuscation target is `node` and value is not default', () => {
+                assert.throws(testFunc, expectedError);
+            });
+        });
+    });
+});

+ 72 - 0
test/functional-tests/options/source-map-base-url/Validation.spec.ts

@@ -0,0 +1,72 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
+
+describe('`sourceMapBaseUrl` validation', () => {
+    describe('IsAllowedForObfuscationTarget', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: obfuscation target: `browser`', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapBaseUrl: 'http://www.example.com',
+                            target: ObfuscationTarget.Browser
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `browser`', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: obfuscation target: `node` and default value', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapBaseUrl: '',
+                            target: ObfuscationTarget.Node
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `node` and value is default', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            const expectedError: string = 'This option allowed only for obfuscation targets';
+
+            let testFunc: () => string;
+
+            beforeEach(() => {
+                testFunc = () => JavaScriptObfuscator.obfuscate(
+                    '',
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        sourceMapBaseUrl: 'http://www.example.com',
+                        target: ObfuscationTarget.Node
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not pass validation when obfuscation target is `node` and value is not default', () => {
+                assert.throws(testFunc, expectedError);
+            });
+        });
+    });
+});

+ 72 - 0
test/functional-tests/options/source-map-file-name/Validation.spec.ts

@@ -0,0 +1,72 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
+
+describe('`sourceMapFileName` validation', () => {
+    describe('IsAllowedForObfuscationTarget', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: obfuscation target: `browser`', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapFileName: 'foo',
+                            target: ObfuscationTarget.Browser
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `browser`', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: obfuscation target: `node` and default value', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapFileName: '',
+                            target: ObfuscationTarget.Node
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `node` and value is default', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            const expectedError: string = 'This option allowed only for obfuscation targets';
+
+            let testFunc: () => string;
+
+            beforeEach(() => {
+                testFunc = () => JavaScriptObfuscator.obfuscate(
+                    '',
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        sourceMapFileName: 'foo',
+                        target: ObfuscationTarget.Node
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not pass validation when obfuscation target is `node` and value is not default', () => {
+                assert.throws(testFunc, expectedError);
+            });
+        });
+    });
+});

+ 73 - 0
test/functional-tests/options/source-map-mode/Validation.spec.ts

@@ -0,0 +1,73 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
+import { SourceMapMode } from '../../../../src/enums/source-map/SourceMapMode';
+
+describe('`sourceMapMode` validation', () => {
+    describe('IsAllowedForObfuscationTarget', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: obfuscation target: `browser`', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapMode: SourceMapMode.Inline,
+                            target: ObfuscationTarget.Browser
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `browser`', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: obfuscation target: `node` and default value', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMapMode: SourceMapMode.Separate,
+                            target: ObfuscationTarget.Node
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `node` and value is default', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            const expectedError: string = 'This option allowed only for obfuscation targets';
+
+            let testFunc: () => string;
+
+            beforeEach(() => {
+                testFunc = () => JavaScriptObfuscator.obfuscate(
+                    '',
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        sourceMapMode: SourceMapMode.Inline,
+                        target: ObfuscationTarget.Node
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not pass validation when obfuscation target is `node` and value is not default', () => {
+                assert.throws(testFunc, expectedError);
+            });
+        });
+    });
+});

+ 72 - 0
test/functional-tests/options/source-map/Validation.spec.ts

@@ -0,0 +1,72 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+import { ObfuscationTarget } from '../../../../src/enums/ObfuscationTarget';
+
+describe('`sourceMap` validation', () => {
+    describe('IsAllowedForObfuscationTarget', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: obfuscation target: `browser`', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMap: true,
+                            target: ObfuscationTarget.Browser
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `browser`', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: obfuscation target: `node` and default value', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            sourceMap: false,
+                            target: ObfuscationTarget.Node
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation when obfuscation target is `node` and value is default', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            const expectedError: string = 'This option allowed only for obfuscation targets';
+
+            let testFunc: () => string;
+
+            beforeEach(() => {
+                testFunc = () => JavaScriptObfuscator.obfuscate(
+                    '',
+                    {
+                        ...NO_ADDITIONAL_NODES_PRESET,
+                        sourceMap: true,
+                        target: ObfuscationTarget.Node
+                    }
+                ).getObfuscatedCode();
+            });
+
+            it('should not pass validation when obfuscation target is `node` and value is not default', () => {
+                assert.throws(testFunc, expectedError);
+            });
+        });
+    });
+});

+ 5 - 0
test/index.spec.ts

@@ -89,6 +89,11 @@ import './functional-tests/node-transformers/preparing-transformers/obfuscating-
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/conditional-comment-obfuscating-guard/ConditionalCommentObfuscatingGuard.spec';
 import './functional-tests/node-transformers/preparing-transformers/obfuscating-guards/reserved-string-obfuscating-guard/ReservedStringObfuscatingGuard.spec';
 import './functional-tests/options/OptionsNormalizer.spec';
+import './functional-tests/options/domain-lock/Validation.spec';
+import './functional-tests/options/source-map/Validation.spec';
+import './functional-tests/options/source-map-base-url/Validation.spec';
+import './functional-tests/options/source-map-file-name/Validation.spec';
+import './functional-tests/options/source-map-mode/Validation.spec';
 import './functional-tests/storages/string-array-storage/StringArrayStorage.spec';
 import './functional-tests/templates/debug-protection-nodes/DebugProtectionFunctionCallTemplate.spec';
 import './functional-tests/templates/domain-lock-nodes/DomainLockNodeTemplate.spec';

+ 5 - 0
yarn.lock

@@ -1765,6 +1765,11 @@ extsprintf@^1.2.0:
   resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
   integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 
[email protected]:
+  version "3.1.1"
+  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.1.tgz#545145077c501491e33b15ec408c294376e94ae4"
+  integrity sha512-8UEa58QDLauDNfpbrX55Q9jrGHThw2ZMdOky5Gl1CDtVeJDPVrG4Jxx1N8jw2gkWaff5UUuX1KJd+9zGe2B+ZA==
+
 fast-deep-equal@^2.0.1:
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"

Some files were not shown because too many files changed in this diff