Browse Source

Sync with dev

sanex 3 years ago
parent
commit
2144b34c88

+ 10 - 1
CHANGELOG.md

@@ -1,9 +1,18 @@
 Change Log
 Change Log
 
 
-v2.15.0
+v2.16.0
 ---
 ---
 * Added support of `es2022` features: private identifiers and class properties
 * Added support of `es2022` features: private identifiers and class properties
 
 
+v2.15.1
+---
+* **Hotfix**: `domainDest` => `domainLockRedirectUrl` option rename
+
+v2.15.0
+---
+* Added `domainDest` option that option allows the browser to be redirected to a passed domain if the source code isn't run on the domains or URL specified by `domainLock`. Thank you https://github.com/erikdubbelboer!
+* `ObfuscationResult` object now contains `getOptions` method to get options that were used during obfuscation
+
 v2.14.0
 v2.14.0
 ---
 ---
 * Added `identifierNamesCache` option for reading and writing identifier names cache. See `README.md`.
 * Added `identifierNamesCache` option for reading and writing identifier names cache. See `README.md`.

+ 25 - 10
README.md

@@ -361,6 +361,7 @@ Following options are available for the JS Obfuscator:
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesCache: null,
     identifierNamesCache: null,
     identifierNamesGenerator: 'hexadecimal',
     identifierNamesGenerator: 'hexadecimal',
@@ -421,6 +422,7 @@ Following options are available for the JS Obfuscator:
     --debug-protection-interval <boolean>
     --debug-protection-interval <boolean>
     --disable-console-output <boolean>
     --disable-console-output <boolean>
     --domain-lock '<list>' (comma separated)
     --domain-lock '<list>' (comma separated)
+    --domain-lock-redirect-url <string>
     --exclude '<list>' (comma separated)
     --exclude '<list>' (comma separated)
     --force-transform-strings '<list>' (comma separated)
     --force-transform-strings '<list>' (comma separated)
     --identifier-names-cache-path <string>
     --identifier-names-cache-path <string>
@@ -688,9 +690,18 @@ Type: `string[]` Default: `[]`
 
 
 Allows to run the obfuscated source code only on specific domains and/or sub-domains. This makes really hard for someone to just copy and paste your source code and run it elsewhere.
 Allows to run the obfuscated source code only on specific domains and/or sub-domains. This makes really hard for someone to just copy and paste your source code and run it elsewhere.
 
 
+If the source code isn't run on the domains specified by this option, the browser will be redirected to a passed to the [`domainLockRedirectUrl`](#domainlockredirecturl) option URL.
+
 ##### Multiple domains and sub-domains
 ##### Multiple domains and sub-domains
 It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`. To make it work on the root domain including any sub-domains (`example.com`, `sub.example.com`), use `.example.com`.
 It's possible to lock your code to more than one domain or sub-domain. For instance, to lock it so the code only runs on **www.example.com** add `www.example.com`. To make it work on the root domain including any sub-domains (`example.com`, `sub.example.com`), use `.example.com`.
 
 
+### `domainLockRedirectUrl`
+Type: `string` Default: `about:blank`
+
+##### :warning: This option does not work with `target: 'node'`
+
+Allows the browser to be redirected to a passed URL if the source code isn't run on the domains specified by [`domainLock`](#domainlock)
+
 ### `exclude`
 ### `exclude`
 Type: `string[]` Default: `[]`
 Type: `string[]` Default: `[]`
 
 
@@ -736,7 +747,7 @@ If an empty object (`{}`) is passed, enables the writing identifier names to the
 The resulting cache-object can be next used as `identifierNamesGenerator` option value for using these names during obfuscation of all matched identifier names of next sources.
 The resulting cache-object can be next used as `identifierNamesGenerator` option value for using these names during obfuscation of all matched identifier names of next sources.
 
 
 Example:
 Example:
-```
+```ts
 const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
 const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
     `
     `
         function foo(arg) {
         function foo(arg) {
@@ -755,12 +766,14 @@ const source1ObfuscationResult = JavaScriptObfuscator.obfuscate(
 )
 )
 
 
 console.log(source1ObfuscationResult.getIdentifierNamesCache());
 console.log(source1ObfuscationResult.getIdentifierNamesCache());
-/*{ 
-    globalIdentifiers: {
-        foo: '_0x5de86d',
-        bar: '_0x2a943b'
+/*
+    { 
+        globalIdentifiers: {
+            foo: '_0x5de86d',
+            bar: '_0x2a943b'
+        }
     }
     }
-}*/
+*/
 
 
 
 
 
 
@@ -781,9 +794,11 @@ const source2ObfuscationResult = JavaScriptObfuscator.obfuscate(
 )
 )
 
 
 console.log(source2ObfuscationResult.getObfuscatedCode());
 console.log(source2ObfuscationResult.getObfuscatedCode());
-// _0x5de86d(0x1);
-// _0x2a943b();
-// baz();
+/*
+    _0x5de86d(0x1);
+    _0x2a943b();
+    baz();
+ */
 ```
 ```
 
 
 #### CLI
 #### CLI
@@ -1600,7 +1615,7 @@ Become a sponsor and get your logo on our README on Github with a link to your s
 ## License
 ## License
 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator?ref=badge_large)
 [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2Fjavascript-obfuscator%2Fjavascript-obfuscator?ref=badge_large)
 
 
-Copyright (C) 2016-2020 [Timofey Kachalov](http://github.com/sanex3339).
+Copyright (C) 2016-2021 [Timofey Kachalov](http://github.com/sanex3339).
 
 
 Redistribution and use in source and binary forms, with or without
 Redistribution and use in source and binary forms, with or without
 modification, are permitted provided that the following conditions are met:
 modification, are permitted provided that the following conditions are met:

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


+ 126 - 42
dist/index.cli.js

@@ -209,7 +209,7 @@ let JavaScriptObfuscator = JavaScriptObfuscator_1 = class JavaScriptObfuscator {
             sourceCode = '';
             sourceCode = '';
         }
         }
         const timeStart = Date.now();
         const timeStart = Date.now();
-        this.logger.info(LoggingMessage_1.LoggingMessage.Version, Utils_1.Utils.buildVersionMessage("2.15.0", 1623003625223));
+        this.logger.info(LoggingMessage_1.LoggingMessage.Version, Utils_1.Utils.buildVersionMessage("2.16.0", 1623404313493));
         this.logger.info(LoggingMessage_1.LoggingMessage.ObfuscationStarted);
         this.logger.info(LoggingMessage_1.LoggingMessage.ObfuscationStarted);
         this.logger.info(LoggingMessage_1.LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getInputSeed());
         this.logger.info(LoggingMessage_1.LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getInputSeed());
         sourceCode = this.runCodeTransformationStage(sourceCode, CodeTransformationStage_1.CodeTransformationStage.PreparingTransformers);
         sourceCode = this.runCodeTransformationStage(sourceCode, CodeTransformationStage_1.CodeTransformationStage.PreparingTransformers);
@@ -423,7 +423,7 @@ class JavaScriptObfuscatorFacade {
     }
     }
 }
 }
 exports.JavaScriptObfuscator = JavaScriptObfuscatorFacade;
 exports.JavaScriptObfuscator = JavaScriptObfuscatorFacade;
-JavaScriptObfuscatorFacade.version = (_a = "2.15.0") !== null && _a !== void 0 ? _a : 'unknown';
+JavaScriptObfuscatorFacade.version = (_a = "2.16.0") !== null && _a !== void 0 ? _a : 'unknown';
 
 
 
 
 /***/ }),
 /***/ }),
@@ -1419,7 +1419,7 @@ class JavaScriptObfuscatorCLI {
     configureCommands() {
     configureCommands() {
         this.commands
         this.commands
             .usage('<inputPath> [options]')
             .usage('<inputPath> [options]')
-            .version(Utils_1.Utils.buildVersionMessage("2.15.0", 1623003625223), '-v, --version')
+            .version(Utils_1.Utils.buildVersionMessage("2.16.0", 1623404313493), '-v, --version')
             .option('-o, --output <path>', 'Output path for obfuscated code')
             .option('-o, --output <path>', 'Output path for obfuscated code')
             .option('--compact <boolean>', 'Disable one line output code compacting', BooleanSanitizer_1.BooleanSanitizer)
             .option('--compact <boolean>', 'Disable one line output code compacting', BooleanSanitizer_1.BooleanSanitizer)
             .option('--config <boolean>', 'Name of js / json config file')
             .option('--config <boolean>', 'Name of js / json config file')
@@ -1431,6 +1431,7 @@ class JavaScriptObfuscatorCLI {
             .option('--debug-protection-interval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', BooleanSanitizer_1.BooleanSanitizer)
             .option('--debug-protection-interval <boolean>', 'Disable browser Debug panel even after page was loaded (can cause DevTools enabled browser freeze)', BooleanSanitizer_1.BooleanSanitizer)
             .option('--disable-console-output <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', BooleanSanitizer_1.BooleanSanitizer)
             .option('--disable-console-output <boolean>', 'Allow console.log, console.info, console.error and console.warn messages output into browser console', BooleanSanitizer_1.BooleanSanitizer)
             .option('--domain-lock <list> (comma separated, without whitespaces)', 'Allows to run the obfuscated source code only on specific domains and/or sub-domains (comma separated)', ArraySanitizer_1.ArraySanitizer)
             .option('--domain-lock <list> (comma separated, without whitespaces)', 'Allows to run the obfuscated source code only on specific domains and/or sub-domains (comma separated)', ArraySanitizer_1.ArraySanitizer)
+            .option('--domain-lock-redirect-url <string>', 'Allows the browser to be redirected to a passed URL if the source code isn\'t run on the domains specified by --domain-lock')
             .option('--exclude <list> (comma separated, without whitespaces)', 'A filename or glob which indicates files to exclude from obfuscation', ArraySanitizer_1.ArraySanitizer)
             .option('--exclude <list> (comma separated, without whitespaces)', 'A filename or glob which indicates files to exclude from obfuscation', ArraySanitizer_1.ArraySanitizer)
             .option('--force-transform-strings <list> (comma separated, without whitespaces)', 'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)', ArraySanitizer_1.ArraySanitizer)
             .option('--force-transform-strings <list> (comma separated, without whitespaces)', 'Enables force transformation of string literals, which being matched by passed RegExp patterns (comma separated)', ArraySanitizer_1.ArraySanitizer)
             .option('--identifier-names-cache-path <string>', 'Sets path for identifier names cache')
             .option('--identifier-names-cache-path <string>', 'Sets path for identifier names cache')
@@ -4725,15 +4726,19 @@ let DomainLockCodeHelper = class DomainLockCodeHelper extends AbstractCustomCode
     }
     }
     getCodeHelperTemplate() {
     getCodeHelperTemplate() {
         const domainsString = this.options.domainLock.join(';');
         const domainsString = this.options.domainLock.join(';');
-        const [hiddenDomainsString, diff] = this.cryptUtils.hideString(domainsString, domainsString.length * 3);
+        const domainsLockRedirectUrl = this.options.domainLockRedirectUrl;
+        const [hiddenDomainsString, domainsStringDiff] = this.cryptUtils.hideString(domainsString, domainsString.length * 3);
+        const [hiddenDomainLockRedirectUrl, domainLockRedirectUrlDiff] = this.cryptUtils.hideString(domainsLockRedirectUrl, domainsLockRedirectUrl.length * 3);
         const globalVariableTemplate = this.options.target !== ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval
         const globalVariableTemplate = this.options.target !== ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate_1.GlobalVariableNoEvalTemplate();
             : GlobalVariableNoEvalTemplate_1.GlobalVariableNoEvalTemplate();
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate_1.DomainLockTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate_1.DomainLockTemplate(), {
             callControllerFunctionName: this.callsControllerFunctionName,
             callControllerFunctionName: this.callsControllerFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
-            diff,
+            domainsStringDiff,
             domains: hiddenDomainsString,
             domains: hiddenDomainsString,
+            domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+            hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
             globalVariableTemplate
             globalVariableTemplate
         });
         });
     }
     }
@@ -4874,34 +4879,43 @@ function DomainLockTemplate() {
             
             
             {globalVariableTemplate}
             {globalVariableTemplate}
             
             
-            const func = function () {
-                return {
-                    key: 'item',
-                    value: 'attribute',
-                    getAttribute: function () {
-                        for (let i = 0; i < 1000; i--) {
-                            const isPositive = i > 0;
-                            
-                            switch (isPositive) {
-                                case true:
-                                    return this.item + '_' + this.value + '_' + i;
-                                default:
-                                    this.item + '_' + this.value;
-                            }
-                        }
-                    }()
-                };
-            };
-                        
-            const regExp = new RegExp("[{diff}]", "g");
+            const regExp = new RegExp("[{domainsStringDiff}]", "g");
             const domains = "{domains}".replace(regExp, "").split(";");
             const domains = "{domains}".replace(regExp, "").split(";");
             let document;
             let document;
             let domain;
             let domain;
             let location;
             let location;
             let hostname;
             let hostname;
 
 
+            const isName = function(name, length, cs) {
+                if (name.length != length) {
+                    return false;
+                }
+
+                for (let i = 0; i < length; i++) {
+                    for (let j = 0; j < cs.length; j += 2) {
+                        if (i == cs[j] && name.charCodeAt(i) != cs[j+1]) {
+                            return false;
+                        }
+                    }
+                }
+
+                return true;
+            };
+
+            const isNameVariant1 = function(cs, name, length) {
+              return isName(name, length, cs);
+            };
+
+            const isNameVariant2 = function(name, cs, length) {
+              return isNameVariant1(cs, name, length);
+            };
+
+            const isNameVariant3 = function(length, name, cs) {
+              return isNameVariant2(name, cs, length);
+            };
+
             for (let d in that) {
             for (let d in that) {
-                if (d.length == 8 && d.charCodeAt(7) == 116 && d.charCodeAt(5) == 101 && d.charCodeAt(3) == 117 && d.charCodeAt(0) == 100) {
+                if (isName(d, 8, [7, 116, 5, 101, 3, 117, 0, 100])) {
                     document = d;
                     document = d;
                 
                 
                     break;
                     break;
@@ -4909,24 +4923,24 @@ function DomainLockTemplate() {
             }
             }
 
 
             for (let d1 in that[document]) {
             for (let d1 in that[document]) {
-                if (d1.length == 6 && d1.charCodeAt(5) == 110 && d1.charCodeAt(0) == 100) {
+                if (isNameVariant3(6, d1, [5, 110, 0, 100])) {
                     domain = d1;
                     domain = d1;
-                    
+
                     break;
                     break;
                 }
                 }
             }
             }
 
 
-            if (!("~" > domain)) {
-                for (let d2 in that[document]) {
-                    if (d2.length == 8 && d2.charCodeAt(7) == 110 && d2.charCodeAt(0) == 108) {
-                        location = d2;
-                        
-                        break;
-                    }
+            for (let d2 in that[document]) {
+                if (isNameVariant2(d2, [7, 110, 0, 108], 8)) {
+                    location = d2;
+
+                    break;
                 }
                 }
+            }
 
 
+            if (!("~" > domain)) {
                 for (let d3 in that[document][location]) {
                 for (let d3 in that[document][location]) {
-                    if (d3.length == 8 && d3.charCodeAt(7) == 101 && d3.charCodeAt(0) == 104) {
+                    if (isNameVariant1([7, 101, 0, 104], d3, 8)) {
                         hostname = d3;
                         hostname = d3;
                         
                         
                         break;
                         break;
@@ -4963,14 +4977,13 @@ function DomainLockTemplate() {
                     }
                     }
                 }
                 }
             }
             }
-               
+
             if (!ok) {
             if (!ok) {
-                data;
-            } else {
-                return;
+                const regExp2 = new RegExp("[{domainLockRedirectUrlDiff}]", "g");
+                const domainLockRedirectUrl = "{hiddenDomainLockRedirectUrl}".replace(regExp2, "");
+
+                that[document][location] = domainLockRedirectUrl;
             }
             }
-            
-            func();
         });
         });
 
 
         {domainLockFunctionName}();
         {domainLockFunctionName}();
@@ -16223,6 +16236,7 @@ const MediumObfuscation_1 = __webpack_require__(/*! ./presets/MediumObfuscation
 const HighObfuscation_1 = __webpack_require__(/*! ./presets/HighObfuscation */ "./src/options/presets/HighObfuscation.ts");
 const HighObfuscation_1 = __webpack_require__(/*! ./presets/HighObfuscation */ "./src/options/presets/HighObfuscation.ts");
 const ValidationErrorsFormatter_1 = __webpack_require__(/*! ./ValidationErrorsFormatter */ "./src/options/ValidationErrorsFormatter.ts");
 const ValidationErrorsFormatter_1 = __webpack_require__(/*! ./ValidationErrorsFormatter */ "./src/options/ValidationErrorsFormatter.ts");
 const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./validators/IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
 const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./validators/IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
+const IsDomainLockRedirectUrl_1 = __webpack_require__(/*! ./validators/IsDomainLockRedirectUrl */ "./src/options/validators/IsDomainLockRedirectUrl.ts");
 const IsIdentifierNamesCache_1 = __webpack_require__(/*! ./validators/IsIdentifierNamesCache */ "./src/options/validators/IsIdentifierNamesCache.ts");
 const IsIdentifierNamesCache_1 = __webpack_require__(/*! ./validators/IsIdentifierNamesCache */ "./src/options/validators/IsIdentifierNamesCache.ts");
 let Options = Options_1 = class Options {
 let Options = Options_1 = class Options {
     constructor(inputOptions, optionsNormalizer) {
     constructor(inputOptions, optionsNormalizer) {
@@ -16302,6 +16316,10 @@ __decorate([
     ]),
     ]),
     __metadata("design:type", Array)
     __metadata("design:type", Array)
 ], Options.prototype, "domainLock", void 0);
 ], Options.prototype, "domainLock", void 0);
+__decorate([
+    IsDomainLockRedirectUrl_1.IsDomainLockRedirectUrl(),
+    __metadata("design:type", String)
+], Options.prototype, "domainLockRedirectUrl", void 0);
 __decorate([
 __decorate([
     class_validator_1.IsArray(),
     class_validator_1.IsArray(),
     class_validator_1.ArrayUnique(),
     class_validator_1.ArrayUnique(),
@@ -16527,6 +16545,7 @@ const inversify_1 = __webpack_require__(/*! inversify */ "inversify");
 const ControlFlowFlatteningThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/ControlFlowFlatteningThresholdRule */ "./src/options/normalizer-rules/ControlFlowFlatteningThresholdRule.ts");
 const ControlFlowFlatteningThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/ControlFlowFlatteningThresholdRule */ "./src/options/normalizer-rules/ControlFlowFlatteningThresholdRule.ts");
 const DeadCodeInjectionRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionRule */ "./src/options/normalizer-rules/DeadCodeInjectionRule.ts");
 const DeadCodeInjectionRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionRule */ "./src/options/normalizer-rules/DeadCodeInjectionRule.ts");
 const DeadCodeInjectionThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionThresholdRule */ "./src/options/normalizer-rules/DeadCodeInjectionThresholdRule.ts");
 const DeadCodeInjectionThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionThresholdRule */ "./src/options/normalizer-rules/DeadCodeInjectionThresholdRule.ts");
+const DomainLockRedirectUrlRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRedirectUrlRule */ "./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts");
 const DomainLockRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRule */ "./src/options/normalizer-rules/DomainLockRule.ts");
 const DomainLockRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRule */ "./src/options/normalizer-rules/DomainLockRule.ts");
 const IdentifierNamesCacheRule_1 = __webpack_require__(/*! ./normalizer-rules/IdentifierNamesCacheRule */ "./src/options/normalizer-rules/IdentifierNamesCacheRule.ts");
 const IdentifierNamesCacheRule_1 = __webpack_require__(/*! ./normalizer-rules/IdentifierNamesCacheRule */ "./src/options/normalizer-rules/IdentifierNamesCacheRule.ts");
 const InputFileNameRule_1 = __webpack_require__(/*! ./normalizer-rules/InputFileNameRule */ "./src/options/normalizer-rules/InputFileNameRule.ts");
 const InputFileNameRule_1 = __webpack_require__(/*! ./normalizer-rules/InputFileNameRule */ "./src/options/normalizer-rules/InputFileNameRule.ts");
@@ -16553,6 +16572,7 @@ OptionsNormalizer.normalizerRules = [
     ControlFlowFlatteningThresholdRule_1.ControlFlowFlatteningThresholdRule,
     ControlFlowFlatteningThresholdRule_1.ControlFlowFlatteningThresholdRule,
     DeadCodeInjectionRule_1.DeadCodeInjectionRule,
     DeadCodeInjectionRule_1.DeadCodeInjectionRule,
     DeadCodeInjectionThresholdRule_1.DeadCodeInjectionThresholdRule,
     DeadCodeInjectionThresholdRule_1.DeadCodeInjectionThresholdRule,
+    DomainLockRedirectUrlRule_1.DomainLockRedirectUrlRule,
     DomainLockRule_1.DomainLockRule,
     DomainLockRule_1.DomainLockRule,
     IdentifierNamesCacheRule_1.IdentifierNamesCacheRule,
     IdentifierNamesCacheRule_1.IdentifierNamesCacheRule,
     InputFileNameRule_1.InputFileNameRule,
     InputFileNameRule_1.InputFileNameRule,
@@ -16691,6 +16711,31 @@ const DeadCodeInjectionThresholdRule = (options) => {
 exports.DeadCodeInjectionThresholdRule = DeadCodeInjectionThresholdRule;
 exports.DeadCodeInjectionThresholdRule = DeadCodeInjectionThresholdRule;
 
 
 
 
+/***/ }),
+
+/***/ "./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts":
+/*!*******************************************************************!*\
+  !*** ./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts ***!
+  \*******************************************************************/
+/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.DomainLockRedirectUrlRule = void 0;
+const Default_1 = __webpack_require__(/*! ../presets/Default */ "./src/options/presets/Default.ts");
+const DomainLockRedirectUrlRule = (options) => {
+    if (!options.domainLock.length) {
+        options = {
+            ...options,
+            domainLockRedirectUrl: Default_1.DEFAULT_PRESET.domainLockRedirectUrl
+        };
+    }
+    return options;
+};
+exports.DomainLockRedirectUrlRule = DomainLockRedirectUrlRule;
+
+
 /***/ }),
 /***/ }),
 
 
 /***/ "./src/options/normalizer-rules/DomainLockRule.ts":
 /***/ "./src/options/normalizer-rules/DomainLockRule.ts":
@@ -17057,6 +17102,7 @@ exports.DEFAULT_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesCache: null,
     identifierNamesCache: null,
@@ -17223,6 +17269,7 @@ exports.NO_ADDITIONAL_NODES_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesGenerator: IdentifierNamesGenerator_1.IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifierNamesGenerator: IdentifierNamesGenerator_1.IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
@@ -17312,6 +17359,40 @@ function IsAllowedForObfuscationTargets(obfuscationTargets, validationOptions) {
 exports.IsAllowedForObfuscationTargets = IsAllowedForObfuscationTargets;
 exports.IsAllowedForObfuscationTargets = IsAllowedForObfuscationTargets;
 
 
 
 
+/***/ }),
+
+/***/ "./src/options/validators/IsDomainLockRedirectUrl.ts":
+/*!***********************************************************!*\
+  !*** ./src/options/validators/IsDomainLockRedirectUrl.ts ***!
+  \***********************************************************/
+/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.IsDomainLockRedirectUrl = void 0;
+const class_validator_1 = __webpack_require__(/*! class-validator */ "class-validator");
+const ObfuscationTarget_1 = __webpack_require__(/*! ../../enums/ObfuscationTarget */ "./src/enums/ObfuscationTarget.ts");
+const Default_1 = __webpack_require__(/*! ../presets/Default */ "./src/options/presets/Default.ts");
+const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
+const IsDomainLockRedirectUrl = () => {
+    return (target, key) => {
+        class_validator_1.ValidateIf(({ domainLockRedirectUrl }) => {
+            return domainLockRedirectUrl !== Default_1.DEFAULT_PRESET.domainLockRedirectUrl;
+        })(target, key);
+        class_validator_1.IsUrl({
+            require_protocol: false,
+            require_host: false
+        })(target, key);
+        IsAllowedForObfuscationTargets_1.IsAllowedForObfuscationTargets([
+            ObfuscationTarget_1.ObfuscationTarget.Browser,
+            ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval,
+        ])(target, key);
+    };
+};
+exports.IsDomainLockRedirectUrl = IsDomainLockRedirectUrl;
+
+
 /***/ }),
 /***/ }),
 
 
 /***/ "./src/options/validators/IsIdentifierNamesCache.ts":
 /***/ "./src/options/validators/IsIdentifierNamesCache.ts":
@@ -17438,6 +17519,9 @@ let ObfuscationResult = class ObfuscationResult {
     getObfuscatedCode() {
     getObfuscatedCode() {
         return this.correctObfuscatedCode();
         return this.correctObfuscatedCode();
     }
     }
+    getOptions() {
+        return this.options;
+    }
     getSourceMap() {
     getSourceMap() {
         return this.sourceMap;
         return this.sourceMap;
     }
     }

+ 122 - 41
dist/index.js

@@ -207,7 +207,7 @@ let JavaScriptObfuscator = JavaScriptObfuscator_1 = class JavaScriptObfuscator {
             sourceCode = '';
             sourceCode = '';
         }
         }
         const timeStart = Date.now();
         const timeStart = Date.now();
-        this.logger.info(LoggingMessage_1.LoggingMessage.Version, Utils_1.Utils.buildVersionMessage("2.15.0", 1623003625223));
+        this.logger.info(LoggingMessage_1.LoggingMessage.Version, Utils_1.Utils.buildVersionMessage("2.16.0", 1623404313493));
         this.logger.info(LoggingMessage_1.LoggingMessage.ObfuscationStarted);
         this.logger.info(LoggingMessage_1.LoggingMessage.ObfuscationStarted);
         this.logger.info(LoggingMessage_1.LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getInputSeed());
         this.logger.info(LoggingMessage_1.LoggingMessage.RandomGeneratorSeed, this.randomGenerator.getInputSeed());
         sourceCode = this.runCodeTransformationStage(sourceCode, CodeTransformationStage_1.CodeTransformationStage.PreparingTransformers);
         sourceCode = this.runCodeTransformationStage(sourceCode, CodeTransformationStage_1.CodeTransformationStage.PreparingTransformers);
@@ -396,7 +396,7 @@ class JavaScriptObfuscatorFacade {
     }
     }
 }
 }
 exports.JavaScriptObfuscator = JavaScriptObfuscatorFacade;
 exports.JavaScriptObfuscator = JavaScriptObfuscatorFacade;
-JavaScriptObfuscatorFacade.version = (_a = "2.15.0") !== null && _a !== void 0 ? _a : 'unknown';
+JavaScriptObfuscatorFacade.version = (_a = "2.16.0") !== null && _a !== void 0 ? _a : 'unknown';
 
 
 
 
 /***/ }),
 /***/ }),
@@ -3939,15 +3939,19 @@ let DomainLockCodeHelper = class DomainLockCodeHelper extends AbstractCustomCode
     }
     }
     getCodeHelperTemplate() {
     getCodeHelperTemplate() {
         const domainsString = this.options.domainLock.join(';');
         const domainsString = this.options.domainLock.join(';');
-        const [hiddenDomainsString, diff] = this.cryptUtils.hideString(domainsString, domainsString.length * 3);
+        const domainsLockRedirectUrl = this.options.domainLockRedirectUrl;
+        const [hiddenDomainsString, domainsStringDiff] = this.cryptUtils.hideString(domainsString, domainsString.length * 3);
+        const [hiddenDomainLockRedirectUrl, domainLockRedirectUrlDiff] = this.cryptUtils.hideString(domainsLockRedirectUrl, domainsLockRedirectUrl.length * 3);
         const globalVariableTemplate = this.options.target !== ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval
         const globalVariableTemplate = this.options.target !== ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate_1.GlobalVariableNoEvalTemplate();
             : GlobalVariableNoEvalTemplate_1.GlobalVariableNoEvalTemplate();
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate_1.DomainLockTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate_1.DomainLockTemplate(), {
             callControllerFunctionName: this.callsControllerFunctionName,
             callControllerFunctionName: this.callsControllerFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
-            diff,
+            domainsStringDiff,
             domains: hiddenDomainsString,
             domains: hiddenDomainsString,
+            domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+            hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
             globalVariableTemplate
             globalVariableTemplate
         });
         });
     }
     }
@@ -4086,34 +4090,43 @@ function DomainLockTemplate() {
             
             
             {globalVariableTemplate}
             {globalVariableTemplate}
             
             
-            const func = function () {
-                return {
-                    key: 'item',
-                    value: 'attribute',
-                    getAttribute: function () {
-                        for (let i = 0; i < 1000; i--) {
-                            const isPositive = i > 0;
-                            
-                            switch (isPositive) {
-                                case true:
-                                    return this.item + '_' + this.value + '_' + i;
-                                default:
-                                    this.item + '_' + this.value;
-                            }
-                        }
-                    }()
-                };
-            };
-                        
-            const regExp = new RegExp("[{diff}]", "g");
+            const regExp = new RegExp("[{domainsStringDiff}]", "g");
             const domains = "{domains}".replace(regExp, "").split(";");
             const domains = "{domains}".replace(regExp, "").split(";");
             let document;
             let document;
             let domain;
             let domain;
             let location;
             let location;
             let hostname;
             let hostname;
 
 
+            const isName = function(name, length, cs) {
+                if (name.length != length) {
+                    return false;
+                }
+
+                for (let i = 0; i < length; i++) {
+                    for (let j = 0; j < cs.length; j += 2) {
+                        if (i == cs[j] && name.charCodeAt(i) != cs[j+1]) {
+                            return false;
+                        }
+                    }
+                }
+
+                return true;
+            };
+
+            const isNameVariant1 = function(cs, name, length) {
+              return isName(name, length, cs);
+            };
+
+            const isNameVariant2 = function(name, cs, length) {
+              return isNameVariant1(cs, name, length);
+            };
+
+            const isNameVariant3 = function(length, name, cs) {
+              return isNameVariant2(name, cs, length);
+            };
+
             for (let d in that) {
             for (let d in that) {
-                if (d.length == 8 && d.charCodeAt(7) == 116 && d.charCodeAt(5) == 101 && d.charCodeAt(3) == 117 && d.charCodeAt(0) == 100) {
+                if (isName(d, 8, [7, 116, 5, 101, 3, 117, 0, 100])) {
                     document = d;
                     document = d;
                 
                 
                     break;
                     break;
@@ -4121,24 +4134,24 @@ function DomainLockTemplate() {
             }
             }
 
 
             for (let d1 in that[document]) {
             for (let d1 in that[document]) {
-                if (d1.length == 6 && d1.charCodeAt(5) == 110 && d1.charCodeAt(0) == 100) {
+                if (isNameVariant3(6, d1, [5, 110, 0, 100])) {
                     domain = d1;
                     domain = d1;
-                    
+
                     break;
                     break;
                 }
                 }
             }
             }
 
 
-            if (!("~" > domain)) {
-                for (let d2 in that[document]) {
-                    if (d2.length == 8 && d2.charCodeAt(7) == 110 && d2.charCodeAt(0) == 108) {
-                        location = d2;
-                        
-                        break;
-                    }
+            for (let d2 in that[document]) {
+                if (isNameVariant2(d2, [7, 110, 0, 108], 8)) {
+                    location = d2;
+
+                    break;
                 }
                 }
+            }
 
 
+            if (!("~" > domain)) {
                 for (let d3 in that[document][location]) {
                 for (let d3 in that[document][location]) {
-                    if (d3.length == 8 && d3.charCodeAt(7) == 101 && d3.charCodeAt(0) == 104) {
+                    if (isNameVariant1([7, 101, 0, 104], d3, 8)) {
                         hostname = d3;
                         hostname = d3;
                         
                         
                         break;
                         break;
@@ -4175,14 +4188,13 @@ function DomainLockTemplate() {
                     }
                     }
                 }
                 }
             }
             }
-               
+
             if (!ok) {
             if (!ok) {
-                data;
-            } else {
-                return;
+                const regExp2 = new RegExp("[{domainLockRedirectUrlDiff}]", "g");
+                const domainLockRedirectUrl = "{hiddenDomainLockRedirectUrl}".replace(regExp2, "");
+
+                that[document][location] = domainLockRedirectUrl;
             }
             }
-            
-            func();
         });
         });
 
 
         {domainLockFunctionName}();
         {domainLockFunctionName}();
@@ -15265,6 +15277,7 @@ const MediumObfuscation_1 = __webpack_require__(/*! ./presets/MediumObfuscation
 const HighObfuscation_1 = __webpack_require__(/*! ./presets/HighObfuscation */ "./src/options/presets/HighObfuscation.ts");
 const HighObfuscation_1 = __webpack_require__(/*! ./presets/HighObfuscation */ "./src/options/presets/HighObfuscation.ts");
 const ValidationErrorsFormatter_1 = __webpack_require__(/*! ./ValidationErrorsFormatter */ "./src/options/ValidationErrorsFormatter.ts");
 const ValidationErrorsFormatter_1 = __webpack_require__(/*! ./ValidationErrorsFormatter */ "./src/options/ValidationErrorsFormatter.ts");
 const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./validators/IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
 const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./validators/IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
+const IsDomainLockRedirectUrl_1 = __webpack_require__(/*! ./validators/IsDomainLockRedirectUrl */ "./src/options/validators/IsDomainLockRedirectUrl.ts");
 const IsIdentifierNamesCache_1 = __webpack_require__(/*! ./validators/IsIdentifierNamesCache */ "./src/options/validators/IsIdentifierNamesCache.ts");
 const IsIdentifierNamesCache_1 = __webpack_require__(/*! ./validators/IsIdentifierNamesCache */ "./src/options/validators/IsIdentifierNamesCache.ts");
 let Options = Options_1 = class Options {
 let Options = Options_1 = class Options {
     constructor(inputOptions, optionsNormalizer) {
     constructor(inputOptions, optionsNormalizer) {
@@ -15344,6 +15357,10 @@ __decorate([
     ]),
     ]),
     __metadata("design:type", Array)
     __metadata("design:type", Array)
 ], Options.prototype, "domainLock", void 0);
 ], Options.prototype, "domainLock", void 0);
+__decorate([
+    IsDomainLockRedirectUrl_1.IsDomainLockRedirectUrl(),
+    __metadata("design:type", String)
+], Options.prototype, "domainLockRedirectUrl", void 0);
 __decorate([
 __decorate([
     class_validator_1.IsArray(),
     class_validator_1.IsArray(),
     class_validator_1.ArrayUnique(),
     class_validator_1.ArrayUnique(),
@@ -15568,6 +15585,7 @@ const inversify_1 = __webpack_require__(/*! inversify */ "inversify");
 const ControlFlowFlatteningThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/ControlFlowFlatteningThresholdRule */ "./src/options/normalizer-rules/ControlFlowFlatteningThresholdRule.ts");
 const ControlFlowFlatteningThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/ControlFlowFlatteningThresholdRule */ "./src/options/normalizer-rules/ControlFlowFlatteningThresholdRule.ts");
 const DeadCodeInjectionRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionRule */ "./src/options/normalizer-rules/DeadCodeInjectionRule.ts");
 const DeadCodeInjectionRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionRule */ "./src/options/normalizer-rules/DeadCodeInjectionRule.ts");
 const DeadCodeInjectionThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionThresholdRule */ "./src/options/normalizer-rules/DeadCodeInjectionThresholdRule.ts");
 const DeadCodeInjectionThresholdRule_1 = __webpack_require__(/*! ./normalizer-rules/DeadCodeInjectionThresholdRule */ "./src/options/normalizer-rules/DeadCodeInjectionThresholdRule.ts");
+const DomainLockRedirectUrlRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRedirectUrlRule */ "./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts");
 const DomainLockRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRule */ "./src/options/normalizer-rules/DomainLockRule.ts");
 const DomainLockRule_1 = __webpack_require__(/*! ./normalizer-rules/DomainLockRule */ "./src/options/normalizer-rules/DomainLockRule.ts");
 const IdentifierNamesCacheRule_1 = __webpack_require__(/*! ./normalizer-rules/IdentifierNamesCacheRule */ "./src/options/normalizer-rules/IdentifierNamesCacheRule.ts");
 const IdentifierNamesCacheRule_1 = __webpack_require__(/*! ./normalizer-rules/IdentifierNamesCacheRule */ "./src/options/normalizer-rules/IdentifierNamesCacheRule.ts");
 const InputFileNameRule_1 = __webpack_require__(/*! ./normalizer-rules/InputFileNameRule */ "./src/options/normalizer-rules/InputFileNameRule.ts");
 const InputFileNameRule_1 = __webpack_require__(/*! ./normalizer-rules/InputFileNameRule */ "./src/options/normalizer-rules/InputFileNameRule.ts");
@@ -15594,6 +15612,7 @@ OptionsNormalizer.normalizerRules = [
     ControlFlowFlatteningThresholdRule_1.ControlFlowFlatteningThresholdRule,
     ControlFlowFlatteningThresholdRule_1.ControlFlowFlatteningThresholdRule,
     DeadCodeInjectionRule_1.DeadCodeInjectionRule,
     DeadCodeInjectionRule_1.DeadCodeInjectionRule,
     DeadCodeInjectionThresholdRule_1.DeadCodeInjectionThresholdRule,
     DeadCodeInjectionThresholdRule_1.DeadCodeInjectionThresholdRule,
+    DomainLockRedirectUrlRule_1.DomainLockRedirectUrlRule,
     DomainLockRule_1.DomainLockRule,
     DomainLockRule_1.DomainLockRule,
     IdentifierNamesCacheRule_1.IdentifierNamesCacheRule,
     IdentifierNamesCacheRule_1.IdentifierNamesCacheRule,
     InputFileNameRule_1.InputFileNameRule,
     InputFileNameRule_1.InputFileNameRule,
@@ -15728,6 +15747,30 @@ const DeadCodeInjectionThresholdRule = (options) => {
 exports.DeadCodeInjectionThresholdRule = DeadCodeInjectionThresholdRule;
 exports.DeadCodeInjectionThresholdRule = DeadCodeInjectionThresholdRule;
 
 
 
 
+/***/ }),
+
+/***/ "./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts":
+/*!*******************************************************************!*\
+  !*** ./src/options/normalizer-rules/DomainLockRedirectUrlRule.ts ***!
+  \*******************************************************************/
+/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
+
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.DomainLockRedirectUrlRule = void 0;
+const Default_1 = __webpack_require__(/*! ../presets/Default */ "./src/options/presets/Default.ts");
+const DomainLockRedirectUrlRule = (options) => {
+    if (!options.domainLock.length) {
+        options = {
+            ...options,
+            domainLockRedirectUrl: Default_1.DEFAULT_PRESET.domainLockRedirectUrl
+        };
+    }
+    return options;
+};
+exports.DomainLockRedirectUrlRule = DomainLockRedirectUrlRule;
+
+
 /***/ }),
 /***/ }),
 
 
 /***/ "./src/options/normalizer-rules/DomainLockRule.ts":
 /***/ "./src/options/normalizer-rules/DomainLockRule.ts":
@@ -16082,6 +16125,7 @@ exports.DEFAULT_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesCache: null,
     identifierNamesCache: null,
@@ -16244,6 +16288,7 @@ exports.NO_ADDITIONAL_NODES_PRESET = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesGenerator: IdentifierNamesGenerator_1.IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifierNamesGenerator: IdentifierNamesGenerator_1.IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
@@ -16332,6 +16377,39 @@ function IsAllowedForObfuscationTargets(obfuscationTargets, validationOptions) {
 exports.IsAllowedForObfuscationTargets = IsAllowedForObfuscationTargets;
 exports.IsAllowedForObfuscationTargets = IsAllowedForObfuscationTargets;
 
 
 
 
+/***/ }),
+
+/***/ "./src/options/validators/IsDomainLockRedirectUrl.ts":
+/*!***********************************************************!*\
+  !*** ./src/options/validators/IsDomainLockRedirectUrl.ts ***!
+  \***********************************************************/
+/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
+
+
+Object.defineProperty(exports, "__esModule", ({ value: true }));
+exports.IsDomainLockRedirectUrl = void 0;
+const class_validator_1 = __webpack_require__(/*! class-validator */ "class-validator");
+const ObfuscationTarget_1 = __webpack_require__(/*! ../../enums/ObfuscationTarget */ "./src/enums/ObfuscationTarget.ts");
+const Default_1 = __webpack_require__(/*! ../presets/Default */ "./src/options/presets/Default.ts");
+const IsAllowedForObfuscationTargets_1 = __webpack_require__(/*! ./IsAllowedForObfuscationTargets */ "./src/options/validators/IsAllowedForObfuscationTargets.ts");
+const IsDomainLockRedirectUrl = () => {
+    return (target, key) => {
+        class_validator_1.ValidateIf(({ domainLockRedirectUrl }) => {
+            return domainLockRedirectUrl !== Default_1.DEFAULT_PRESET.domainLockRedirectUrl;
+        })(target, key);
+        class_validator_1.IsUrl({
+            require_protocol: false,
+            require_host: false
+        })(target, key);
+        IsAllowedForObfuscationTargets_1.IsAllowedForObfuscationTargets([
+            ObfuscationTarget_1.ObfuscationTarget.Browser,
+            ObfuscationTarget_1.ObfuscationTarget.BrowserNoEval,
+        ])(target, key);
+    };
+};
+exports.IsDomainLockRedirectUrl = IsDomainLockRedirectUrl;
+
+
 /***/ }),
 /***/ }),
 
 
 /***/ "./src/options/validators/IsIdentifierNamesCache.ts":
 /***/ "./src/options/validators/IsIdentifierNamesCache.ts":
@@ -16456,6 +16534,9 @@ let ObfuscationResult = class ObfuscationResult {
     getObfuscatedCode() {
     getObfuscatedCode() {
         return this.correctObfuscatedCode();
         return this.correctObfuscatedCode();
     }
     }
+    getOptions() {
+        return this.options;
+    }
     getSourceMap() {
     getSourceMap() {
         return this.sourceMap;
         return this.sourceMap;
     }
     }

+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "javascript-obfuscator",
   "name": "javascript-obfuscator",
-  "version": "2.15.0",
+  "version": "2.16.0",
   "description": "JavaScript obfuscator",
   "description": "JavaScript obfuscator",
   "keywords": [
   "keywords": [
     "obfuscator",
     "obfuscator",
@@ -57,7 +57,7 @@
     "@types/mkdirp": "1.0.1",
     "@types/mkdirp": "1.0.1",
     "@types/mocha": "8.2.2",
     "@types/mocha": "8.2.2",
     "@types/multimatch": "4.0.0",
     "@types/multimatch": "4.0.0",
-    "@types/node": "15.12.1",
+    "@types/node": "15.12.2",
     "@types/rimraf": "3.0.0",
     "@types/rimraf": "3.0.0",
     "@types/sinon": "10.0.2",
     "@types/sinon": "10.0.2",
     "@types/string-template": "1.0.2",
     "@types/string-template": "1.0.2",

+ 4 - 0
src/cli/JavaScriptObfuscatorCLI.ts

@@ -236,6 +236,10 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
                 'Allows to run the obfuscated source code only on specific domains and/or sub-domains (comma separated)',
                 'Allows to run the obfuscated source code only on specific domains and/or sub-domains (comma separated)',
                 ArraySanitizer
                 ArraySanitizer
             )
             )
+            .option(
+                '--domain-lock-redirect-url <string>',
+                'Allows the browser to be redirected to a passed URL if the source code isn\'t run on the domains specified by --domain-lock',
+            )
             .option(
             .option(
                 '--exclude <list> (comma separated, without whitespaces)',
                 '--exclude <list> (comma separated, without whitespaces)',
                 'A filename or glob which indicates files to exclude from obfuscation',
                 'A filename or glob which indicates files to exclude from obfuscation',

+ 9 - 2
src/custom-code-helpers/domain-lock/DomainLockCodeHelper.ts

@@ -89,10 +89,15 @@ export class DomainLockCodeHelper extends AbstractCustomCodeHelper {
      */
      */
     protected override getCodeHelperTemplate (): string {
     protected override getCodeHelperTemplate (): string {
         const domainsString: string = this.options.domainLock.join(';');
         const domainsString: string = this.options.domainLock.join(';');
-        const [hiddenDomainsString, diff]: string[] = this.cryptUtils.hideString(
+        const domainsLockRedirectUrl: string = this.options.domainLockRedirectUrl;
+        const [hiddenDomainsString, domainsStringDiff]: string[] = this.cryptUtils.hideString(
             domainsString,
             domainsString,
             domainsString.length * 3
             domainsString.length * 3
         );
         );
+        const [hiddenDomainLockRedirectUrl, domainLockRedirectUrlDiff]: string[] = this.cryptUtils.hideString(
+            domainsLockRedirectUrl,
+            domainsLockRedirectUrl.length * 3
+        );
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
         const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
             ? this.getGlobalVariableTemplate()
             ? this.getGlobalVariableTemplate()
             : GlobalVariableNoEvalTemplate();
             : GlobalVariableNoEvalTemplate();
@@ -100,8 +105,10 @@ export class DomainLockCodeHelper extends AbstractCustomCodeHelper {
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate(), {
         return this.customCodeHelperFormatter.formatTemplate(DomainLockTemplate(), {
             callControllerFunctionName: this.callsControllerFunctionName,
             callControllerFunctionName: this.callsControllerFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
             domainLockFunctionName: this.domainLockFunctionName,
-            diff,
+            domainsStringDiff,
             domains: hiddenDomainsString,
             domains: hiddenDomainsString,
+            domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+            hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
             globalVariableTemplate
             globalVariableTemplate
         });
         });
     }
     }

+ 45 - 37
src/custom-code-helpers/domain-lock/templates/DomainLockTemplate.ts

@@ -7,34 +7,43 @@ export function DomainLockTemplate (): string {
             
             
             {globalVariableTemplate}
             {globalVariableTemplate}
             
             
-            const func = function () {
-                return {
-                    key: 'item',
-                    value: 'attribute',
-                    getAttribute: function () {
-                        for (let i = 0; i < 1000; i--) {
-                            const isPositive = i > 0;
-                            
-                            switch (isPositive) {
-                                case true:
-                                    return this.item + '_' + this.value + '_' + i;
-                                default:
-                                    this.item + '_' + this.value;
-                            }
-                        }
-                    }()
-                };
-            };
-                        
-            const regExp = new RegExp("[{diff}]", "g");
+            const regExp = new RegExp("[{domainsStringDiff}]", "g");
             const domains = "{domains}".replace(regExp, "").split(";");
             const domains = "{domains}".replace(regExp, "").split(";");
             let document;
             let document;
             let domain;
             let domain;
             let location;
             let location;
             let hostname;
             let hostname;
 
 
+            const isName = function(name, length, cs) {
+                if (name.length != length) {
+                    return false;
+                }
+
+                for (let i = 0; i < length; i++) {
+                    for (let j = 0; j < cs.length; j += 2) {
+                        if (i == cs[j] && name.charCodeAt(i) != cs[j+1]) {
+                            return false;
+                        }
+                    }
+                }
+
+                return true;
+            };
+
+            const isNameVariant1 = function(cs, name, length) {
+              return isName(name, length, cs);
+            };
+
+            const isNameVariant2 = function(name, cs, length) {
+              return isNameVariant1(cs, name, length);
+            };
+
+            const isNameVariant3 = function(length, name, cs) {
+              return isNameVariant2(name, cs, length);
+            };
+
             for (let d in that) {
             for (let d in that) {
-                if (d.length == 8 && d.charCodeAt(7) == 116 && d.charCodeAt(5) == 101 && d.charCodeAt(3) == 117 && d.charCodeAt(0) == 100) {
+                if (isName(d, 8, [7, 116, 5, 101, 3, 117, 0, 100])) {
                     document = d;
                     document = d;
                 
                 
                     break;
                     break;
@@ -42,24 +51,24 @@ export function DomainLockTemplate (): string {
             }
             }
 
 
             for (let d1 in that[document]) {
             for (let d1 in that[document]) {
-                if (d1.length == 6 && d1.charCodeAt(5) == 110 && d1.charCodeAt(0) == 100) {
+                if (isNameVariant3(6, d1, [5, 110, 0, 100])) {
                     domain = d1;
                     domain = d1;
-                    
+
                     break;
                     break;
                 }
                 }
             }
             }
 
 
-            if (!("~" > domain)) {
-                for (let d2 in that[document]) {
-                    if (d2.length == 8 && d2.charCodeAt(7) == 110 && d2.charCodeAt(0) == 108) {
-                        location = d2;
-                        
-                        break;
-                    }
+            for (let d2 in that[document]) {
+                if (isNameVariant2(d2, [7, 110, 0, 108], 8)) {
+                    location = d2;
+
+                    break;
                 }
                 }
+            }
 
 
+            if (!("~" > domain)) {
                 for (let d3 in that[document][location]) {
                 for (let d3 in that[document][location]) {
-                    if (d3.length == 8 && d3.charCodeAt(7) == 101 && d3.charCodeAt(0) == 104) {
+                    if (isNameVariant1([7, 101, 0, 104], d3, 8)) {
                         hostname = d3;
                         hostname = d3;
                         
                         
                         break;
                         break;
@@ -96,14 +105,13 @@ export function DomainLockTemplate (): string {
                     }
                     }
                 }
                 }
             }
             }
-               
+
             if (!ok) {
             if (!ok) {
-                data;
-            } else {
-                return;
+                const regExp2 = new RegExp("[{domainLockRedirectUrlDiff}]", "g");
+                const domainLockRedirectUrl = "{hiddenDomainLockRedirectUrl}".replace(regExp2, "");
+
+                that[document][location] = domainLockRedirectUrl;
             }
             }
-            
-            func();
         });
         });
 
 
         {domainLockFunctionName}();
         {domainLockFunctionName}();

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

@@ -20,6 +20,7 @@ export interface IOptions {
     readonly debugProtectionInterval: boolean;
     readonly debugProtectionInterval: boolean;
     readonly disableConsoleOutput: boolean;
     readonly disableConsoleOutput: boolean;
     readonly domainLock: string[];
     readonly domainLock: string[];
+    readonly domainLockRedirectUrl: string;
     readonly forceTransformStrings: string[];
     readonly forceTransformStrings: string[];
     readonly identifierNamesCache: TIdentifierNamesCache;
     readonly identifierNamesCache: TIdentifierNamesCache;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;
     readonly identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>;

+ 7 - 0
src/interfaces/source-code/IObfuscationResult.ts

@@ -1,5 +1,7 @@
 import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
 import { TIdentifierNamesCache } from '../../types/TIdentifierNamesCache';
+
 import { IInitializable } from '../IInitializable';
 import { IInitializable } from '../IInitializable';
+import { IOptions } from '../options/IOptions';
 
 
 export interface IObfuscationResult extends IInitializable <[string, string]> {
 export interface IObfuscationResult extends IInitializable <[string, string]> {
     /**
     /**
@@ -12,6 +14,11 @@ export interface IObfuscationResult extends IInitializable <[string, string]> {
      */
      */
     getObfuscatedCode (): string;
     getObfuscatedCode (): string;
 
 
+    /**
+     * @return {IOptions}
+     */
+    getOptions (): IOptions;
+
     /**
     /**
      * @return {string}
      * @return {string}
      */
      */

+ 7 - 0
src/options/Options.ts

@@ -46,6 +46,7 @@ import { HIGH_OBFUSCATION_PRESET } from './presets/HighObfuscation';
 
 
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { ValidationErrorsFormatter } from './ValidationErrorsFormatter';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
 import { IsAllowedForObfuscationTargets } from './validators/IsAllowedForObfuscationTargets';
+import { IsDomainLockRedirectUrl } from './validators/IsDomainLockRedirectUrl';
 import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
 import { IsIdentifierNamesCache } from './validators/IsIdentifierNamesCache';
 
 
 @injectable()
 @injectable()
@@ -134,6 +135,12 @@ export class Options implements IOptions {
     ])
     ])
     public readonly domainLock!: string[];
     public readonly domainLock!: string[];
 
 
+    /**
+     * @type {string}
+     */
+    @IsDomainLockRedirectUrl()
+    public readonly domainLockRedirectUrl!: string;
+
     /**
     /**
      * @type {string[]}
      * @type {string[]}
      */
      */

+ 2 - 0
src/options/OptionsNormalizer.ts

@@ -8,6 +8,7 @@ import { IOptionsNormalizer } from '../interfaces/options/IOptionsNormalizer';
 import { ControlFlowFlatteningThresholdRule } from './normalizer-rules/ControlFlowFlatteningThresholdRule';
 import { ControlFlowFlatteningThresholdRule } from './normalizer-rules/ControlFlowFlatteningThresholdRule';
 import { DeadCodeInjectionRule } from './normalizer-rules/DeadCodeInjectionRule';
 import { DeadCodeInjectionRule } from './normalizer-rules/DeadCodeInjectionRule';
 import { DeadCodeInjectionThresholdRule } from './normalizer-rules/DeadCodeInjectionThresholdRule';
 import { DeadCodeInjectionThresholdRule } from './normalizer-rules/DeadCodeInjectionThresholdRule';
+import { DomainLockRedirectUrlRule } from './normalizer-rules/DomainLockRedirectUrlRule';
 import { DomainLockRule } from './normalizer-rules/DomainLockRule';
 import { DomainLockRule } from './normalizer-rules/DomainLockRule';
 import { IdentifierNamesCacheRule } from './normalizer-rules/IdentifierNamesCacheRule';
 import { IdentifierNamesCacheRule } from './normalizer-rules/IdentifierNamesCacheRule';
 import { InputFileNameRule } from './normalizer-rules/InputFileNameRule';
 import { InputFileNameRule } from './normalizer-rules/InputFileNameRule';
@@ -29,6 +30,7 @@ export class OptionsNormalizer implements IOptionsNormalizer {
         ControlFlowFlatteningThresholdRule,
         ControlFlowFlatteningThresholdRule,
         DeadCodeInjectionRule,
         DeadCodeInjectionRule,
         DeadCodeInjectionThresholdRule,
         DeadCodeInjectionThresholdRule,
+        DomainLockRedirectUrlRule,
         DomainLockRule,
         DomainLockRule,
         IdentifierNamesCacheRule,
         IdentifierNamesCacheRule,
         InputFileNameRule,
         InputFileNameRule,

+ 20 - 0
src/options/normalizer-rules/DomainLockRedirectUrlRule.ts

@@ -0,0 +1,20 @@
+import { TOptionsNormalizerRule } from '../../types/options/TOptionsNormalizerRule';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+/**
+ * @param {IOptions} options
+ * @returns {IOptions}
+ */
+export const DomainLockRedirectUrlRule: TOptionsNormalizerRule = (options: IOptions): IOptions => {
+    if (!options.domainLock.length) {
+        options = {
+            ...options,
+            domainLockRedirectUrl: <string>DEFAULT_PRESET.domainLockRedirectUrl
+        };
+    }
+
+    return options;
+};

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

@@ -20,6 +20,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesCache: null,
     identifierNamesCache: null,

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

@@ -18,6 +18,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     debugProtectionInterval: false,
     debugProtectionInterval: false,
     disableConsoleOutput: false,
     disableConsoleOutput: false,
     domainLock: [],
     domainLock: [],
+    domainLockRedirectUrl: 'about:blank',
     exclude: [],
     exclude: [],
     forceTransformStrings: [],
     forceTransformStrings: [],
     identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,
     identifierNamesGenerator: IdentifierNamesGenerator.HexadecimalIdentifierNamesGenerator,

+ 30 - 0
src/options/validators/IsDomainLockRedirectUrl.ts

@@ -0,0 +1,30 @@
+import { IsUrl, ValidateIf } from 'class-validator';
+
+import { TInputOptions } from '../../types/options/TInputOptions';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+
+import { ObfuscationTarget } from '../../enums/ObfuscationTarget';
+
+import { DEFAULT_PRESET } from '../presets/Default';
+
+import { IsAllowedForObfuscationTargets } from './IsAllowedForObfuscationTargets';
+
+/**
+ * @returns {PropertyDecorator}
+ */
+export const IsDomainLockRedirectUrl = (): PropertyDecorator => {
+    return (target: any, key: string | symbol): void => {
+        ValidateIf(({domainLockRedirectUrl}: TInputOptions) => {
+            return domainLockRedirectUrl !== DEFAULT_PRESET.domainLockRedirectUrl;
+        })(target, key);
+        IsUrl({
+            require_protocol: false,
+            require_host: false
+        })(target, key);
+        IsAllowedForObfuscationTargets([
+            ObfuscationTarget.Browser,
+            ObfuscationTarget.BrowserNoEval,
+        ])(target, <keyof IOptions>key);
+    };
+};

+ 7 - 0
src/source-code/ObfuscationResult.ts

@@ -96,6 +96,13 @@ export class ObfuscationResult implements IObfuscationResult {
         return this.correctObfuscatedCode();
         return this.correctObfuscatedCode();
     }
     }
 
 
+    /**
+     * @returns {IOptions}
+     */
+    public getOptions (): IOptions {
+        return this.options;
+    }
+
     /**
     /**
      * @returns {string}
      * @returns {string}
      */
      */

+ 424 - 170
test/functional-tests/custom-code-helpers/domain-lock/templates/DomainLockNodeTemplate.spec.ts

@@ -13,53 +13,11 @@ import { IObfuscationResult } from '../../../../../src/interfaces/source-code/IO
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 import { NO_ADDITIONAL_NODES_PRESET } from '../../../../../src/options/presets/NoCustomNodes';
 
 
 import { DomainLockTemplate } from '../../../../../src/custom-code-helpers/domain-lock/templates/DomainLockTemplate';
 import { DomainLockTemplate } from '../../../../../src/custom-code-helpers/domain-lock/templates/DomainLockTemplate';
-import { GlobalVariableTemplate1 } from '../../../../../src/custom-code-helpers/common/templates/GlobalVariableTemplate1';
 
 
 import { InversifyContainerFacade } from '../../../../../src/container/InversifyContainerFacade';
 import { InversifyContainerFacade } from '../../../../../src/container/InversifyContainerFacade';
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 import { JavaScriptObfuscator } from '../../../../../src/JavaScriptObfuscatorFacade';
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 import { readFileAsString } from '../../../../helpers/readFileAsString';
 
 
-/**
- * @param {string} currentDomain
- * @returns {string}
- */
-function getDocumentDomainTemplate (currentDomain: string): string {
-    return `
-        document = {
-            domain: '${currentDomain}'
-        };
-    `
-}
-
-/**
- * @param {string} currentDomain
- * @returns {string}
- */
-function getDocumentLocationTemplate (currentDomain: string): string {
-    return `
-        document = {
-            location: {
-                hostname: '${currentDomain}'
-            }
-        };
-    `
-}
-
-/**
- * @param {string} currentDomain
- * @returns {string}
- */
-function getDocumentDomainAndLocationTemplate (currentDomain: string): string {
-    return `
-        document = {
-            domain: '${currentDomain}',
-            location: {
-                hostname: '${currentDomain}'
-            }
-        };
-    `
-}
-
 /**
 /**
  * @param templateData
  * @param templateData
  * @param {string} callsControllerFunctionName
  * @param {string} callsControllerFunctionName
@@ -85,11 +43,12 @@ function getFunctionFromTemplate (
         })();
         })();
 
 
         ${domainLockTemplate}
         ${domainLockTemplate}
-    `)();
+    `);
 }
 }
 
 
 describe('DomainLockTemplate', () => {
 describe('DomainLockTemplate', () => {
     const singleCallControllerFunctionName: string = 'callsController';
     const singleCallControllerFunctionName: string = 'callsController';
+    const thatThisTemplate = 'const that = this;';
 
 
     let cryptUtils: ICryptUtils;
     let cryptUtils: ICryptUtils;
 
 
@@ -102,393 +61,614 @@ describe('DomainLockTemplate', () => {
 
 
     describe('Variant #1: current domain matches with `domainsString`', () => {
     describe('Variant #1: current domain matches with `domainsString`', () => {
         const domainsString: string = ['www.example.com'].join(';');
         const domainsString: string = ['www.example.com'].join(';');
+        const domainLockRedirectUrl: string = 'about:blank';
         const currentDomain: string = 'www.example.com';
         const currentDomain: string = 'www.example.com';
 
 
-        let testFunc: () => void;
+        let testFunc: Function;
+        let root: any;
 
 
         before(() => {
         before(() => {
             const [
             const [
                 hiddenDomainsString,
                 hiddenDomainsString,
-                diff
+                domainsStringDiff
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-            testFunc = () => getFunctionFromTemplate(
+            const [
+                hiddenDomainLockRedirectUrl,
+                domainLockRedirectUrlDiff
+            ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+            root = {
+                document: {
+                    domain: currentDomain,
+                    location: undefined,
+                },
+            };
+
+            testFunc = getFunctionFromTemplate(
                 {
                 {
                     domainLockFunctionName: 'domainLockFunction',
                     domainLockFunctionName: 'domainLockFunction',
-                    diff: diff,
+                    domainsStringDiff,
                     domains: hiddenDomainsString,
                     domains: hiddenDomainsString,
-                    globalVariableTemplate: GlobalVariableTemplate1(),
+                    domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                    hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                    globalVariableTemplate: '',
                     singleCallControllerFunctionName
                     singleCallControllerFunctionName
                 },
                 },
                 singleCallControllerFunctionName,
                 singleCallControllerFunctionName,
-                getDocumentDomainTemplate(currentDomain)
+                thatThisTemplate
             );
             );
         });
         });
 
 
         it('should correctly run code inside template', () => {
         it('should correctly run code inside template', () => {
-            assert.doesNotThrow(testFunc);
+            assert.doesNotThrow(() => testFunc.apply(root));
+            assert.isUndefined(root.document.location);
         });
         });
     });
     });
 
 
     describe('Variant #2: current domain matches with base domain of `domainsString`', () => {
     describe('Variant #2: current domain matches with base domain of `domainsString`', () => {
         const domainsString: string = ['.example.com'].join(';');
         const domainsString: string = ['.example.com'].join(';');
+        const domainLockRedirectUrl: string = 'about:blank';
         const currentDomain: string = 'www.example.com';
         const currentDomain: string = 'www.example.com';
 
 
-        let testFunc: () => void;
+        let testFunc: Function;
+        let root: any;
 
 
         before(() => {
         before(() => {
             const [
             const [
                 hiddenDomainsString,
                 hiddenDomainsString,
-                diff
+                domainsStringDiff
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-            testFunc = () => getFunctionFromTemplate(
+            const [
+                hiddenDomainLockRedirectUrl,
+                domainLockRedirectUrlDiff
+            ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+            root = {
+                document: {
+                    domain: currentDomain,
+                    location: undefined,
+                },
+            };
+
+            testFunc = getFunctionFromTemplate(
                 {
                 {
                     domainLockFunctionName: 'domainLockFunction',
                     domainLockFunctionName: 'domainLockFunction',
-                    diff: diff,
+                    domainsStringDiff,
                     domains: hiddenDomainsString,
                     domains: hiddenDomainsString,
-                    globalVariableTemplate: GlobalVariableTemplate1(),
+                    domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                    hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                    globalVariableTemplate: '',
                     singleCallControllerFunctionName
                     singleCallControllerFunctionName
                 },
                 },
                 singleCallControllerFunctionName,
                 singleCallControllerFunctionName,
-                getDocumentDomainTemplate(currentDomain)
+                thatThisTemplate
             );
             );
         });
         });
 
 
         it('should correctly run code inside template', () => {
         it('should correctly run code inside template', () => {
-            assert.doesNotThrow(testFunc);
+            assert.doesNotThrow(() => testFunc.apply(root));
+            assert.isUndefined(root.document.location);
         });
         });
     });
     });
 
 
     describe('Variant #3: current domain matches with root domain of `domainsString`', () => {
     describe('Variant #3: current domain matches with root domain of `domainsString`', () => {
         const domainsString: string = ['.example.com'].join(';');
         const domainsString: string = ['.example.com'].join(';');
+        const domainLockRedirectUrl: string = 'about:blank';
         const currentDomain: string = 'example.com';
         const currentDomain: string = 'example.com';
 
 
-        let testFunc: () => void;
+        let testFunc: Function;
+        let root: any;
 
 
         before(() => {
         before(() => {
             const [
             const [
                 hiddenDomainsString,
                 hiddenDomainsString,
-                diff
+                domainsStringDiff
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-            testFunc = () => getFunctionFromTemplate(
+            const [
+                hiddenDomainLockRedirectUrl,
+                domainLockRedirectUrlDiff
+            ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+            root = {
+                document: {
+                    domain: currentDomain,
+                    location: undefined,
+                },
+            };
+
+            testFunc = getFunctionFromTemplate(
                 {
                 {
                     domainLockFunctionName: 'domainLockFunction',
                     domainLockFunctionName: 'domainLockFunction',
-                    diff: diff,
+                    domainsStringDiff,
                     domains: hiddenDomainsString,
                     domains: hiddenDomainsString,
-                    globalVariableTemplate: GlobalVariableTemplate1(),
+                    domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                    hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                    globalVariableTemplate: '',
                     singleCallControllerFunctionName
                     singleCallControllerFunctionName
                 },
                 },
                 singleCallControllerFunctionName,
                 singleCallControllerFunctionName,
-                getDocumentDomainTemplate(currentDomain)
+                thatThisTemplate
             );
             );
         });
         });
 
 
         it('should correctly run code inside template', () => {
         it('should correctly run code inside template', () => {
-            assert.doesNotThrow(testFunc);
+            assert.doesNotThrow(() => testFunc.apply(root));
+            assert.isUndefined(root.document.location);
         });
         });
     });
     });
 
 
     describe('Variant #4: current root domain matches with `domainsString`', () => {
     describe('Variant #4: current root domain matches with `domainsString`', () => {
         describe('Variant #1', () => {
         describe('Variant #1', () => {
             const domainsString: string = ['example.com'].join(';');
             const domainsString: string = ['example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'example.com';
             const currentDomain: string = 'example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isUndefined(root.document.location);
             });
             });
         });
         });
 
 
         describe('Variant #2', () => {
         describe('Variant #2', () => {
             const domainsString: string = ['example.com', '.example.com'].join(';');
             const domainsString: string = ['example.com', '.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'subdomain.example.com';
             const currentDomain: string = 'subdomain.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isUndefined(root.document.location);
             });
             });
         });
         });
 
 
         describe('Variant #3', () => {
         describe('Variant #3', () => {
             const domainsString: string = ['.example.com', 'example.com'].join(';');
             const domainsString: string = ['.example.com', 'example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'subdomain.example.com';
             const currentDomain: string = 'subdomain.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isUndefined(root.document.location);
             });
             });
         });
         });
 
 
         describe('Variant #4', () => {
         describe('Variant #4', () => {
             const domainsString: string = ['sub1.example.com', 'sub2.example.com'].join(';');
             const domainsString: string = ['sub1.example.com', 'sub2.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'sub1.example.com';
             const currentDomain: string = 'sub1.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isUndefined(root.document.location);
             });
             });
         });
         });
     });
     });
 
 
     describe('Variant #5: current domain matches with base domain of `domainsString` item', () => {
     describe('Variant #5: current domain matches with base domain of `domainsString` item', () => {
         const domainsString: string = ['www.test.com', '.example.com'].join(';');
         const domainsString: string = ['www.test.com', '.example.com'].join(';');
+        const domainLockRedirectUrl: string = 'about:blank';
         const currentDomain: string = 'subdomain.example.com';
         const currentDomain: string = 'subdomain.example.com';
 
 
-        let testFunc: () => void;
+        let testFunc: Function;
+        let root: any;
 
 
         before(() => {
         before(() => {
             const [
             const [
                 hiddenDomainsString,
                 hiddenDomainsString,
-                diff
+                domainsStringDiff
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
             ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-            testFunc = () => getFunctionFromTemplate(
+            const [
+                hiddenDomainLockRedirectUrl,
+                domainLockRedirectUrlDiff
+            ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+            root = {
+                document: {
+                    domain: currentDomain,
+                    location: undefined,
+                },
+            };
+
+            testFunc = getFunctionFromTemplate(
                 {
                 {
                     domainLockFunctionName: 'domainLockFunction',
                     domainLockFunctionName: 'domainLockFunction',
-                    diff: diff,
+                    domainsStringDiff,
                     domains: hiddenDomainsString,
                     domains: hiddenDomainsString,
-                    globalVariableTemplate: GlobalVariableTemplate1(),
+                    domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                    hiddenDomainLockRedirectUrl,
+                    globalVariableTemplate: '',
                     singleCallControllerFunctionName
                     singleCallControllerFunctionName
                 },
                 },
                 singleCallControllerFunctionName,
                 singleCallControllerFunctionName,
-                getDocumentDomainTemplate(currentDomain)
+                thatThisTemplate
             );
             );
         });
         });
 
 
         it('should correctly run code inside template', () => {
         it('should correctly run code inside template', () => {
-            assert.doesNotThrow(testFunc);
+            assert.doesNotThrow(() => testFunc.apply(root));
+            assert.isUndefined(root.document.location);
         });
         });
     });
     });
 
 
     describe('Variant #6: current domain doesn\'t match with `domainsString`', () => {
     describe('Variant #6: current domain doesn\'t match with `domainsString`', () => {
         describe('Variant #1', () => {
         describe('Variant #1', () => {
             const domainsString: string = ['www.example.com'].join(';');
             const domainsString: string = ['www.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'www.test.com';
             const currentDomain: string = 'www.test.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
 
 
         describe('Variant #2', () => {
         describe('Variant #2', () => {
             const domainsString: string = ['sub1.test.com', 'sub2.test.com'].join(';');
             const domainsString: string = ['sub1.test.com', 'sub2.test.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'sub3.test.com';
             const currentDomain: string = 'sub3.test.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate({
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate({
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
 
 
         describe('Variant #3', () => {
         describe('Variant #3', () => {
             const domainsString: string = ['www.example.com', '.example.com', 'sub.test.com'].join(';');
             const domainsString: string = ['www.example.com', '.example.com', 'sub.test.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'www.test.com';
             const currentDomain: string = 'www.test.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
 
 
         describe('Variant #4', () => {
         describe('Variant #4', () => {
             const domainsString: string = ['.example.com'].join(';');
             const domainsString: string = ['.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'example1.com';
             const currentDomain: string = 'example1.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
 
 
-        describe('Variant #4', () => {
+        describe('Variant #5', () => {
             const domainsString: string = ['example.com'].join(';');
             const domainsString: string = ['example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentDomain: string = 'sub.example.com';
             const currentDomain: string = 'sub.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentDomain,
+                        location: undefined,
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainTemplate(currentDomain)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
     });
     });
@@ -496,61 +676,97 @@ describe('DomainLockTemplate', () => {
     describe('Variant #7: location.hostname', () => {
     describe('Variant #7: location.hostname', () => {
         describe('Variant #1: current location.hostname matches with `domainsString`', () => {
         describe('Variant #1: current location.hostname matches with `domainsString`', () => {
             const domainsString: string = ['www.example.com'].join(';');
             const domainsString: string = ['www.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentHostName: string = 'www.example.com';
             const currentHostName: string = 'www.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        location: {
+                            hostname: currentHostName,
+                        },
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentLocationTemplate(currentHostName)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isObject(root.document.location);
             });
             });
         });
         });
 
 
         describe('Variant #2: current location.hostname doesn\'t match with `domainsString`', () => {
         describe('Variant #2: current location.hostname doesn\'t match with `domainsString`', () => {
             const domainsString: string = ['www.example.com'].join(';');
             const domainsString: string = ['www.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentHostName: string = 'www.test.com';
             const currentHostName: string = 'www.test.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        location: {
+                            hostname: currentHostName,
+                        },
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentLocationTemplate(currentHostName)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
     });
     });
@@ -558,61 +774,99 @@ describe('DomainLockTemplate', () => {
     describe('Variant #8: domain and location.hostname presented', () => {
     describe('Variant #8: domain and location.hostname presented', () => {
         describe('Variant #1: current domain matches with `domainsString`', () => {
         describe('Variant #1: current domain matches with `domainsString`', () => {
             const domainsString: string = ['www.example.com'].join(';');
             const domainsString: string = ['www.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentHostName: string = 'www.example.com';
             const currentHostName: string = 'www.example.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentHostName,
+                        location: {
+                            hostname: currentHostName,
+                        },
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainAndLocationTemplate(currentHostName)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
             it('should correctly run code inside template', () => {
             it('should correctly run code inside template', () => {
-                assert.doesNotThrow(testFunc);
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.isObject(root.document.location);
             });
             });
         });
         });
 
 
         describe('Variant #2: current domain doesn\'t match with `domainsString`', () => {
         describe('Variant #2: current domain doesn\'t match with `domainsString`', () => {
             const domainsString: string = ['www.example.com'].join(';');
             const domainsString: string = ['www.example.com'].join(';');
+            const domainLockRedirectUrl: string = 'about:blank';
             const currentHostName: string = 'www.test.com';
             const currentHostName: string = 'www.test.com';
 
 
-            let testFunc: () => void;
+            let testFunc: Function;
+            let root: any;
 
 
             before(() => {
             before(() => {
                 const [
                 const [
                     hiddenDomainsString,
                     hiddenDomainsString,
-                    diff
+                    domainsStringDiff
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
                 ] = cryptUtils.hideString(domainsString, domainsString.length * 3);
 
 
-                testFunc = () => getFunctionFromTemplate(
+                const [
+                    hiddenDomainLockRedirectUrl,
+                    domainLockRedirectUrlDiff
+                ] = cryptUtils.hideString(domainLockRedirectUrl, domainLockRedirectUrl.length * 3);
+
+                root = {
+                    document: {
+                        domain: currentHostName,
+                        location: {
+                            hostname: currentHostName,
+                        },
+                    },
+                };
+
+                testFunc = getFunctionFromTemplate(
                     {
                     {
                         domainLockFunctionName: 'domainLockFunction',
                         domainLockFunctionName: 'domainLockFunction',
-                        diff: diff,
+                        domainsStringDiff,
                         domains: hiddenDomainsString,
                         domains: hiddenDomainsString,
-                        globalVariableTemplate: GlobalVariableTemplate1(),
+                        domainLockRedirectUrlDiff: domainLockRedirectUrlDiff,
+                        hiddenDomainLockRedirectUrl: hiddenDomainLockRedirectUrl,
+                        globalVariableTemplate: '',
                         singleCallControllerFunctionName
                         singleCallControllerFunctionName
                     },
                     },
                     singleCallControllerFunctionName,
                     singleCallControllerFunctionName,
-                    getDocumentDomainAndLocationTemplate(currentHostName)
+                    thatThisTemplate
                 );
                 );
             });
             });
 
 
-            it('should throw an error', () => {
-                assert.throws(testFunc);
+            it('should change document.location', () => {
+                assert.doesNotThrow(() => testFunc.apply(root));
+                assert.equal(root.document.location, domainLockRedirectUrl);
             });
             });
         });
         });
     });
     });

+ 12 - 10
test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

@@ -1140,14 +1140,14 @@ describe('JavaScriptObfuscator', () => {
 
 
             const samplesCount: number = 30;
             const samplesCount: number = 30;
 
 
-            let areCollisionsExists: boolean = false;
-            let obfuscateFunc: (identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => string;
+            let collisionError: string | null = null;
+            let obfuscateFunc: (identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => IObfuscationResult;
 
 
             before(() => {
             before(() => {
                 const code: string = readFileAsString(__dirname + '/fixtures/custom-nodes-identifier-names-collision.js');
                 const code: string = readFileAsString(__dirname + '/fixtures/custom-nodes-identifier-names-collision.js');
 
 
                 obfuscateFunc = (identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => {
                 obfuscateFunc = (identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => {
-                    const obfuscatedCode = JavaScriptObfuscator.obfuscate(
+                    return JavaScriptObfuscator.obfuscate(
                         code,
                         code,
                         {
                         {
                             identifierNamesGenerator,
                             identifierNamesGenerator,
@@ -1156,9 +1156,7 @@ describe('JavaScriptObfuscator', () => {
                             identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
                             identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
                             stringArray: true
                             stringArray: true
                         }
                         }
-                    ).getObfuscatedCode();
-
-                    return obfuscatedCode;
+                    );
                 };
                 };
 
 
 
 
@@ -1167,10 +1165,14 @@ describe('JavaScriptObfuscator', () => {
                     IdentifierNamesGenerator.MangledIdentifierNamesGenerator
                     IdentifierNamesGenerator.MangledIdentifierNamesGenerator
                 ].forEach((identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => {
                 ].forEach((identifierNamesGenerator: TTypeFromEnum<typeof IdentifierNamesGenerator>) => {
                     for (let i = 0; i < samplesCount; i++) {
                     for (let i = 0; i < samplesCount; i++) {
+                        const obfuscationResult = obfuscateFunc(identifierNamesGenerator);
+                        const obfuscatedCode = obfuscationResult.getObfuscatedCode();
+                        const seed = obfuscationResult.getOptions().seed;
+
                         try {
                         try {
-                            eval(obfuscateFunc(identifierNamesGenerator));
-                        } catch {
-                            areCollisionsExists = true;
+                            eval(obfuscatedCode);
+                        } catch (error) {
+                            collisionError = `Seed: ${seed}. Error: ${error.message}`;
                             break;
                             break;
                         }
                         }
                     }
                     }
@@ -1178,7 +1180,7 @@ describe('JavaScriptObfuscator', () => {
             });
             });
 
 
             it('It does not create identifier names collision', () => {
             it('It does not create identifier names collision', () => {
-                assert.equal(areCollisionsExists, false);
+                assert.isNull(collisionError);
             });
             });
         });
         });
 
 

+ 46 - 0
test/functional-tests/options/OptionsNormalizer.spec.ts

@@ -159,6 +159,52 @@ describe('OptionsNormalizer', () => {
             });
             });
         });
         });
 
 
+        describe('domainLockRedirectUrlRule', () => {
+            describe('Variant #1: `domainLock` option is set', () => {
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        domainLock: [
+                            'localhost'
+                        ],
+                        domainLockRedirectUrl: 'https://example.com'
+                    });
+
+                    expectedOptionsPreset = {
+                        ...getDefaultOptions(),
+                        domainLock: [
+                            'localhost'
+                        ],
+                        domainLockRedirectUrl: 'https://example.com'
+                    };
+                });
+
+                it('should not normalize options preset', () => {
+                    assert.deepEqual(optionsPreset, expectedOptionsPreset);
+                });
+            });
+
+            describe('Variant #2 `domainLock` option is not set', () => {
+                before(() => {
+                    optionsPreset = getNormalizedOptions({
+                        ...getDefaultOptions(),
+                        domainLock: [],
+                        domainLockRedirectUrl: 'https://example.com'
+                    });
+
+                    expectedOptionsPreset = {
+                        ...getDefaultOptions(),
+                        domainLock: [],
+                        domainLockRedirectUrl: 'about:blank'
+                    };
+                });
+
+                it('should normalize options preset', () => {
+                    assert.deepEqual(optionsPreset, expectedOptionsPreset);
+                });
+            });
+        });
+
         describe('domainLockRule', () => {
         describe('domainLockRule', () => {
             before(() => {
             before(() => {
                 optionsPreset = getNormalizedOptions({
                 optionsPreset = getNormalizedOptions({

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

@@ -0,0 +1,104 @@
+import { assert } from 'chai';
+
+import { JavaScriptObfuscator } from '../../../../src/JavaScriptObfuscatorFacade';
+
+import { NO_ADDITIONAL_NODES_PRESET } from '../../../../src/options/presets/NoCustomNodes';
+
+describe('`domainLockRedirectUrl` validation', () => {
+    describe('IsDomainLockRedirectUrl', () => {
+        describe('Variant #1: positive validation', () => {
+            describe('Variant #1: string with url containing protocol, host and some path', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLockRedirectUrl: 'https://example.com/path'
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #2: string with url containing host and some path', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLockRedirectUrl: 'example.com/path'
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #3: string with url containing host and some path', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLockRedirectUrl: '/path'
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+
+            describe('Variant #4: `about:blank` string', () => {
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLockRedirectUrl: 'about:blank'
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should pass validation', () => {
+                    assert.doesNotThrow(testFunc);
+                });
+            });
+        });
+
+        describe('Variant #2: negative validation', () => {
+            describe('Variant #1: some non-url string', () => {
+                const expectedError: string = 'must be an URL address';
+                let testFunc: () => string;
+
+                beforeEach(() => {
+                    testFunc = () => JavaScriptObfuscator.obfuscate(
+                        '',
+                        {
+                            ...NO_ADDITIONAL_NODES_PRESET,
+                            domainLockRedirectUrl: 'foo'
+                        }
+                    ).getObfuscatedCode();
+                });
+
+                it('should not pass validation', () => {
+                    assert.throws(testFunc, expectedError);
+                });
+            });
+        });
+    });
+});

+ 1 - 1
test/functional-tests/options/identifier-names-cache/Validation.spec.ts

@@ -233,7 +233,7 @@ describe('`identifierNamesCache` validation', () => {
                 });
                 });
             });
             });
 
 
-            describe('Variant #4: cache with nullable dictionary fields', () => {
+            describe('Variant #5: cache with nullable dictionary fields', () => {
                 let testFunc: () => string;
                 let testFunc: () => string;
 
 
                 beforeEach(() => {
                 beforeEach(() => {

+ 1 - 0
test/index.spec.ts

@@ -133,6 +133,7 @@ import './functional-tests/node-transformers/string-array-transformers/string-ar
 import './functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec';
 import './functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec';
 import './functional-tests/node-transformers/string-array-transformers/string-array-transformer/StringArrayTransformer.spec';
 import './functional-tests/node-transformers/string-array-transformers/string-array-transformer/StringArrayTransformer.spec';
 import './functional-tests/options/OptionsNormalizer.spec';
 import './functional-tests/options/OptionsNormalizer.spec';
+import './functional-tests/options/domain-lock-destination/Validation.spec';
 import './functional-tests/options/domain-lock/Validation.spec';
 import './functional-tests/options/domain-lock/Validation.spec';
 import './functional-tests/options/identifier-names-cache/Validation.spec';
 import './functional-tests/options/identifier-names-cache/Validation.spec';
 import './functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec';
 import './functional-tests/storages/string-array-transformers/string-array-storage/StringArrayStorage.spec';

+ 64 - 9
test/unit-tests/source-code/ObfuscationResult.spec.ts

@@ -4,15 +4,47 @@ import { ServiceIdentifiers } from '../../../src/container/ServiceIdentifiers';
 
 
 import { assert } from 'chai';
 import { assert } from 'chai';
 
 
+import { TInputOptions } from '../../../src/types/options/TInputOptions';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 import { TTypeFromEnum } from '../../../src/types/utils/TTypeFromEnum';
 
 
 import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
 import { IInversifyContainerFacade } from '../../../src/interfaces/container/IInversifyContainerFacade';
 import { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
 import { IObfuscationResult } from '../../../src/interfaces/source-code/IObfuscationResult';
+import { IOptions } from '../../../src/interfaces/options/IOptions';
 
 
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
 
 
+import { DEFAULT_PRESET } from '../../../src/options/presets/Default';
+
 import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
 import { InversifyContainerFacade } from '../../../src/container/InversifyContainerFacade';
 
 
+/**
+ * @param {string} rawObfuscatedCode
+ * @param {TInputOptions} options
+ * @returns {IObfuscationResult}
+ */
+function getObfuscationResult (
+    rawObfuscatedCode: string,
+    options: TInputOptions
+): IObfuscationResult {
+    const inversifyContainerFacade: IInversifyContainerFacade = new InversifyContainerFacade();
+
+    inversifyContainerFacade.load(
+        '',
+        '',
+        {
+            ...DEFAULT_PRESET,
+            ...options
+        }
+    );
+
+    const obfuscationResult: IObfuscationResult = inversifyContainerFacade
+        .get<IObfuscationResult>(ServiceIdentifiers.IObfuscationResult);
+
+    obfuscationResult.initialize(rawObfuscatedCode, '');
+
+    return obfuscationResult;
+}
+
 /**
 /**
  * @param rawObfuscatedCode
  * @param rawObfuscatedCode
  * @param sourceMap
  * @param sourceMap
@@ -20,7 +52,7 @@ import { InversifyContainerFacade } from '../../../src/container/InversifyContai
  * @param sourceMapFileName
  * @param sourceMapFileName
  * @param sourceMapMode
  * @param sourceMapMode
  */
  */
-function getObfuscationResult (
+function getSourceMapObfuscationResult (
     rawObfuscatedCode: string,
     rawObfuscatedCode: string,
     sourceMap: string,
     sourceMap: string,
     sourceMapBaseUrl: string,
     sourceMapBaseUrl: string,
@@ -33,10 +65,11 @@ function getObfuscationResult (
         '',
         '',
         '',
         '',
         {
         {
+            ...DEFAULT_PRESET,
             sourceMap: true,
             sourceMap: true,
-            sourceMapBaseUrl: sourceMapBaseUrl,
-            sourceMapFileName: sourceMapFileName,
-            sourceMapMode: sourceMapMode
+            sourceMapBaseUrl,
+            sourceMapFileName,
+            sourceMapMode
         }
         }
     );
     );
 
 
@@ -56,7 +89,7 @@ describe('ObfuscatedCode', () => {
         let obfuscationResult: IObfuscationResult;
         let obfuscationResult: IObfuscationResult;
 
 
         before(() => {
         before(() => {
-            obfuscationResult = getObfuscationResult(
+            obfuscationResult = getSourceMapObfuscationResult(
                 expectedObfuscatedCode,
                 expectedObfuscatedCode,
                 sourceMap,
                 sourceMap,
                 '',
                 '',
@@ -75,7 +108,7 @@ describe('ObfuscatedCode', () => {
 
 
         describe('source map doest\'t exist', () => {
         describe('source map doest\'t exist', () => {
             before(() => {
             before(() => {
-                obfuscatedCode = getObfuscationResult(
+                obfuscatedCode = getSourceMapObfuscationResult(
                     expectedObfuscatedCode,
                     expectedObfuscatedCode,
                     '',
                     '',
                     '',
                     '',
@@ -93,7 +126,7 @@ describe('ObfuscatedCode', () => {
             const regExp: RegExp = /data:application\/json;base64,dGVzdA==/;
             const regExp: RegExp = /data:application\/json;base64,dGVzdA==/;
 
 
             before(() => {
             before(() => {
-                obfuscatedCode = getObfuscationResult(
+                obfuscatedCode = getSourceMapObfuscationResult(
                     expectedObfuscatedCode,
                     expectedObfuscatedCode,
                     sourceMap,
                     sourceMap,
                     '',
                     '',
@@ -111,7 +144,7 @@ describe('ObfuscatedCode', () => {
             const regExp: RegExp = /sourceMappingURL=http:\/\/example\.com\/output\.js\.map/;
             const regExp: RegExp = /sourceMappingURL=http:\/\/example\.com\/output\.js\.map/;
 
 
             before(() => {
             before(() => {
-                obfuscatedCode = getObfuscationResult(
+                obfuscatedCode = getSourceMapObfuscationResult(
                     expectedObfuscatedCode,
                     expectedObfuscatedCode,
                     sourceMap,
                     sourceMap,
                     'http://example.com',
                     'http://example.com',
@@ -127,7 +160,7 @@ describe('ObfuscatedCode', () => {
 
 
         describe('source map mode is `separate`, `sourceMapUrl` is not set', () => {
         describe('source map mode is `separate`, `sourceMapUrl` is not set', () => {
             before(() => {
             before(() => {
-                obfuscatedCode = getObfuscationResult(
+                obfuscatedCode = getSourceMapObfuscationResult(
                     expectedObfuscatedCode,
                     expectedObfuscatedCode,
                     sourceMap,
                     sourceMap,
                     '',
                     '',
@@ -141,4 +174,26 @@ describe('ObfuscatedCode', () => {
             });
             });
         });
         });
     });
     });
+
+    describe('getOptions', () => {
+        const seed: number = 1234;
+        const expectedOptions: IOptions = {
+            ...DEFAULT_PRESET,
+            seed
+        } as IOptions;
+        let options: IOptions;
+
+        before(() => {
+            options = getObfuscationResult(
+                expectedObfuscatedCode,
+                {
+                    seed
+                }
+            ).getOptions();
+        });
+
+        it('should return options object', () => {
+            assert.deepEqual(options, expectedOptions);
+        });
+    });
 });
 });

+ 15 - 27
yarn.lock

@@ -693,10 +693,10 @@
   resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz"
   resolved "https://registry.npmjs.org/@types/node/-/node-13.9.3.tgz"
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
   integrity sha512-01s+ac4qerwd6RHD+mVbOEsraDHSgUaefQlEdBbUolnQFjKwCr7luvAlEwW1RFojh67u0z4OUTjPn9LEl4zIkA==
 
 
-"@types/[email protected].1":
-  version "15.12.1"
-  resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.1.tgz#9b60797dee1895383a725f828a869c86c6caa5c2"
-  integrity sha512-zyxJM8I1c9q5sRMtVF+zdd13Jt6RU4r4qfhTd7lQubyThvLfx6yYekWSQjGCGV2Tkecgxnlpl/DNlb6Hg+dmEw==
+"@types/[email protected].2":
+  version "15.12.2"
+  resolved "https://registry.yarnpkg.com/@types/node/-/node-15.12.2.tgz#1f2b42c4be7156ff4a6f914b2fb03d05fa84e38d"
+  integrity sha512-zjQ69G564OCIWIOHSXyQEEDpdpGl+G348RAKY0XXy9Z5kU9Vzv1GMNnkar/ZJ8dzXB3COzD9Mo9NtRZ4xfgUww==
 
 
 "@types/normalize-package-data@^2.4.0":
 "@types/normalize-package-data@^2.4.0":
   version "2.4.0"
   version "2.4.0"
@@ -1629,7 +1629,7 @@ debug@^4.0.1, debug@^4.1.0, debug@^4.1.1:
 
 
 decamelize@^1.2.0:
 decamelize@^1.2.0:
   version "1.2.0"
   version "1.2.0"
-  resolved "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz"
+  resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290"
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
   integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=
 
 
 decamelize@^4.0.0:
 decamelize@^4.0.0:
@@ -2355,14 +2355,7 @@ get-stream@^6.0.0:
   resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz"
   resolved "https://registry.npmjs.org/get-stream/-/get-stream-6.0.0.tgz"
   integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==
   integrity sha512-A1B3Bh1UmL0bidM/YX2NsCOTnGJePL9rO/M+Mw3m9f2gUpfokS0hi5Eah0WSUEWZdZhIZtMjkIYS7mDfOqNHbg==
 
 
-glob-parent@^5.1.0, glob-parent@~5.1.0:
-  version "5.1.1"
-  resolved "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz"
-  integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==
-  dependencies:
-    is-glob "^4.0.1"
-
-glob-parent@^5.1.2:
+glob-parent@^5.1.0, glob-parent@^5.1.2, glob-parent@~5.1.0:
   version "5.1.2"
   version "5.1.2"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
   resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
   integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
@@ -2643,7 +2636,7 @@ is-date-object@^1.0.1:
 
 
 is-extglob@^2.1.1:
 is-extglob@^2.1.1:
   version "2.1.1"
   version "2.1.1"
-  resolved "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz"
+  resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
 
 
 is-fullwidth-code-point@^2.0.0:
 is-fullwidth-code-point@^2.0.0:
@@ -2663,7 +2656,7 @@ is-generator-function@^1.0.7:
 
 
 is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
 is-glob@^4.0.0, is-glob@^4.0.1, is-glob@~4.0.1:
   version "4.0.1"
   version "4.0.1"
-  resolved "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz"
+  resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc"
   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
   integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==
   dependencies:
   dependencies:
     is-extglob "^2.1.1"
     is-extglob "^2.1.1"
@@ -3067,12 +3060,7 @@ lodash.truncate@^4.4.2:
   resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
   resolved "https://registry.yarnpkg.com/lodash.truncate/-/lodash.truncate-4.4.2.tgz#5a350da0b1113b837ecfffd5812cbe58d6eae193"
   integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
   integrity sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=
 
 
-lodash@^4.17.13, lodash@^4.17.19:
-  version "4.17.19"
-  resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.19.tgz"
-  integrity sha512-JNvd8XER9GQX0v2qJgsaN/mzFCNA5BRe/j8JN9d+tWyGLSodKQHKFicdwNYzWwI3wjRnaKPsGj1XkBjx/F96DQ==
-
-lodash@^4.17.21:
+lodash@^4.17.13, lodash@^4.17.19, lodash@^4.17.21:
   version "4.17.21"
   version "4.17.21"
   resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
   resolved "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
   integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
@@ -4737,9 +4725,9 @@ write-file-atomic@^3.0.0:
     typedarray-to-buffer "^3.1.5"
     typedarray-to-buffer "^3.1.5"
 
 
 y18n@^4.0.0:
 y18n@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.npmjs.org/y18n/-/y18n-4.0.0.tgz"
-  integrity sha512-r9S/ZyXu/Xu9q1tYlpsLIsa3EeLXXk0VwlxqTcFRfg9EhMW+17kbt9G0NrgCmhGb5vT2hyhJZLfDGx+7+5Uj/w==
+  version "4.0.3"
+  resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf"
+  integrity sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ==
 
 
 y18n@^5.0.5:
 y18n@^5.0.5:
   version "5.0.5"
   version "5.0.5"
@@ -4767,9 +4755,9 @@ [email protected]:
   integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
   integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==
 
 
 yargs-parser@^18.1.1:
 yargs-parser@^18.1.1:
-  version "18.1.1"
-  resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-18.1.1.tgz"
-  integrity sha512-KRHEsOM16IX7XuLnMOqImcPNbLVXMNHYAoFc3BKR8Ortl5gzDbtXvvEoGx9imk5E+X1VeNKNlcHr8B8vi+7ipA==
+  version "18.1.3"
+  resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-18.1.3.tgz#be68c4975c6b2abf469236b0c870362fab09a7b0"
+  integrity sha512-o50j0JeToy/4K6OZcaQmW6lyXXKhq7csREXcDwk2omFPJEwUNOVtJKvmDr9EI1fAJZUyZcRF7kxGBWmRXudrCQ==
   dependencies:
   dependencies:
     camelcase "^5.0.0"
     camelcase "^5.0.0"
     decamelize "^1.2.0"
     decamelize "^1.2.0"

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