• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

javascript-obfuscator / javascript-obfuscator / 19907815758

03 Dec 2025 08:27PM UTC coverage: 97.319%. Remained the same
19907815758

push

github

sanex3339
Adjust precommit hook

1770 of 1891 branches covered (93.6%)

Branch coverage included in aggregate %.

5671 of 5755 relevant lines covered (98.54%)

34102965.58 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.67
/src/node-transformers/preparing-transformers/EvalCallExpressionTransformer.ts
1
import { inject, injectable } from 'inversify';
6✔
2
import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
6✔
3

4
import * as ESTree from 'estree';
5

6
import { IOptions } from '../../interfaces/options/IOptions';
7
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
8
import { IVisitor } from '../../interfaces/node-transformers/IVisitor';
9

10
import { NodeTransformer } from '../../enums/node-transformers/NodeTransformer';
6✔
11
import { NodeTransformationStage } from '../../enums/node-transformers/NodeTransformationStage';
6✔
12

13
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
6✔
14
import { NodeFactory } from '../../node/NodeFactory';
6✔
15
import { NodeGuards } from '../../node/NodeGuards';
6✔
16
import { NodeMetadata } from '../../node/NodeMetadata';
6✔
17
import { NodeUtils } from '../../node/NodeUtils';
6✔
18
import { StringUtils } from '../../utils/StringUtils';
6✔
19

20
@injectable()
21
export class EvalCallExpressionTransformer extends AbstractNodeTransformer {
6✔
22
    /**
23
     * @type {NodeTransformer[]}
24
     */
25
    public override readonly runAfter: NodeTransformer[] = [
197,808✔
26
        NodeTransformer.EscapeSequenceTransformer,
27
        NodeTransformer.ParentificationTransformer,
28
        NodeTransformer.VariablePreserveTransformer
29
    ];
30

31
    /**
32
     * @param {IRandomGenerator} randomGenerator
33
     * @param {IOptions} options
34
     */
35
    public constructor(
36
        @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
37
        @inject(ServiceIdentifiers.IOptions) options: IOptions
38
    ) {
39
        super(randomGenerator, options);
197,808✔
40
    }
41

42
    /**
43
     * @param {Expression | SpreadElement} node
44
     * @returns {string | null}
45
     */
46
    private static extractEvalStringFromCallExpressionArgument(
47
        node: ESTree.Expression | ESTree.SpreadElement
48
    ): string | null {
49
        if (NodeGuards.isLiteralNode(node)) {
108✔
50
            return EvalCallExpressionTransformer.extractEvalStringFromLiteralNode(node);
66✔
51
        }
52

53
        if (NodeGuards.isTemplateLiteralNode(node)) {
42✔
54
            return EvalCallExpressionTransformer.extractEvalStringFromTemplateLiteralNode(node);
42✔
55
        }
56

57
        return null;
×
58
    }
59

60
    /**
61
     * @param {Literal} node
62
     * @returns {string | null}
63
     */
64
    private static extractEvalStringFromLiteralNode(node: ESTree.Literal): string | null {
65
        return typeof node.value === 'string' ? node.value : null;
66!
66
    }
67

68
    /**
69
     * @param {TemplateLiteral} node
70
     * @returns {string | null}
71
     */
72
    private static extractEvalStringFromTemplateLiteralNode(node: ESTree.TemplateLiteral): string | null {
73
        const quasis: ESTree.TemplateElement[] = node.quasis;
42✔
74
        const allowedQuasisLength: number = 1;
42✔
75

76
        if (quasis.length !== allowedQuasisLength || node.expressions.length) {
42✔
77
            return null;
36✔
78
        }
79

80
        return quasis[0].value.cooked ?? null;
6!
81
    }
82

83
    /**
84
     * @param {NodeTransformationStage} nodeTransformationStage
85
     * @returns {IVisitor | null}
86
     */
87
    public getVisitor(nodeTransformationStage: NodeTransformationStage): IVisitor | null {
88
        switch (nodeTransformationStage) {
1,810,386✔
89
            case NodeTransformationStage.Preparing:
1,810,386✔
90
                return {
395,352✔
91
                    enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
92
                        if (parentNode) {
16,440,042✔
93
                            return this.transformNode(node, parentNode);
16,440,042✔
94
                        }
95
                    }
96
                };
97

98
            case NodeTransformationStage.Finalizing:
99
                return {
395,352✔
100
                    leave: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node | undefined => {
101
                        if (parentNode) {
376,355,042✔
102
                            return this.restoreNode(node, parentNode);
376,355,042✔
103
                        }
104
                    }
105
                };
106

107
            default:
108
                return null;
1,019,682✔
109
        }
110
    }
111

112
    /**
113
     * @param {Node} node
114
     * @param {Node} parentNode
115
     * @returns {Node}
116
     */
117
    public transformNode(node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
118
        const isEvalCallExpressionNode =
119
            parentNode &&
16,440,042✔
120
            NodeGuards.isCallExpressionNode(node) &&
121
            NodeGuards.isIdentifierNode(node.callee) &&
122
            node.callee.name === 'eval';
123

124
        if (!isEvalCallExpressionNode) {
16,440,042✔
125
            return node;
16,439,934✔
126
        }
127

128
        const evalCallExpressionFirstArgument: ESTree.Expression | ESTree.SpreadElement | undefined = node.arguments[0];
108✔
129

130
        if (!evalCallExpressionFirstArgument) {
108!
131
            return node;
×
132
        }
133

134
        const evalString: string | null = EvalCallExpressionTransformer.extractEvalStringFromCallExpressionArgument(
108✔
135
            evalCallExpressionFirstArgument
136
        );
137

138
        if (!evalString) {
108✔
139
            return node;
36✔
140
        }
141

142
        let ast: ESTree.Statement[];
143

144
        // wrapping into try-catch to prevent parsing of incorrect `eval` string
145
        try {
72✔
146
            ast = NodeUtils.convertCodeToStructure(evalString);
72✔
147
        } catch {
148
            return node;
6✔
149
        }
150

151
        /**
152
         * we should wrap AST-tree into the parent function expression node (ast root host node).
153
         * This function expression node will help to correctly transform AST-tree.
154
         */
155
        const evalRootAstHostNode: ESTree.FunctionExpression = NodeFactory.functionExpressionNode(
66✔
156
            [],
157
            NodeFactory.blockStatementNode(ast)
158
        );
159

160
        NodeMetadata.set(evalRootAstHostNode, { evalHostNode: true });
66✔
161

162
        NodeUtils.parentizeAst(evalRootAstHostNode);
66✔
163
        NodeUtils.parentizeNode(evalRootAstHostNode, parentNode);
66✔
164

165
        return evalRootAstHostNode;
66✔
166
    }
167

168
    /**
169
     * @param {Node} node
170
     * @param {Node} parentNode
171
     * @returns {Node}
172
     */
173
    public restoreNode(node: ESTree.Node, parentNode: ESTree.Node): ESTree.Node {
174
        if (!this.isEvalRootAstHostNode(node)) {
376,355,042✔
175
            return node;
376,354,970✔
176
        }
177

178
        const targetAst: ESTree.Statement[] = node.body.body;
72✔
179
        const obfuscatedCode: string = NodeUtils.convertStructureToCode(targetAst);
72✔
180

181
        return NodeFactory.callExpressionNode(NodeFactory.identifierNode('eval'), [
72✔
182
            NodeFactory.literalNode(StringUtils.escapeJsString(obfuscatedCode))
183
        ]);
184
    }
185

186
    /**
187
     * @param {Node} node
188
     * @returns {boolean}
189
     */
190
    private isEvalRootAstHostNode(node: ESTree.Node): node is ESTree.FunctionExpression {
191
        return NodeMetadata.isEvalHostNode(node);
376,355,042✔
192
    }
193
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc