summaryrefslogtreecommitdiff
path: root/src/node_modules/javascript-obfuscator/src/analyzers
diff options
context:
space:
mode:
authorMinteck <contact@minteck.org>2021-12-21 16:52:28 +0100
committerMinteck <contact@minteck.org>2021-12-21 16:52:28 +0100
commit46e43f4bde4a35785b4997b81e86cd19f046b69b (patch)
treec53c2f826f777f9d6b2d249dab556feb72a6c3a6 /src/node_modules/javascript-obfuscator/src/analyzers
downloadlangdetect-46e43f4bde4a35785b4997b81e86cd19f046b69b.tar.gz
langdetect-46e43f4bde4a35785b4997b81e86cd19f046b69b.tar.bz2
langdetect-46e43f4bde4a35785b4997b81e86cd19f046b69b.zip
Commit
Diffstat (limited to 'src/node_modules/javascript-obfuscator/src/analyzers')
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.ts170
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts16
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts59
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts70
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts177
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/number-numerical-expression-analyzer/NumberNumericalExpressionAnalyzer.ts130
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts61
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/scope-analyzer/ScopeAnalyzer.ts152
-rw-r--r--src/node_modules/javascript-obfuscator/src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts134
9 files changed, 969 insertions, 0 deletions
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.ts b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.ts
new file mode 100644
index 0000000..68f22ec
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/CallsGraphAnalyzer.ts
@@ -0,0 +1,170 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { TCalleeDataExtractorFactory } from '../../types/container/calls-graph-analyzer/TCalleeDataExtractorFactory';
+
+import { ICalleeData } from '../../interfaces/analyzers/calls-graph-analyzer/ICalleeData';
+import { ICallsGraphAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphAnalyzer';
+import { ICallsGraphData } from '../../interfaces/analyzers/calls-graph-analyzer/ICallsGraphData';
+
+import { CalleeDataExtractor } from '../../enums/analyzers/calls-graph-analyzer/CalleeDataExtractor';
+
+import { NodeGuards } from '../../node/NodeGuards';
+import { NodeStatementUtils } from '../../node/NodeStatementUtils';
+
+/**
+ * This class generates a data with a graph of functions calls
+ *
+ * For example:
+ *
+ * function Foo () {
+ * var baz = function () {
+ *
+ * }
+ *
+ * baz();
+ * }
+ *
+ * foo();
+ *
+ * Will generate a structure like:
+ *
+ * [
+ * {
+ * callee: FOO_FUNCTION_NODE
+ * name: 'Foo',
+ * trace: [
+ * {
+ * callee: BAZ_FUNCTION_NODE,
+ * name: 'baz,
+ * trace: []
+ * }
+ * ]
+ * }
+ * ]
+ */
+@injectable()
+export class CallsGraphAnalyzer implements ICallsGraphAnalyzer {
+ /**
+ * @type {CalleeDataExtractor[]}
+ */
+ private static readonly calleeDataExtractorsList: CalleeDataExtractor[] = [
+ CalleeDataExtractor.FunctionDeclarationCalleeDataExtractor,
+ CalleeDataExtractor.FunctionExpressionCalleeDataExtractor,
+ CalleeDataExtractor.ObjectExpressionCalleeDataExtractor
+ ];
+
+ /**
+ * @type {number}
+ */
+ private static readonly limitThresholdActivationLength: number = 25;
+
+ /**
+ * @type {number}
+ */
+ private static readonly limitThreshold: number = 0.002;
+
+ /**
+ * @type {TCalleeDataExtractorFactory}
+ */
+ private readonly calleeDataExtractorFactory: TCalleeDataExtractorFactory;
+
+ public constructor (
+ @inject(ServiceIdentifiers.Factory__ICalleeDataExtractor) calleeDataExtractorFactory: TCalleeDataExtractorFactory
+ ) {
+ this.calleeDataExtractorFactory = calleeDataExtractorFactory;
+ }
+
+ /**
+ * @param {number} blockScopeBodyLength
+ * @returns {number}
+ */
+ public static getLimitIndex (blockScopeBodyLength: number): number {
+ const lastIndex: number = blockScopeBodyLength - 1;
+ const limitThresholdActivationIndex: number = CallsGraphAnalyzer.limitThresholdActivationLength - 1;
+
+ let limitIndex: number = lastIndex;
+
+ if (lastIndex > limitThresholdActivationIndex) {
+ limitIndex = Math.round(
+ limitThresholdActivationIndex + (lastIndex * CallsGraphAnalyzer.limitThreshold)
+ );
+
+ if (limitIndex > lastIndex) {
+ limitIndex = lastIndex;
+ }
+ }
+
+ return limitIndex;
+ }
+
+ /**
+ * @param {Program} astTree
+ * @returns {ICallsGraphData[]}
+ */
+ public analyze (astTree: ESTree.Program): ICallsGraphData[] {
+ return this.analyzeRecursive(astTree.body);
+ }
+
+ /**
+ * @param {NodeGuards[]} blockScopeBody
+ * @returns {ICallsGraphData[]}
+ */
+ private analyzeRecursive (blockScopeBody: ESTree.Node[]): ICallsGraphData[] {
+ const limitIndex: number = CallsGraphAnalyzer.getLimitIndex(blockScopeBody.length);
+ const callsGraphData: ICallsGraphData[] = [];
+ const blockScopeBodyLength: number = blockScopeBody.length;
+
+ for (let index: number = 0; index < blockScopeBodyLength; index++) {
+ if (index > limitIndex) {
+ break;
+ }
+
+ const blockScopeBodyNode: ESTree.Node = blockScopeBody[index];
+
+ estraverse.traverse(blockScopeBodyNode, {
+ enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+ if (!NodeGuards.isCallExpressionNode(node)) {
+ return;
+ }
+
+ if (blockScopeBodyNode.parentNode !== NodeStatementUtils.getParentNodeWithStatements(node)) {
+ return estraverse.VisitorOption.Skip;
+ }
+
+ this.analyzeCallExpressionNode(callsGraphData, blockScopeBody, node);
+ }
+ });
+ }
+
+ return callsGraphData;
+ }
+
+ /**
+ * @param {ICallsGraphData[]} callsGraphData
+ * @param {NodeGuards[]} blockScopeBody
+ * @param {CallExpression} callExpressionNode
+ */
+ private analyzeCallExpressionNode (
+ callsGraphData: ICallsGraphData[],
+ blockScopeBody: ESTree.Node[],
+ callExpressionNode: ESTree.CallExpression
+ ): void {
+ CallsGraphAnalyzer.calleeDataExtractorsList.forEach((calleeDataExtractorName: CalleeDataExtractor) => {
+ const calleeData: ICalleeData | null = this.calleeDataExtractorFactory(calleeDataExtractorName)
+ .extract(blockScopeBody, callExpressionNode.callee);
+
+ if (!calleeData) {
+ return;
+ }
+
+ callsGraphData.push({
+ ...calleeData,
+ callsGraph: this.analyzeRecursive(calleeData.callee.body)
+ });
+ });
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts
new file mode 100644
index 0000000..34381f6
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/AbstractCalleeDataExtractor.ts
@@ -0,0 +1,16 @@
+import { injectable } from 'inversify';
+
+import * as ESTree from 'estree';
+
+import { ICalleeData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeData';
+import { ICalleeDataExtractor } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeDataExtractor';
+
+@injectable()
+export abstract class AbstractCalleeDataExtractor implements ICalleeDataExtractor {
+ /**
+ * @param {Node[]} blockScopeBody
+ * @param {Node} callee
+ * @returns {ICalleeData}
+ */
+ public abstract extract (blockScopeBody: ESTree.Node[], callee: ESTree.Node): ICalleeData | null;
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts
new file mode 100644
index 0000000..aeb89cf
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionDeclarationCalleeDataExtractor.ts
@@ -0,0 +1,59 @@
+import { injectable } from 'inversify';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { ICalleeData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeData';
+
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
+import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+
+@injectable()
+export class FunctionDeclarationCalleeDataExtractor extends AbstractCalleeDataExtractor {
+ /**
+ * @param {NodeGuards[]} blockScopeBody
+ * @param {Identifier} callee
+ * @returns {ICalleeData}
+ */
+ public extract (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier): ICalleeData | null {
+ if (!NodeGuards.isIdentifierNode(callee)) {
+ return null;
+ }
+
+ const calleeBlockStatement: ESTree.BlockStatement | null = this.getCalleeBlockStatement(
+ NodeStatementUtils.getParentNodeWithStatements(blockScopeBody[0]),
+ callee.name
+ );
+
+ if (!calleeBlockStatement) {
+ return null;
+ }
+
+ return {
+ callee: calleeBlockStatement,
+ name: callee.name
+ };
+ }
+
+ /**
+ * @param {NodeGuards} targetNode
+ * @param {string} name
+ * @returns {BlockStatement}
+ */
+ private getCalleeBlockStatement (targetNode: ESTree.Node, name: string): ESTree.BlockStatement | null {
+ let calleeBlockStatement: ESTree.BlockStatement | null = null;
+
+ estraverse.traverse(targetNode, {
+ enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+ if (NodeGuards.isFunctionDeclarationNode(node) && node.id.name === name) {
+ calleeBlockStatement = node.body;
+
+ return estraverse.VisitorOption.Break;
+ }
+ }
+ });
+
+ return calleeBlockStatement;
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts
new file mode 100644
index 0000000..42ac074
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/FunctionExpressionCalleeDataExtractor.ts
@@ -0,0 +1,70 @@
+import { injectable } from 'inversify';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { ICalleeData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeData';
+
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
+import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+
+@injectable()
+export class FunctionExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
+ /**
+ * @param {NodeGuards[]} blockScopeBody
+ * @param {Identifier} callee
+ * @returns {ICalleeData}
+ */
+ public extract (blockScopeBody: ESTree.Node[], callee: ESTree.Identifier | ESTree.FunctionExpression): ICalleeData | null {
+ let calleeName: string | null = null;
+ let calleeBlockStatement: ESTree.BlockStatement | null = null;
+
+ if (NodeGuards.isIdentifierNode(callee)) {
+ calleeName = callee.name;
+ calleeBlockStatement = this.getCalleeBlockStatement(
+ NodeStatementUtils.getParentNodeWithStatements(blockScopeBody[0]),
+ callee.name
+ );
+ } else if (NodeGuards.isFunctionExpressionNode(callee)) {
+ calleeName = null;
+ calleeBlockStatement = callee.body;
+ }
+
+ if (!calleeBlockStatement) {
+ return null;
+ }
+
+ return {
+ callee: calleeBlockStatement,
+ name: calleeName
+ };
+ }
+
+ /**
+ * @param {NodeGuards} targetNode
+ * @param {string} name
+ * @returns {BlockStatement}
+ */
+ private getCalleeBlockStatement (targetNode: ESTree.Node, name: string): ESTree.BlockStatement | null {
+ let calleeBlockStatement: ESTree.BlockStatement | null = null;
+
+ estraverse.traverse(targetNode, {
+ enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
+ if (
+ NodeGuards.isFunctionExpressionNode(node) &&
+ parentNode &&
+ NodeGuards.isVariableDeclaratorNode(parentNode) &&
+ NodeGuards.isIdentifierNode(parentNode.id) &&
+ parentNode.id.name === name
+ ) {
+ calleeBlockStatement = node.body;
+
+ return estraverse.VisitorOption.Break;
+ }
+ }
+ });
+
+ return calleeBlockStatement;
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts
new file mode 100644
index 0000000..6937589
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/calls-graph-analyzer/callee-data-extractors/ObjectExpressionCalleeDataExtractor.ts
@@ -0,0 +1,177 @@
+import { injectable } from 'inversify';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { TObjectMembersCallsChain } from '../../../types/analyzers/calls-graph-analyzer/TObjectMembersCallsChain';
+
+import { ICalleeData } from '../../../interfaces/analyzers/calls-graph-analyzer/ICalleeData';
+
+import { AbstractCalleeDataExtractor } from './AbstractCalleeDataExtractor';
+import { NodeGuards } from '../../../node/NodeGuards';
+import { NodeStatementUtils } from '../../../node/NodeStatementUtils';
+
+@injectable()
+export class ObjectExpressionCalleeDataExtractor extends AbstractCalleeDataExtractor {
+ /**
+ * @param {Property} propertyNode
+ * @param {string | number} nextItemInCallsChain
+ * @returns {boolean}
+ */
+ private static isValidTargetPropertyNode (propertyNode: ESTree.Property, nextItemInCallsChain: string | number): boolean {
+ if (!propertyNode.key) {
+ return false;
+ }
+
+ const isTargetPropertyNodeWithIdentifierKey: boolean =
+ NodeGuards.isIdentifierNode(propertyNode.key) && propertyNode.key.name === nextItemInCallsChain;
+ const isTargetPropertyNodeWithLiteralKey: boolean =
+ NodeGuards.isLiteralNode(propertyNode.key) &&
+ Boolean(propertyNode.key.value) &&
+ propertyNode.key.value === nextItemInCallsChain;
+
+ return isTargetPropertyNodeWithIdentifierKey || isTargetPropertyNodeWithLiteralKey;
+ }
+
+ /**
+ * @param {NodeGuards[]} blockScopeBody
+ * @param {MemberExpression} callee
+ * @returns {ICalleeData}
+ */
+ public extract (blockScopeBody: ESTree.Node[], callee: ESTree.MemberExpression): ICalleeData | null {
+ if (!NodeGuards.isMemberExpressionNode(callee)) {
+ return null;
+ }
+
+ const objectMembersCallsChain: TObjectMembersCallsChain = this.createObjectMembersCallsChain([], callee);
+
+ if (!objectMembersCallsChain.length) {
+ return null;
+ }
+
+ const functionExpressionName: string | number | null = objectMembersCallsChain[objectMembersCallsChain.length - 1];
+ const calleeBlockStatement: ESTree.BlockStatement | null = this.getCalleeBlockStatement(
+ NodeStatementUtils.getParentNodeWithStatements(blockScopeBody[0]),
+ objectMembersCallsChain
+ );
+
+ if (!calleeBlockStatement) {
+ return null;
+ }
+
+ return {
+ callee: calleeBlockStatement,
+ name: functionExpressionName
+ };
+ }
+
+ /**
+ * Creates array with MemberExpression calls chain.
+ *
+ * Example: object.foo.bar(); // ['object', 'foo', 'bar']
+ *
+ * @param {TObjectMembersCallsChain} currentChain
+ * @param {MemberExpression} memberExpression
+ * @returns {TObjectMembersCallsChain}
+ */
+ private createObjectMembersCallsChain (
+ currentChain: TObjectMembersCallsChain,
+ memberExpression: ESTree.MemberExpression
+ ): TObjectMembersCallsChain {
+ // first step: processing memberExpression `property` property
+ if (NodeGuards.isIdentifierNode(memberExpression.property) && !memberExpression.computed) {
+ currentChain.unshift(memberExpression.property.name);
+ } else if (
+ NodeGuards.isLiteralNode(memberExpression.property) &&
+ (
+ typeof memberExpression.property.value === 'string' ||
+ typeof memberExpression.property.value === 'number'
+ )
+ ) {
+ currentChain.unshift(memberExpression.property.value);
+ } else {
+ return currentChain;
+ }
+
+ // second step: processing memberExpression `object` property
+ if (NodeGuards.isMemberExpressionNode(memberExpression.object)) {
+ return this.createObjectMembersCallsChain(currentChain, memberExpression.object);
+ } else if (NodeGuards.isIdentifierNode(memberExpression.object)) {
+ currentChain.unshift(memberExpression.object.name);
+ }
+
+ return currentChain;
+ }
+
+ /**
+ * @param {NodeGuards} targetNode
+ * @param {TObjectMembersCallsChain} objectMembersCallsChain
+ * @returns {BlockStatement}
+ */
+ private getCalleeBlockStatement (
+ targetNode: ESTree.Node,
+ objectMembersCallsChain: TObjectMembersCallsChain
+ ): ESTree.BlockStatement | null {
+ const objectName: string | number | undefined = objectMembersCallsChain.shift();
+
+ if (!objectName) {
+ return null;
+ }
+
+ let calleeBlockStatement: ESTree.BlockStatement | null = null;
+
+ estraverse.traverse(targetNode, {
+ enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+ if (
+ NodeGuards.isVariableDeclaratorNode(node) &&
+ NodeGuards.isIdentifierNode(node.id) &&
+ node.init &&
+ NodeGuards.isObjectExpressionNode(node.init) &&
+ node.id.name === objectName
+ ) {
+ calleeBlockStatement = this.findCalleeBlockStatement(node.init.properties, objectMembersCallsChain);
+
+ return estraverse.VisitorOption.Break;
+ }
+ }
+ });
+
+ return calleeBlockStatement;
+ }
+
+ /**
+ * @param {Property[]} objectExpressionProperties
+ * @param {TObjectMembersCallsChain} objectMembersCallsChain
+ * @returns {BlockStatement}
+ */
+ private findCalleeBlockStatement (
+ objectExpressionProperties: (ESTree.Property | ESTree.SpreadElement)[],
+ objectMembersCallsChain: TObjectMembersCallsChain
+ ): ESTree.BlockStatement | null {
+ const nextItemInCallsChain: string | number | undefined = objectMembersCallsChain.shift();
+
+ if (!nextItemInCallsChain) {
+ return null;
+ }
+
+ for (const propertyNode of objectExpressionProperties) {
+ if (!NodeGuards.isPropertyNode(propertyNode)) {
+ continue;
+ }
+
+ if (!ObjectExpressionCalleeDataExtractor.isValidTargetPropertyNode(propertyNode, nextItemInCallsChain)) {
+ continue;
+ }
+
+ if (NodeGuards.isObjectExpressionNode(propertyNode.value)) {
+ return this.findCalleeBlockStatement(propertyNode.value.properties, objectMembersCallsChain);
+ }
+
+ if (NodeGuards.isFunctionExpressionNode(propertyNode.value)) {
+ return propertyNode.value.body;
+ }
+ }
+
+ return null;
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/number-numerical-expression-analyzer/NumberNumericalExpressionAnalyzer.ts b/src/node_modules/javascript-obfuscator/src/analyzers/number-numerical-expression-analyzer/NumberNumericalExpressionAnalyzer.ts
new file mode 100644
index 0000000..483f817
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/number-numerical-expression-analyzer/NumberNumericalExpressionAnalyzer.ts
@@ -0,0 +1,130 @@
+import { injectable, inject } from 'inversify';
+
+import { TNumberNumericalExpressionData } from '../../types/analyzers/number-numerical-expression-analyzer/TNumberNumericalExpressionData';
+
+import { INumberNumericalExpressionAnalyzer } from '../../interfaces/analyzers/number-numerical-expression-analyzer/INumberNumericalExpressionAnalyzer';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import { NumberUtils } from '../../utils/NumberUtils';
+
+/**
+ * Based on https://gist.github.com/da411d/0e59f79dcf4603cdabf0024a10eeb6fe
+ */
+@injectable()
+export class NumberNumericalExpressionAnalyzer implements INumberNumericalExpressionAnalyzer {
+ /**
+ * @type {number}
+ */
+ private static readonly additionalParts: number = 3;
+
+ /**
+ * @type {Map<number, number[]>}
+ */
+ private readonly numberFactorsMap: Map<number, number[]> = new Map();
+
+ /**
+ * @type {IRandomGenerator}
+ */
+ private readonly randomGenerator: IRandomGenerator;
+
+ /**
+ * @param {IRandomGenerator} randomGenerator
+ */
+ public constructor (
+ @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator
+ ) {
+ this.randomGenerator = randomGenerator;
+ }
+
+ /**
+ * @param {number} number
+ * @returns {TNumberNumericalExpressionData}
+ */
+ public analyze (number: number): TNumberNumericalExpressionData {
+ if (isNaN(number)) {
+ throw new Error('Given value is NaN');
+ }
+
+ if (NumberUtils.isUnsafeNumber(number)) {
+ return [number];
+ }
+
+ const additionParts: number[] = this.generateAdditionParts(number);
+
+ return additionParts.map((addition: number) => this.mixWithMultiplyParts(addition));
+ }
+
+ /**
+ * @param {number} number
+ * @returns {number[]}
+ */
+ private generateAdditionParts (number: number): number[] {
+ const additionParts = [];
+
+ const upperNumberLimit: number = Math.min(Math.abs(number * 2), Number.MAX_SAFE_INTEGER);
+
+ const from: number = Math.min(-10000, -upperNumberLimit);
+ const to: number = Math.max(10000, upperNumberLimit);
+
+ let temporarySum = 0;
+
+ for (let i = 0; i < NumberNumericalExpressionAnalyzer.additionalParts; i++) {
+ if (i < NumberNumericalExpressionAnalyzer.additionalParts - 1) {
+ // trailing parts
+
+ let addition: number = this.randomGenerator.getRandomInteger(from, to);
+ const isUnsafeCombination: boolean = NumberUtils.isUnsafeNumber(temporarySum + addition);
+
+ // we have to flip sign if total expression sum overflows over safe integer limits
+ if (isUnsafeCombination) {
+ addition = -addition;
+ }
+
+ additionParts.push(addition);
+ temporarySum += addition;
+ } else {
+ const combination: number = number - temporarySum;
+ const isUnsafeCombination: boolean = NumberUtils.isUnsafeNumber(combination);
+
+ // last part
+ if (isUnsafeCombination) {
+ additionParts.push(0 - temporarySum);
+ additionParts.push(number);
+ } else {
+ additionParts.push(combination);
+ }
+ }
+ }
+
+ return additionParts;
+ }
+
+ /**
+ * @param {number} number
+ * @returns {number | number[]}
+ */
+ private mixWithMultiplyParts (number: number): number | number[] {
+ const shouldMixWithMultiplyParts: boolean = this.randomGenerator.getMathRandom() > 0.5;
+
+ if (!shouldMixWithMultiplyParts || number === 0) {
+ return number;
+ }
+
+ let factors: number[] | null = this.numberFactorsMap.get(number) ?? null;
+
+ if (!factors) {
+ factors = NumberUtils.getFactors(number);
+ this.numberFactorsMap.set(number, factors);
+ }
+
+ if (!factors.length) {
+ return number;
+ }
+
+ const factor: number = factors[this.randomGenerator.getRandomInteger(0, factors.length - 1)];
+
+ return [factor, number / factor];
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts b/src/node_modules/javascript-obfuscator/src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts
new file mode 100644
index 0000000..f01f9f1
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/prevailing-kind-of-variables-analyzer/PrevailingKindOfVariablesAnalyzer.ts
@@ -0,0 +1,61 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { IArrayUtils } from '../../interfaces/utils/IArrayUtils';
+import { IPrevailingKindOfVariablesAnalyzer } from '../../interfaces/analyzers/calls-graph-analyzer/IPrevailingKindOfVariablesAnalyzer';
+
+import { NodeGuards } from '../../node/NodeGuards';
+
+@injectable()
+export class PrevailingKindOfVariablesAnalyzer implements IPrevailingKindOfVariablesAnalyzer {
+ /**
+ * @type {ESTree.VariableDeclaration['kind']}
+ */
+ private static readonly defaultKindOfVariables: ESTree.VariableDeclaration['kind'] = 'var';
+
+ /**
+ * @type {IArrayUtils}
+ */
+ private readonly arrayUtils: IArrayUtils;
+
+ /**
+ * @type {ESTree.VariableDeclaration['kind']}
+ */
+ private prevailingKindOfVariables: ESTree.VariableDeclaration['kind'] = PrevailingKindOfVariablesAnalyzer.defaultKindOfVariables;
+
+ public constructor (
+ @inject(ServiceIdentifiers.IArrayUtils) arrayUtils: IArrayUtils
+ ) {
+ this.arrayUtils = arrayUtils;
+ }
+
+ /**
+ * @param {Program} astTree
+ */
+ public analyze (astTree: ESTree.Program): void {
+ const variableKinds: ESTree.VariableDeclaration['kind'][] = [];
+
+ estraverse.traverse(astTree, {
+ enter: (node: ESTree.Node): estraverse.VisitorOption | void => {
+ if (!NodeGuards.isVariableDeclarationNode(node)) {
+ return;
+ }
+
+ variableKinds.push(node.kind);
+ }
+ });
+
+ this.prevailingKindOfVariables = this.arrayUtils.findMostOccurringElement(variableKinds)
+ ?? PrevailingKindOfVariablesAnalyzer.defaultKindOfVariables;
+ }
+
+ /**
+ * @returns {VariableDeclaration["kind"]}
+ */
+ public getPrevailingKind (): ESTree.VariableDeclaration['kind'] {
+ return this.prevailingKindOfVariables;
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/scope-analyzer/ScopeAnalyzer.ts b/src/node_modules/javascript-obfuscator/src/analyzers/scope-analyzer/ScopeAnalyzer.ts
new file mode 100644
index 0000000..7641e7a
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/scope-analyzer/ScopeAnalyzer.ts
@@ -0,0 +1,152 @@
+import { injectable, } from 'inversify';
+
+import * as eslintScope from 'eslint-scope';
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { IScopeAnalyzer } from '../../interfaces/analyzers/scope-analyzer/IScopeAnalyzer';
+
+import { ecmaVersion } from '../../constants/EcmaVersion';
+
+import { NodeGuards } from '../../node/NodeGuards';
+
+@injectable()
+export class ScopeAnalyzer implements IScopeAnalyzer {
+ /**
+ * @type {eslintScope.AnalysisOptions}
+ */
+ private static readonly eslintScopeOptions: eslintScope.AnalysisOptions = {
+ ecmaVersion,
+ optimistic: true
+ };
+
+ /**
+ * @type {acorn.Options['sourceType'][]}
+ */
+ private static readonly sourceTypes: acorn.Options['sourceType'][] = [
+ 'script',
+ 'module'
+ ];
+
+ /**
+ * @type {number}
+ */
+ private static readonly emptyRangeValue: number = 0;
+
+ /**
+ * @type {eslintScope.ScopeManager | null}
+ */
+ private scopeManager: eslintScope.ScopeManager | null = null;
+
+ /**
+ * `eslint-scope` reads `ranges` property of a nodes
+ * Should attach that property to the some custom nodes
+ *
+ * @param {Node} astTree
+ */
+ private static attachMissingRanges (astTree: ESTree.Node): void {
+ estraverse.replace(astTree, {
+ enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {
+ if (!node.range) {
+ node.range = [
+ parentNode?.range?.[0] ?? ScopeAnalyzer.emptyRangeValue,
+ parentNode?.range?.[1] ?? ScopeAnalyzer.emptyRangeValue
+ ];
+ }
+
+ return node;
+ }
+ });
+ }
+
+ /**
+ * @param {Node} node
+ * @returns {boolean}
+ */
+ private static isRootNode (node: ESTree.Node): boolean {
+ return NodeGuards.isProgramNode(node) || node.parentNode === node;
+ }
+
+ /**
+ * @param {Program} astTree
+ */
+ public analyze (astTree: ESTree.Node): void {
+ const sourceTypeLength: number = ScopeAnalyzer.sourceTypes.length;
+
+ ScopeAnalyzer.attachMissingRanges(astTree);
+
+ for (let i: number = 0; i < sourceTypeLength; i++) {
+ try {
+ this.scopeManager = eslintScope.analyze(astTree, {
+ ...ScopeAnalyzer.eslintScopeOptions,
+ sourceType: ScopeAnalyzer.sourceTypes[i]
+ });
+
+ return;
+ } catch (error) {
+ if (i < sourceTypeLength - 1) {
+ continue;
+ }
+
+ throw new Error(error);
+ }
+ }
+
+ throw new Error('Scope analyzing error');
+ }
+
+ /**
+ * @param {Node} node
+ * @returns {Scope}
+ */
+ public acquireScope (node: ESTree.Node): eslintScope.Scope {
+ if (!this.scopeManager) {
+ throw new Error('Scope manager is not defined');
+ }
+
+ const scope: eslintScope.Scope | null = this.scopeManager.acquire(
+ node,
+ ScopeAnalyzer.isRootNode(node)
+ );
+
+ if (!scope) {
+ throw new Error('Cannot acquire scope for node');
+ }
+
+ this.sanitizeScopes(scope);
+
+ return scope;
+ }
+
+ /**
+ * @param {Scope} scope
+ */
+ private sanitizeScopes (scope: eslintScope.Scope): void {
+ scope.childScopes.forEach((childScope: eslintScope.Scope) => {
+ // fix of class scopes
+ // trying to move class scope references to the parent scope
+ if (childScope.type === 'class' && childScope.upper) {
+ if (!childScope.variables.length) {
+ return;
+ }
+
+ // class name variable is always first
+ const classNameVariable: eslintScope.Variable = childScope.variables[0];
+
+ const upperVariable: eslintScope.Variable | undefined = childScope.upper.variables
+ .find((variable: eslintScope.Variable) => {
+ const isValidClassNameVariable: boolean = classNameVariable.defs
+ .some((definition: eslintScope.Definition) => definition.type === 'ClassName');
+
+ return isValidClassNameVariable && variable.name === classNameVariable.name;
+ });
+
+ upperVariable?.references.push(...childScope.variables[0].references);
+ }
+ });
+
+ for (const childScope of scope.childScopes) {
+ this.sanitizeScopes(childScope);
+ }
+ }
+}
diff --git a/src/node_modules/javascript-obfuscator/src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts b/src/node_modules/javascript-obfuscator/src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
new file mode 100644
index 0000000..3b850bd
--- /dev/null
+++ b/src/node_modules/javascript-obfuscator/src/analyzers/string-array-storage-analyzer/StringArrayStorageAnalyzer.ts
@@ -0,0 +1,134 @@
+import { inject, injectable, } from 'inversify';
+import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
+
+import * as estraverse from 'estraverse';
+import * as ESTree from 'estree';
+
+import { IOptions } from '../../interfaces/options/IOptions';
+import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
+import { IStringArrayStorage } from '../../interfaces/storages/string-array-transformers/IStringArrayStorage';
+import { IStringArrayStorageAnalyzer } from '../../interfaces/analyzers/string-array-storage-analyzer/IStringArrayStorageAnalyzer';
+import { IStringArrayStorageItemData } from '../../interfaces/storages/string-array-transformers/IStringArrayStorageItem';
+
+import { NodeGuards } from '../../node/NodeGuards';
+import { NodeLiteralUtils } from '../../node/NodeLiteralUtils';
+import { NodeMetadata } from '../../node/NodeMetadata';
+
+/**
+ * Adds values of literal nodes to the string array storage
+ */
+@injectable()
+export class StringArrayStorageAnalyzer implements IStringArrayStorageAnalyzer {
+ /**
+ * @type {number}
+ */
+ private static readonly minimumLengthForStringArray: number = 3;
+
+ /**
+ * @type {IOptions}
+ */
+ private readonly options: IOptions;
+
+ /**
+ * @type {randomGenerator}
+ */
+ private readonly randomGenerator: IRandomGenerator;
+
+ /**
+ * @type {IStringArrayStorage}
+ */
+ private readonly stringArrayStorage: IStringArrayStorage;
+
+ /**
+ * @type {Map<ESTree.Literal, IStringArrayStorageItemData>}
+ */
+ private readonly stringArrayStorageData: Map<ESTree.Literal, IStringArrayStorageItemData> = new Map();
+
+ /**
+ * @param {IStringArrayStorage} stringArrayStorage
+ * @param {IRandomGenerator} randomGenerator
+ * @param {IOptions} options
+ */
+ public constructor (
+ @inject(ServiceIdentifiers.IStringArrayStorage) stringArrayStorage: IStringArrayStorage,
+ @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
+ @inject(ServiceIdentifiers.IOptions) options: IOptions,
+ ) {
+ this.stringArrayStorage = stringArrayStorage;
+ this.randomGenerator = randomGenerator;
+ this.options = options;
+ }
+
+ /**
+ * @param {Program} astTree
+ */
+ public analyze (astTree: ESTree.Program): void {
+ if (!this.options.stringArray) {
+ return;
+ }
+
+ estraverse.traverse(astTree, {
+ enter: (node: ESTree.Node, parentNode: ESTree.Node | null): estraverse.VisitorOption | void => {
+ if (!parentNode) {
+ return;
+ }
+
+ if (NodeMetadata.isIgnoredNode(node)) {
+ return estraverse.VisitorOption.Skip;
+ }
+
+ if (!NodeGuards.isLiteralNode(node)) {
+ return;
+ }
+
+ this.analyzeLiteralNode(node, parentNode);
+ }
+ });
+ }
+
+ /**
+ * @param {Literal} literalNode
+ * @returns {IStringArrayStorageItemData | undefined}
+ */
+ public getItemDataForLiteralNode (literalNode: ESTree.Literal): IStringArrayStorageItemData | undefined {
+ return this.stringArrayStorageData.get(literalNode);
+ }
+
+ /**
+ * @param {Literal} literalNode
+ * @param {Node} parentNode
+ */
+ private analyzeLiteralNode (literalNode: ESTree.Literal, parentNode: ESTree.Node): void {
+ if (!NodeLiteralUtils.isStringLiteralNode(literalNode)) {
+ return;
+ }
+
+ if (NodeLiteralUtils.isProhibitedLiteralNode(literalNode, parentNode)) {
+ return;
+ }
+
+ if (!this.shouldAddValueToStringArray(literalNode)) {
+ return;
+ }
+
+ this.stringArrayStorageData.set(
+ literalNode,
+ this.stringArrayStorage.getOrThrow(literalNode.value)
+ );
+ }
+
+ /**
+ * @param {(SimpleLiteral & {value: string})} literalNode
+ * @returns {boolean}
+ */
+ private shouldAddValueToStringArray (literalNode: ESTree.Literal & {value: string}): boolean {
+ const isForceTransformNode: boolean = NodeMetadata.isForceTransformNode(literalNode);
+
+ if (isForceTransformNode) {
+ return true;
+ }
+
+ return literalNode.value.length >= StringArrayStorageAnalyzer.minimumLengthForStringArray
+ && this.randomGenerator.getMathRandom() <= this.options.stringArrayThreshold;
+ }
+}