# All files / src/node-obfuscators NodeObfuscator.ts

91.3% Statements 42/46
86.49% Branches 32/37
50% Functions 1/2
91.3% Lines 42/46

    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
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176

    1x

    1x
    1x

    1x

    577x
    577x

    94x

    94x
    94x

    7032x

    257x

    257x

    257x

    257x

    30x

    155x

    155x
    3x

    152x

    162x

    162x
    1x

    162x

    162x
    49x

    113x

    49x

    49x

    49x
    49x
    49x
    49x

    49x
    25x

    24x
    24x

    49x

    49x
    44x

    44x

    44x

    5x

     import { ICustomNode } from '../interfaces/custom-nodes/ICustomNode';
    import { INodeObfuscator } from '../interfaces/INodeObfuscator';
    import { INode } from "../interfaces/nodes/INode";
    import { IOptions } from "../interfaces/IOptions";

    import { TUnicodeArrayCallsWrapper } from "../types/custom-nodes/TUnicodeArrayCallsWrapper";
    import { TUnicodeArrayNode } from "../types/custom-nodes/TUnicodeArrayNode";

    import { JSFuck } from "../enums/JSFuck";

    import { Nodes } from "../Nodes";
    import { Utils } from '../Utils';

    export abstract class NodeObfuscator implements INodeObfuscator {
        /**
         * @type Map <string, Node>
         */
        protected nodes: Map <string, ICustomNode>;

        /**
         * @type {IOptions}
         */
        protected options: IOptions;

        /**
         * @param nodes
         * @param options
         */
        constructor(nodes: Map <string, ICustomNode>, options: IOptions) {
            this.nodes = nodes;
            this.options = options;
        }

        /**
         * @param node
         * @param parentNode
         */
        public abstract obfuscateNode (node: INode, parentNode?: INode): void;

        /**
         * @param name
         * @returns {boolean}
         */
        protected isReservedName (name: string): boolean {
            return this.options.reservedNames
                .some((reservedName: string) => {
                    return new RegExp(reservedName, 'g').test(name);
                });
        }

        /**
         * Store all identifiers names as keys in given `namesMap` with random names as value.
         * Reserved names will be ignored.
         *
         * @param node
         * @param namesMap
         */
        protected storeIdentifiersNames (
            node: INode,
            namesMap: Map <string, string>
        ): void {
            Eif (Nodes.isIdentifierNode(node) && !this.isReservedName(node.name)) {
                namesMap.set(node.name, Utils.getRandomVariableName());
            }
        }

        /**
         * @param node
         * @param parentNode
         * @param namesMap
         */
        protected replaceIdentifiersWithRandomNames (
            node: INode,
            parentNode: INode,
            namesMap: Map <string, string>
        ): void {
            if (Nodes.isIdentifierNode(node) && namesMap.has(node.name)) {
                const parentNodeIsPropertyNode: boolean = (
                        Nodes.isPropertyNode(parentNode) &&
                        parentNode.key === node
                    ),
                    parentNodeIsMemberExpressionNode: boolean = (
                        Nodes.isMemberExpressionNode(parentNode) &&
                        parentNode.computed === false &&
                        parentNode.property === node
                    );

                Iif (parentNodeIsPropertyNode || parentNodeIsMemberExpressionNode) {
                    return;
                }

                node.name = <string>namesMap.get(node.name);
            }
        }

        /**
         * @param nodeValue
         * @returns {string}
         */
        protected replaceLiteralBooleanWithJSFuck (nodeValue: boolean): string {
            return nodeValue ? JSFuck.True : JSFuck.False;
        }

        /**
         * @param nodeValue
         * @returns {string}
         */
        protected replaceLiteralNumberWithHexadecimalValue (nodeValue: number): string {
            const prefix: string = '0x';

            if (!Utils.isInteger(nodeValue)) {
                return String(nodeValue);
            }

            return `${prefix}${Utils.decToHex(nodeValue)}`;
        }

        /**
         * @param nodeValue
         * @returns {string}
         */
        protected replaceLiteralValueWithUnicodeValue (nodeValue: string): string {
            let replaceWithUnicodeArrayFlag: boolean = Math.random() <= this.options.unicodeArrayThreshold;

            if (this.options.encodeUnicodeLiterals && replaceWithUnicodeArrayFlag) {
                nodeValue = Utils.btoa(nodeValue);
            }

            nodeValue = Utils.stringToUnicode(nodeValue);

            if (this.options.unicodeArray && replaceWithUnicodeArrayFlag) {
                return this.replaceLiteralValueWithUnicodeArrayCall(nodeValue);
            }

            return nodeValue;
        }

        /**
         * @param value
         * @returns {string}
         */
        protected replaceLiteralValueWithUnicodeArrayCall (value: string): string {
            let unicodeArrayNode: TUnicodeArrayNode = <TUnicodeArrayNode>this.nodes.get('unicodeArrayNode');

            Iif (!unicodeArrayNode) {
                throw new ReferenceError('`unicodeArrayNode` node is not found in Map with custom nodes.');
            }

            let unicodeArray: string[] = unicodeArrayNode.getNodeData(),
                valueIndex: number = unicodeArray.indexOf(value),
                literalValueCallIndex: number,
                hexadecimalIndex: string;

            if (valueIndex >= 0) {
                literalValueCallIndex = valueIndex;
            } else {
                literalValueCallIndex = unicodeArray.length;
                unicodeArrayNode.updateNodeData(value);
            }

            hexadecimalIndex = this.replaceLiteralNumberWithHexadecimalValue(literalValueCallIndex);

            if (this.options.wrapUnicodeArrayCalls) {
                let unicodeArrayCallsWrapper: TUnicodeArrayCallsWrapper = <TUnicodeArrayCallsWrapper>this.nodes.get('unicodeArrayCallsWrapper');

                Iif (!unicodeArrayCallsWrapper) {
                    throw new ReferenceError('`unicodeArrayCallsWrapper` node is not found in Map with custom nodes.');
                }

                return `${unicodeArrayCallsWrapper.getNodeIdentifier()}('${hexadecimalIndex}')`;
            }

            return `${unicodeArrayNode.getNodeIdentifier()}[${hexadecimalIndex}]`;
        }
    }