source map WIP integration

This commit is contained in:
sanex3339
2016-07-07 19:12:11 +03:00
parent 392f8456c5
commit e46fb2d9bb
16 changed files with 515 additions and 120 deletions
+38 -4
View File
@@ -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
View File
@@ -21,7 +21,8 @@ export class Nodes {
public static getProgramNode (bodyNode: TStatement[]): IProgramNode {
return {
'type': NodeType.Program,
'body': bodyNode
'body': bodyNode,
'sourceType': 'script'
};
}
+72
View File
@@ -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
View File
@@ -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');
}
/**
+46 -10
View File
@@ -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
}
+4
View File
@@ -0,0 +1,4 @@
export const SourceMapMode: any = {
Inline: 'inline',
Separate: 'separate'
};
+5
View File
@@ -0,0 +1,5 @@
import { IGeneratorOutput } from "./IGeneratorOutput";
declare module 'escodegen' {
export function generate(ast: any, options?: GenerateOptions): IGeneratorOutput;
}
+4
View File
@@ -0,0 +1,4 @@
export interface IGeneratorOutput {
code: string;
map: string;
}
+4
View File
@@ -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
View File
@@ -4,4 +4,5 @@ import { INode } from "./INode";
export interface IProgramNode extends INode {
body: TStatement[];
sourceType: string;
}
+4
View File
@@ -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
+1
View File
@@ -0,0 +1 @@
export type TSourceMapModes = 'inline' | 'string';