Browse Source

A file path will be displayed on obfuscation error

sanex3339 5 years ago
parent
commit
d0285aa085

+ 1 - 0
CHANGELOG.md

@@ -5,6 +5,7 @@ v0.24.0
 * **Internal refactoring:** completely new mechanism to rename variable names
 * Dynamic import and `import.meta` support. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/505
 * Now usage of some browser-related options with `target: 'node'` will cause a validation error
+* **CLI:** a file path will be displayed on obfuscation error. Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/513
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/512
 * Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/496
 * **Internal:** switched from `awesome-typescript-loader` on `ts-loader`

File diff suppressed because it is too large
+ 0 - 0
dist/index.browser.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.cli.js


File diff suppressed because it is too large
+ 0 - 0
dist/index.js


+ 26 - 12
src/ASTParserFacade.ts

@@ -1,9 +1,10 @@
 import * as acorn from 'acorn';
 import acornImportMeta from 'acorn-import-meta';
 import * as ESTree from 'estree';
-
 import chalk, { Chalk } from 'chalk';
 
+import { IASTParserFacadeInputData } from './interfaces/IASTParserFacadeInputData';
+
 /**
  * Facade over AST parser `acorn`
  */
@@ -27,23 +28,23 @@ export class ASTParserFacade {
     ];
 
     /**
-     * @param {string} input
+     * @param {string} inputData
      * @param {Options} config
      * @returns {Program}
      */
-    public static parse (input: string, config: acorn.Options): ESTree.Program | never {
+    public static parse (inputData: IASTParserFacadeInputData, config: acorn.Options): ESTree.Program | never {
         const sourceTypeLength: number = ASTParserFacade.sourceTypes.length;
 
         for (let i: number = 0; i < sourceTypeLength; i++) {
             try {
-                return ASTParserFacade.parseType(input, config, ASTParserFacade.sourceTypes[i]);
+                return ASTParserFacade.parseType(inputData, config, ASTParserFacade.sourceTypes[i]);
             } catch (error) {
                 if (i < sourceTypeLength - 1) {
                     continue;
                 }
 
                 throw new Error(ASTParserFacade.processParsingError(
-                    input,
+                    inputData,
                     error.message,
                     error.loc
                 ));
@@ -54,16 +55,17 @@ export class ASTParserFacade {
     }
 
     /**
-     * @param {string} input
+     * @param {IASTParserFacadeInputData} inputData
      * @param {acorn.Options} inputConfig
      * @param {acorn.Options["sourceType"]} sourceType
      * @returns {Program}
      */
     private static parseType (
-        input: string,
+        inputData: IASTParserFacadeInputData,
         inputConfig: acorn.Options,
         sourceType: acorn.Options['sourceType']
     ): ESTree.Program {
+        const { sourceCode } = inputData;
         const comments: ESTree.Comment[] = [];
         const config: acorn.Options = {
             ...inputConfig,
@@ -74,7 +76,7 @@ export class ASTParserFacade {
         const program: ESTree.Program = (
             <any>acorn
                 .Parser.extend(acornImportMeta)
-                .parse(input, config)
+                .parse(sourceCode, config)
         );
 
         if (comments.length) {
@@ -85,16 +87,22 @@ export class ASTParserFacade {
     }
 
     /**
-     * @param {string} sourceCode
+     * @param {IASTParserFacadeInputData} inputData
      * @param {string} errorMessage
-     * @param {Position} position
+     * @param {Position | null} position
      * @returns {never}
      */
-    private static processParsingError (sourceCode: string, errorMessage: string, position: ESTree.Position | null): never {
+    private static processParsingError (
+        inputData: IASTParserFacadeInputData,
+        errorMessage: string,
+        position: ESTree.Position | null
+    ): never {
         if (!position || !position.line || !position.column) {
             throw new Error(errorMessage);
         }
 
+        const { sourceCode, inputFilePath } = inputData;
+
         const sourceCodeLines: string[] = sourceCode.split(/\r?\n/);
         const errorLine: string | undefined = sourceCodeLines[position.line - 1];
 
@@ -102,6 +110,10 @@ export class ASTParserFacade {
             throw new Error(errorMessage);
         }
 
+        const formattedInputFilePath: string = inputFilePath
+            ? `${inputFilePath}, `
+            : '';
+
         const startErrorIndex: number = Math.max(0, position.column - ASTParserFacade.nearestSymbolsCount);
         const endErrorIndex: number = Math.min(errorLine.length, position.column + ASTParserFacade.nearestSymbolsCount);
 
@@ -110,6 +122,8 @@ export class ASTParserFacade {
             errorLine.substring(startErrorIndex, endErrorIndex).replace(/^\s+/, '')
         }...`;
 
-        throw new Error(`Line ${position.line}: ${errorMessage}\n${formattedPointer} ${formattedCodeSlice}`);
+        throw new Error(
+            `ERROR in ${formattedInputFilePath}line ${position.line}: ${errorMessage}\n${formattedPointer} ${formattedCodeSlice}`
+        );
     }
 }

+ 7 - 1
src/JavaScriptObfuscator.ts

@@ -7,6 +7,7 @@ import * as ESTree from 'estree';
 
 import { TObfuscatedCodeFactory } from './types/container/source-code/TObfuscatedCodeFactory';
 
+import { IASTParserFacadeInputData } from './interfaces/IASTParserFacadeInputData';
 import { IGeneratorOutput } from './interfaces/IGeneratorOutput';
 import { IJavaScriptObfuscator } from './interfaces/IJavaScriptObfsucator';
 import { ILogger } from './interfaces/logger/ILogger';
@@ -148,7 +149,12 @@ export class JavaScriptObfuscator implements IJavaScriptObfuscator {
      * @returns {Program}
      */
     private parseCode (sourceCode: string): ESTree.Program {
-        return ASTParserFacade.parse(sourceCode, JavaScriptObfuscator.parseOptions);
+        const inputData: IASTParserFacadeInputData = {
+            sourceCode,
+            inputFilePath: this.options.inputFilePath
+        };
+
+        return ASTParserFacade.parse(inputData, JavaScriptObfuscator.parseOptions);
     }
 
     /**

+ 3 - 1
src/cli/JavaScriptObfuscatorCLI.ts

@@ -174,12 +174,14 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
         const configFileLocation: string = configFilePath ? path.resolve(configFilePath, '.') : '';
         const configFileOptions: TInputOptions = configFileLocation ? CLIUtils.getUserConfig(configFileLocation) : {};
         const inputFileName: string = path.basename(this.inputPath);
+        const inputFilePath: string = this.inputPath;
 
         return {
             ...DEFAULT_PRESET,
             ...configFileOptions,
             ...inputCLIOptions,
-            inputFileName
+            inputFileName,
+            inputFilePath
         };
     }
 

+ 0 - 3
src/custom-nodes/control-flow-flattening-nodes/BinaryExpressionFunctionNode.ts

@@ -10,8 +10,6 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
 
-import { initializable } from '../../decorators/Initializable';
-
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
@@ -21,7 +19,6 @@ export class BinaryExpressionFunctionNode extends AbstractCustomNode {
     /**
      * @type {BinaryOperator}
      */
-    @initializable()
     private operator!: BinaryOperator;
 
     /**

+ 0 - 3
src/custom-nodes/control-flow-flattening-nodes/LogicalExpressionFunctionNode.ts

@@ -10,8 +10,6 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
 
-import { initializable } from '../../decorators/Initializable';
-
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
@@ -21,7 +19,6 @@ export class LogicalExpressionFunctionNode extends AbstractCustomNode {
     /**
      * @type {LogicalOperator}
      */
-    @initializable()
     private operator!: LogicalOperator;
 
     /**

+ 0 - 2
src/custom-nodes/control-flow-flattening-nodes/control-flow-storage-nodes/ExpressionWithOperatorControlFlowStorageCallNode.ts

@@ -33,13 +33,11 @@ export class ExpressionWithOperatorControlFlowStorageCallNode extends AbstractCu
     /**
      * @type {Expression}
      */
-    @initializable()
     private leftValue!: Expression;
 
     /**
      * @type {ESTree.Expression}
      */
-    @initializable()
     private rightValue!: Expression;
 
     /**

+ 0 - 4
src/custom-nodes/dead-code-injection-nodes/BlockStatementDeadCodeInjectionNode.ts

@@ -10,8 +10,6 @@ import { IOptions } from '../../interfaces/options/IOptions';
 import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
 import { ICustomNodeFormatter } from '../../interfaces/custom-nodes/ICustomNodeFormatter';
 
-import { initializable } from '../../decorators/Initializable';
-
 import { AbstractCustomNode } from '../AbstractCustomNode';
 import { NodeFactory } from '../../node/NodeFactory';
 import { NodeUtils } from '../../node/NodeUtils';
@@ -21,13 +19,11 @@ export class BlockStatementDeadCodeInjectionNode extends AbstractCustomNode {
     /**
      * @type {BlockStatement}
      */
-    @initializable()
     private blockStatementNode!: BlockStatement;
 
     /**
      * @type {BlockStatement}
      */
-    @initializable()
     private deadCodeInjectionRootAstHostNode!: BlockStatement;
 
     /**

+ 11 - 0
src/interfaces/IASTParserFacadeInputData.ts

@@ -0,0 +1,11 @@
+export interface IASTParserFacadeInputData {
+    /**
+     * @type {string}
+     */
+    sourceCode: string;
+
+    /**
+     * @type {string}
+     */
+    inputFilePath?: string;
+}

+ 1 - 0
src/interfaces/options/IOptions.ts

@@ -20,6 +20,7 @@ export interface IOptions {
     readonly identifiersDictionary: string[];
     readonly identifiersPrefix: string;
     readonly inputFileName: string;
+    readonly inputFilePath: string;
     readonly log: boolean;
     readonly renameGlobals: boolean;
     readonly reservedNames: string[];

+ 7 - 4
src/node/NodeUtils.ts

@@ -35,10 +35,13 @@ export class NodeUtils {
      * @returns {Statement[]}
      */
     public static convertCodeToStructure (code: string): ESTree.Statement[] {
-        const structure: ESTree.Program = ASTParserFacade.parse(code, {
-            ecmaVersion,
-            sourceType: 'script'
-        });
+        const structure: ESTree.Program = ASTParserFacade.parse(
+            { sourceCode: code },
+            {
+                ecmaVersion,
+                sourceType: 'script'
+            }
+        );
 
         estraverse.replace(structure, {
             enter: (node: ESTree.Node, parentNode: ESTree.Node | null): ESTree.Node => {

+ 6 - 0
src/options/Options.ts

@@ -144,6 +144,12 @@ export class Options implements IOptions {
     @IsString()
     public readonly inputFileName!: string;
 
+    /**
+     * @type {string}
+     */
+    @IsString()
+    public readonly inputFilePath!: string;
+
     /**
      * @type {boolean}
      */

+ 1 - 0
src/options/presets/Default.ts

@@ -20,6 +20,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
     identifiersPrefix: '',
     identifiersDictionary: [],
     inputFileName: '',
+    inputFilePath: '',
     log: false,
     renameGlobals: false,
     reservedNames: [],

+ 1 - 0
src/options/presets/NoCustomNodes.ts

@@ -19,6 +19,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
     identifiersPrefix: '',
     identifiersDictionary: [],
     inputFileName: '',
+    inputFilePath: '',
     log: false,
     renameGlobals: false,
     reservedNames: [],

+ 58 - 23
test/unit-tests/javascript-obfuscator/ASTParserFacade.spec.ts

@@ -1,4 +1,9 @@
 import { assert } from 'chai';
+
+import { IASTParserFacadeInputData } from '../../../src/interfaces/IASTParserFacadeInputData';
+
+import { ecmaVersion } from '../../../src/constants/EcmaVersion';
+
 import { ASTParserFacade } from '../../../src/ASTParserFacade';
 
 describe('ASTParserFacade', () => {
@@ -15,11 +20,13 @@ describe('ASTParserFacade', () => {
                 let testFunc: () => void;
 
                 before(() => {
-                    testFunc = () => ASTParserFacade.parse(sourceCode, { ecmaVersion: 9 });
+                    const inputData: IASTParserFacadeInputData = {sourceCode};
+
+                    testFunc = () => ASTParserFacade.parse(inputData, { ecmaVersion });
                 });
 
                 it('should output code preview when AST parser throws a parse error', () => {
-                    assert.throws(testFunc, /Line 3: Unexpected token \(3:28\)\n.*\.\.\.var baz = 3;,\.\.\./);
+                    assert.throws(testFunc, /ERROR in line 3: Unexpected token \(3:28\)\n.*\.\.\.var baz = 3;,\.\.\./);
                 });
             });
 
@@ -44,35 +51,63 @@ describe('ASTParserFacade', () => {
                 let testFunc: () => void;
 
                 before(() => {
-                    testFunc = () => ASTParserFacade.parse(sourceCode, { ecmaVersion: 9 });
+                    const inputData: IASTParserFacadeInputData = {sourceCode};
+
+                    testFunc = () => ASTParserFacade.parse(inputData, { ecmaVersion });
                 });
 
                 it('should output code preview when AST parser throws a parse error', () => {
-                    assert.throws(testFunc, /Line 13: Unexpected token \(13:28\)\n.*\.\.\.var baz = 3;,\.\.\./);
+                    assert.throws(testFunc, /ERROR in line 13: Unexpected token \(13:28\)\n.*\.\.\.var baz = 3;,\.\.\./);
                 });
             });
-        });
 
-        describe(`\`Unexpected token\` error code preview`, () => {
-            const sourceCode: string = `` +
-                `function bar () {
-                    var a = 1;
-                }
-                functin baz () {
-                    var a = 1;
-                }
-                function bark () {
-                    var a = 1;
-                }`;
-
-            let testFunc: () => void;
-
-            before(() => {
-                testFunc = () => ASTParserFacade.parse(sourceCode, { ecmaVersion: 9 });
+            describe('Variant #3: code with functions', () => {
+                const sourceCode: string = `` +
+                    `function bar () {
+                        var a = 1;
+                    }
+                    functin baz () {
+                        var a = 1;
+                    }
+                    function bark () {
+                        var a = 1;
+                    }`;
+
+                let testFunc: () => void;
+
+                before(() => {
+                    const inputData: IASTParserFacadeInputData = {sourceCode};
+
+                    testFunc = () => ASTParserFacade.parse(inputData, { ecmaVersion });
+                });
+
+                it('should output code preview when AST parser throws a parse error', () => {
+                    assert.throws(testFunc, /ERROR in line 4: Unexpected token \(4:28\)\n.*\.\.\.functin baz \(\) {\.\.\./);
+                });
             });
 
-            it('should output code preview when AST parser throws a parse error', () => {
-                assert.throws(testFunc, /Line 4: Unexpected token \(4:24\)\n.*\.\.\.functin baz \(\) {\.\.\./);
+            describe('Variant #4: input file path is set', () => {
+                const sourceCode: string = `` +
+                    `var foo = 1;
+                    var bar = 2;
+                    var baz = 3;,
+                    var bark = 4;
+                    var hawk = 5;`;
+
+                let testFunc: () => void;
+
+                before(() => {
+                    const inputData: IASTParserFacadeInputData = {
+                        sourceCode,
+                        inputFilePath: '/src/foo.js'
+                    };
+
+                    testFunc = () => ASTParserFacade.parse(inputData, { ecmaVersion });
+                });
+
+                it('should output code preview when AST parser throws a parse error', () => {
+                    assert.throws(testFunc, /ERROR in \/src\/foo\.js, line 3: Unexpected token \(3:32\)\n.*\.\.\.var baz = 3;,\.\.\./);
+                });
             });
         });
     });

Some files were not shown because too many files changed in this diff