mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
source map WIP integration
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import * as esprima from 'esprima';
|
||||
import * as escodegen from 'escodegen';
|
||||
|
||||
import { IGeneratorOutput } from "./interfaces/IGeneratorOutput";
|
||||
import { INode } from './interfaces/nodes/INode';
|
||||
import { IObfuscator } from "./interfaces/IObfuscator";
|
||||
import { IOptions } from './interfaces/IOptions';
|
||||
@@ -11,13 +12,25 @@ import { IOptionsPreset } from "./interfaces/IOptionsPreset";
|
||||
import { JavaScriptObfuscatorCLI } from "./cli/JavaScriptObfuscatorCLI";
|
||||
import { Obfuscator } from "./Obfuscator";
|
||||
import { Options } from "./Options";
|
||||
import { SourceMapInjector } from "./SourceMapInjector";
|
||||
|
||||
export class JavaScriptObfuscator {
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
public static obfuscatedCode: string;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
public static sourceMap: string;
|
||||
|
||||
/**
|
||||
* @type {GenerateOptions}
|
||||
*/
|
||||
private static escodegenParams: escodegen.GenerateOptions = {
|
||||
verbatim: 'x-verbatim-property'
|
||||
verbatim: 'x-verbatim-property',
|
||||
sourceMapWithCode: true
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -25,13 +38,28 @@ export class JavaScriptObfuscator {
|
||||
* @param customOptions
|
||||
*/
|
||||
public static obfuscate (sourceCode: string, customOptions?: IOptionsPreset): string {
|
||||
let astTree: INode = esprima.parse(sourceCode),
|
||||
let astTree: INode = esprima.parse(sourceCode, {
|
||||
loc: true
|
||||
}),
|
||||
options: IOptions = new Options(customOptions),
|
||||
obfuscator: IObfuscator = new Obfuscator(options);
|
||||
|
||||
astTree = obfuscator.obfuscateNode(astTree);
|
||||
|
||||
return JavaScriptObfuscator.generateCode(astTree, options);
|
||||
let output: IGeneratorOutput = JavaScriptObfuscator.generateCode(astTree, sourceCode, options);
|
||||
|
||||
JavaScriptObfuscator.obfuscatedCode = output.code;
|
||||
|
||||
if (output.map) {
|
||||
JavaScriptObfuscator.sourceMap = output.map.toString();
|
||||
JavaScriptObfuscator.obfuscatedCode = new SourceMapInjector(
|
||||
JavaScriptObfuscator.obfuscatedCode,
|
||||
JavaScriptObfuscator.sourceMap,
|
||||
options
|
||||
).inject();
|
||||
}
|
||||
|
||||
return JavaScriptObfuscator.obfuscatedCode;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -43,11 +71,17 @@ export class JavaScriptObfuscator {
|
||||
|
||||
/**
|
||||
* @param astTree
|
||||
* @param sourceCode
|
||||
* @param options
|
||||
*/
|
||||
private static generateCode (astTree: INode, options: IOptions): string {
|
||||
private static generateCode (astTree: INode, sourceCode: string, options: IOptions): IGeneratorOutput {
|
||||
let escodegenParams: escodegen.GenerateOptions = Object.assign({}, JavaScriptObfuscator.escodegenParams);
|
||||
|
||||
if (options.get<boolean>('sourceMap')) {
|
||||
escodegenParams.sourceMap = 'sourceMap';
|
||||
escodegenParams.sourceContent = sourceCode;
|
||||
}
|
||||
|
||||
escodegenParams.format = {
|
||||
compact: options.get<boolean>('compact')
|
||||
};
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ export class Nodes {
|
||||
public static getProgramNode (bodyNode: TStatement[]): IProgramNode {
|
||||
return {
|
||||
'type': NodeType.Program,
|
||||
'body': bodyNode
|
||||
'body': bodyNode,
|
||||
'sourceType': 'script'
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { IOptions } from "./interfaces/IOptions";
|
||||
|
||||
import { TSourceMapModes } from "./types/TSourceMapModes";
|
||||
|
||||
import { SourceMapMode } from "./enums/SourceMapMode";
|
||||
|
||||
import { Utils } from "./Utils";
|
||||
|
||||
export class SourceMapInjector {
|
||||
/**
|
||||
* @type {IOptions}
|
||||
*/
|
||||
private options: IOptions;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
private sourceCode: string;
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
private sourceMap: string;
|
||||
|
||||
/**
|
||||
* @param sourceCode
|
||||
* @param sourceMap
|
||||
* @param options
|
||||
*/
|
||||
constructor (sourceCode: string, sourceMap: string, options: IOptions) {
|
||||
this.sourceCode = sourceCode;
|
||||
this.sourceMap = sourceMap;
|
||||
this.options = options;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param sourceCode
|
||||
* @param url
|
||||
* @param mode
|
||||
* @returns {string}
|
||||
*/
|
||||
public static appendSourceMapUrlToSourceCode (
|
||||
sourceCode: string,
|
||||
url: string,
|
||||
mode: TSourceMapModes = SourceMapMode.Separate
|
||||
): string {
|
||||
let sourceMappingUrl: string = '//# sourceMappingURL=';
|
||||
|
||||
if (mode === SourceMapMode.Separate) {
|
||||
sourceMappingUrl += url;
|
||||
} else {
|
||||
sourceMappingUrl += `data:application/json;base64,${Utils.btoa(url, false)}`;
|
||||
}
|
||||
|
||||
return `${sourceCode}\n${sourceMappingUrl}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
public inject (): string {
|
||||
if (this.options.get<string>('sourceMapMode') === SourceMapMode.Inline) {
|
||||
return SourceMapInjector.appendSourceMapUrlToSourceCode(
|
||||
this.sourceCode,
|
||||
this.sourceMap,
|
||||
SourceMapMode.Inline
|
||||
);
|
||||
}
|
||||
|
||||
return this.sourceCode;
|
||||
}
|
||||
}
|
||||
+29
-2
@@ -1,6 +1,8 @@
|
||||
import { Chance } from 'chance';
|
||||
|
||||
import { JSFuck } from './enums/JSFuck';
|
||||
import { SourceMapMode } from "./enums/SourceMapMode";
|
||||
import {TSourceMapModes} from "./types/TSourceMapModes";
|
||||
|
||||
export class Utils {
|
||||
/**
|
||||
@@ -13,6 +15,28 @@ export class Utils {
|
||||
*/
|
||||
private static randomGenerator: Chance.Chance = new Chance();
|
||||
|
||||
/**
|
||||
* @param sourceCode
|
||||
* @param url
|
||||
* @param mode
|
||||
* @returns {string}
|
||||
*/
|
||||
public static appendSourceMapUrlToSourceCode (
|
||||
sourceCode: string,
|
||||
url: string,
|
||||
mode: TSourceMapModes = SourceMapMode.Separate
|
||||
): string {
|
||||
let sourceMappingUrl: string = '//# sourceMappingURL=';
|
||||
|
||||
if (mode === SourceMapMode.Separate) {
|
||||
sourceMappingUrl += url;
|
||||
} else {
|
||||
sourceMappingUrl += `data:application/json;base64,${Utils.btoa(url, false)}`;
|
||||
}
|
||||
|
||||
return `${sourceCode}\n${sourceMappingUrl}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array
|
||||
* @param searchElement
|
||||
@@ -51,9 +75,12 @@ export class Utils {
|
||||
|
||||
/**
|
||||
* @param string
|
||||
* @param encode
|
||||
*/
|
||||
public static btoa (string: string): string {
|
||||
return new Buffer(encodeURI(string)).toString('base64');
|
||||
public static btoa (string: string, encode: boolean = true): string {
|
||||
return new Buffer(
|
||||
encode ? encodeURI(string) : string
|
||||
).toString('base64');
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -6,9 +6,12 @@ import * as path from 'path';
|
||||
import { IOptionsPreset } from "../interfaces/IOptionsPreset";
|
||||
import { IPackageConfig } from "../interfaces/IPackageConfig";
|
||||
|
||||
import { SourceMapMode } from "../enums/SourceMapMode";
|
||||
|
||||
import { DEFAULT_PRESET } from "../preset-options/DefaultPreset";
|
||||
|
||||
import { JavaScriptObfuscator } from "../JavaScriptObfuscator";
|
||||
import { SourceMapInjector } from "../SourceMapInjector";
|
||||
import { Utils } from "../Utils";
|
||||
|
||||
export class JavaScriptObfuscatorCLI {
|
||||
@@ -34,11 +37,6 @@ export class JavaScriptObfuscatorCLI {
|
||||
*/
|
||||
private commands: commander.ICommand;
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
private rawArguments: string[];
|
||||
|
||||
/**
|
||||
* @type {string}
|
||||
*/
|
||||
@@ -49,6 +47,11 @@ export class JavaScriptObfuscatorCLI {
|
||||
*/
|
||||
private inputPath: string;
|
||||
|
||||
/**
|
||||
* @type {string[]}
|
||||
*/
|
||||
private rawArguments: string[];
|
||||
|
||||
/**
|
||||
* @param argv
|
||||
*/
|
||||
@@ -147,6 +150,8 @@ export class JavaScriptObfuscatorCLI {
|
||||
.option('--reservedNames <list>', 'Disable obfuscation of variable names, function names and names of function parameters that match the passed RegExp patterns (comma separated)', (val: string) => val.split(','))
|
||||
.option('--rotateUnicodeArray <boolean>', 'Disable rotation of unicode array values during obfuscation', JavaScriptObfuscatorCLI.parseBoolean)
|
||||
.option('--selfDefending <boolean>', 'Disables self-defending for obfuscated code', JavaScriptObfuscatorCLI.parseBoolean)
|
||||
.option('--sourceMap <boolean>', 'Enables source map generation', JavaScriptObfuscatorCLI.parseBoolean)
|
||||
.option('--sourceMapMode [separate, inline]', 'Create a separate files with code and source map or combine them into a single file')
|
||||
.option('--unicodeArray <boolean>', 'Disables gathering of all literal strings into an array and replacing every literal string with an array call', JavaScriptObfuscatorCLI.parseBoolean)
|
||||
.option('--unicodeArrayThreshold <number>', 'The probability that the literal string will be inserted into unicodeArray (Default: 0.8, Min: 0, Max: 1)', parseFloat)
|
||||
.option('--wrapUnicodeArrayCalls <boolean>', 'Disables usage of special access function instead of direct array call', JavaScriptObfuscatorCLI.parseBoolean)
|
||||
@@ -184,7 +189,7 @@ export class JavaScriptObfuscatorCLI {
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
private getOutputPath (): string {
|
||||
private getOutputCodePath (): string {
|
||||
let outputPath: string = (<any>this.commands).output;
|
||||
|
||||
if (outputPath) {
|
||||
@@ -199,15 +204,46 @@ export class JavaScriptObfuscatorCLI {
|
||||
.join('.');
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns {string}
|
||||
*/
|
||||
private getOutputSourceMapPath (outputCodePath: string): string {
|
||||
return outputCodePath
|
||||
.split('.')
|
||||
.map<string>((value: string, index: number, array: string[]) => {
|
||||
return index === array.length - 1 ? `${value}.map` : value;
|
||||
})
|
||||
.join('.');
|
||||
}
|
||||
|
||||
private processData (): void {
|
||||
let outputPath: string = this.getOutputPath(),
|
||||
dirName: string = path.dirname(outputPath);
|
||||
let outputCodePath: string = this.getOutputCodePath(),
|
||||
outputSourceMapPath: string = this.getOutputSourceMapPath(outputCodePath),
|
||||
dirName: string = path.dirname(outputCodePath),
|
||||
options: IOptionsPreset = this.buildOptions();
|
||||
|
||||
mkdirp.sync(dirName);
|
||||
|
||||
JavaScriptObfuscator.obfuscate(this.data, options);
|
||||
|
||||
if (options.sourceMap && options.sourceMapMode === SourceMapMode.Separate) {
|
||||
JavaScriptObfuscator.obfuscatedCode = SourceMapInjector.appendSourceMapUrlToSourceCode(
|
||||
JavaScriptObfuscator.obfuscatedCode,
|
||||
[...outputSourceMapPath.split('/')].pop()
|
||||
);
|
||||
|
||||
fs.writeFileSync(
|
||||
outputSourceMapPath,
|
||||
JavaScriptObfuscator.sourceMap,
|
||||
{
|
||||
encoding: JavaScriptObfuscatorCLI.encoding
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
this.getOutputPath(),
|
||||
JavaScriptObfuscator.obfuscate(this.data, this.buildOptions()),
|
||||
outputCodePath,
|
||||
JavaScriptObfuscator.obfuscatedCode,
|
||||
{
|
||||
encoding: JavaScriptObfuscatorCLI.encoding
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
export const SourceMapMode: any = {
|
||||
Inline: 'inline',
|
||||
Separate: 'separate'
|
||||
};
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
import { IGeneratorOutput } from "./IGeneratorOutput";
|
||||
|
||||
declare module 'escodegen' {
|
||||
export function generate(ast: any, options?: GenerateOptions): IGeneratorOutput;
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
export interface IGeneratorOutput {
|
||||
code: string;
|
||||
map: string;
|
||||
}
|
||||
Vendored
+4
@@ -1,3 +1,5 @@
|
||||
import { TSourceMapModes } from "../types/TSourceMapModes";
|
||||
|
||||
export interface IOptionsPreset {
|
||||
compact?: boolean;
|
||||
debugProtection?: boolean;
|
||||
@@ -7,6 +9,8 @@ export interface IOptionsPreset {
|
||||
reservedNames?: string[];
|
||||
rotateUnicodeArray?: boolean;
|
||||
selfDefending?: boolean;
|
||||
sourceMap?: boolean;
|
||||
sourceMapMode?: TSourceMapModes;
|
||||
unicodeArray?: boolean;
|
||||
unicodeArrayThreshold?: number;
|
||||
wrapUnicodeArrayCalls?: boolean;
|
||||
|
||||
+1
@@ -4,4 +4,5 @@ import { INode } from "./INode";
|
||||
|
||||
export interface IProgramNode extends INode {
|
||||
body: TStatement[];
|
||||
sourceType: string;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { IOptionsPreset } from "../interfaces/IOptionsPreset";
|
||||
|
||||
import { SourceMapMode } from "../enums/SourceMapMode";
|
||||
|
||||
export const DEFAULT_PRESET: IOptionsPreset = Object.freeze({
|
||||
compact: true,
|
||||
debugProtection: false,
|
||||
@@ -9,6 +11,8 @@ export const DEFAULT_PRESET: IOptionsPreset = Object.freeze({
|
||||
reservedNames: [],
|
||||
rotateUnicodeArray: true,
|
||||
selfDefending: true,
|
||||
sourceMap: false,
|
||||
sourceMapMode: SourceMapMode.Separate,
|
||||
unicodeArray: true,
|
||||
unicodeArrayThreshold: 0.8,
|
||||
wrapUnicodeArrayCalls: true
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { IOptionsPreset } from "../interfaces/IOptionsPreset";
|
||||
|
||||
import { SourceMapMode } from "../enums/SourceMapMode";
|
||||
|
||||
export const NO_CUSTOM_NODES_PRESET: IOptionsPreset = Object.freeze({
|
||||
compact: true,
|
||||
debugProtection: false,
|
||||
@@ -9,6 +11,8 @@ export const NO_CUSTOM_NODES_PRESET: IOptionsPreset = Object.freeze({
|
||||
reservedNames: [],
|
||||
rotateUnicodeArray: false,
|
||||
selfDefending: false,
|
||||
sourceMap: false,
|
||||
sourceMapMode: SourceMapMode.Separate,
|
||||
unicodeArray: false,
|
||||
unicodeArrayThreshold: 0,
|
||||
wrapUnicodeArrayCalls: false
|
||||
|
||||
Vendored
+1
@@ -0,0 +1 @@
|
||||
export type TSourceMapModes = 'inline' | 'string';
|
||||
Reference in New Issue
Block a user