Code coverage report for src/node-transformers/node-obfuscators/VariableDeclarationTransformer.ts

# All files / src/node-transformers/node-obfuscators VariableDeclarationTransformer.ts

100% Statements 42/42
100% Branches 12/12
100% Functions 3/3
100% Lines 37/37

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    1291x
    1x

    1x

    1x
    1x

    1x
    1x
    1x

    1x

    2445x

    2445x

    2445x

    17869x

    17869x
    1578x

    16291x
    16291x

    16291x
    16291x

    16291x

    16291x

    19870x
    19870x

    16291x

    16291x
    8409x

    379669x
    379669x

    8409x

    7882x

    7882x

    499556x
    124515x

    124515x
    21020x

    103495x

    7882x

     import { injectable, inject } from 'inversify';
    import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';

    import * as estraverse from 'estraverse';
    import * as ESTree from 'estree';

    import { TNodeWithBlockStatement } from '../../types/node/TNodeWithBlockStatement';

    import { IOptions } from '../../interfaces/options/IOptions';
    import { IObfuscatorReplacer } from '../../interfaces/node-transformers/IObfuscatorReplacer';
    import { IObfuscatorReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscatorReplacerWithStorage';

    import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscatorsReplacers';
    import { NodeType } from '../../enums/NodeType';

    import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
    import { Node } from '../../node/Node';
    import { NodeUtils } from '../../node/NodeUtils';

    /**
     * replaces:
     *     var variable = 1;
     *     variable++;
     *
     * on:
     *     var _0x12d45f = 1;
     *     _0x12d45f++;
     *
     */
    @injectable()
    export class VariableDeclarationObfuscator extends AbstractNodeTransformer {
        /**
         * @type {IObfuscatorReplacerWithStorage}
         */
        private readonly identifierReplacer: IObfuscatorReplacerWithStorage;

        /**
         * @type {Map<ESTree.Node, ESTree.Identifier[]>}
         */
        private readonly replaceableIdentifiers: Map <ESTree.Node, ESTree.Identifier[]> = new Map();

        /**
         * @param replacersFactory
         * @param options
         */
        constructor(
            @inject(ServiceIdentifiers.Factory__IObfuscatorReplacer) replacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
            @inject(ServiceIdentifiers.IOptions) options: IOptions
        ) {
            super(options);

            this.identifierReplacer = <IObfuscatorReplacerWithStorage>replacersFactory(NodeObfuscatorsReplacers.IdentifierReplacer);
        }

        /**
         * @param variableDeclarationNode
         * @param parentNode
         * @returns {ESTree.Node}
         */
        public transformNode (variableDeclarationNode: ESTree.VariableDeclaration, parentNode: ESTree.Node): ESTree.Node {
            const blockScopeOfVariableDeclarationNode: TNodeWithBlockStatement = NodeUtils
                .getBlockScopesOfNode(variableDeclarationNode)[0];

            if (blockScopeOfVariableDeclarationNode.type === NodeType.Program) {
                return variableDeclarationNode;
            }

            const nodeIdentifier: number = this.nodeIdentifier++;
            const scopeNode: ESTree.Node = variableDeclarationNode.kind === 'var'
                ? blockScopeOfVariableDeclarationNode
                : parentNode;

            this.storeVariableNames(variableDeclarationNode, nodeIdentifier);
            this.replaceVariableNames(scopeNode, nodeIdentifier);

            return variableDeclarationNode;
        }

        /**
         * @param variableDeclarationNode
         * @param nodeIdentifier
         */
        private storeVariableNames (variableDeclarationNode: ESTree.VariableDeclaration, nodeIdentifier: number): void {
            variableDeclarationNode.declarations
                .forEach((declarationNode: ESTree.VariableDeclarator) => {
                    NodeUtils.typedTraverse(declarationNode.id, NodeType.Identifier, {
                        enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
                    });
                });
        }

        /**
         * @param scopeNode
         * @param nodeIdentifier
         */
        private replaceVariableNames (scopeNode: ESTree.Node, nodeIdentifier: number): void {
            let replaceableIdentifiersForCurrentScope: ESTree.Identifier[];

            // check for cached identifiers for current scope node. If exist - loop through them.
            if (this.replaceableIdentifiers.has(scopeNode)) {
                replaceableIdentifiersForCurrentScope = <ESTree.Identifier[]>this.replaceableIdentifiers.get(scopeNode);

                for (const replaceableIdentifier of replaceableIdentifiersForCurrentScope) {
                    replaceableIdentifier.name = this.identifierReplacer.replace(replaceableIdentifier.name, nodeIdentifier);
                }

                return;
            }

            replaceableIdentifiersForCurrentScope = [];

            estraverse.replace(scopeNode, {
                enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
                    if (!node.obfuscated && Node.isReplaceableIdentifierNode(node, parentNode)) {
                        const newNodeName: string = this.identifierReplacer.replace(node.name, nodeIdentifier);

                        if (node.name !== newNodeName) {
                            node.name = newNodeName;
                        } else {
                            replaceableIdentifiersForCurrentScope.push(node);
                        }
                    }
                }
            });

            this.replaceableIdentifiers.set(scopeNode, replaceableIdentifiersForCurrentScope);
        }
    }