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

# All files / src/node-transformers/obfuscation-transformers 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

    6242x

    6242x

    6242x

    30803x

    30803x
    1780x

    29023x
    29023x

    29023x
    29023x

    29023x

    29023x

    33163x
    33163x

    29023x

    29023x
    14991x

    550335x
    550335x

    14991x

    14032x

    14032x

    783572x
    212386x

    212386x
    35449x

    176937x

    14032x

     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 { IObfuscationReplacer } from '../../interfaces/node-transformers/IObfuscationReplacer';
    import { IObfuscationReplacerWithStorage } from '../../interfaces/node-transformers/IObfuscationReplacerWithStorage';

    import { NodeObfuscatorsReplacers } from '../../enums/container/NodeObfuscationReplacers';
    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 VariableDeclarationTransformer extends AbstractNodeTransformer {
        /**
         * @type {IObfuscationReplacerWithStorage}
         */
        private readonly identifierReplacer: IObfuscationReplacerWithStorage;

        /**
         * @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) => IObfuscationReplacer,
            @inject(ServiceIdentifiers.IOptions) options: IOptions
        ) {
            super(options);

            this.identifierReplacer = <IObfuscationReplacerWithStorage>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);
        }
    }