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

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

100% Statements 40/40
100% Branches 8/8
100% Functions 2/2
100% Lines 35/35

    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
    1211x
    1x

    1x

    1x
    1x

    1x
    1x
    1x

    1x

    1353x

    1353x

    1353x

    4230x
    4230x

    4230x
    11x

    4219x
    4219x

    4219x

    4219x
    4219x

    4219x

    4219x
    1091x

    36278x
    36278x

    1091x

    3128x

    3128x

    315818x
    91164x

    91164x
    5252x

    85912x

    3128x

     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:
     *     function foo () { //... };
     *     foo();
     *
     * on:
     *     function _0x12d45f () { //... };
     *     _0x12d45f();
     */
    @injectable()
    export class FunctionDeclarationObfuscator 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 nodeObfuscatorsReplacersFactory
         * @param options
         */
        constructor(
            @inject(ServiceIdentifiers.Factory__IObfuscatorReplacer) nodeObfuscatorsReplacersFactory: (replacer: NodeObfuscatorsReplacers) => IObfuscatorReplacer,
            @inject(ServiceIdentifiers.IOptions) options: IOptions
        ) {
            super(options);

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

        /**
         * @param functionDeclarationNode
         * @param parentNode
         * @returns {ESTree.Node}
         */
        public transformNode (functionDeclarationNode: ESTree.FunctionDeclaration, parentNode: ESTree.Node): ESTree.Node {
            const nodeIdentifier: number = this.nodeIdentifier++;
            const blockScopeOfFunctionDeclarationNode: TNodeWithBlockStatement = NodeUtils
                .getBlockScopesOfNode(functionDeclarationNode)[0];

            if (blockScopeOfFunctionDeclarationNode.type === NodeType.Program) {
                return functionDeclarationNode;
            }

            this.storeFunctionName(functionDeclarationNode, nodeIdentifier);
            this.replaceFunctionName(blockScopeOfFunctionDeclarationNode, nodeIdentifier);

            return functionDeclarationNode;
        }

        /**
         * @param functionDeclarationNode
         * @param nodeIdentifier
         */
        private storeFunctionName (functionDeclarationNode: ESTree.FunctionDeclaration, nodeIdentifier: number): void {
            NodeUtils.typedTraverse(functionDeclarationNode.id, NodeType.Identifier, {
                enter: (node: ESTree.Identifier) => this.identifierReplacer.storeNames(node.name, nodeIdentifier)
            });
        }

        /**
         * @param scopeNode
         * @param nodeIdentifier
         */
        private replaceFunctionName (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.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);
        }
    }