Skip to content

Commit

Permalink
Add target 'service-worker' (javascript-obfuscator#1134)
Browse files Browse the repository at this point in the history
  • Loading branch information
fohletex authored Sep 5, 2023
1 parent c7d4f9c commit 54cbdf6
Show file tree
Hide file tree
Showing 8 changed files with 268 additions and 26 deletions.
16 changes: 13 additions & 3 deletions src/custom-code-helpers/AbstractCustomCodeHelper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import { IRandomGenerator } from '../interfaces/utils/IRandomGenerator';

import { GlobalVariableTemplate1 } from './common/templates/GlobalVariableTemplate1';
import { GlobalVariableTemplate2 } from './common/templates/GlobalVariableTemplate2';
import { ObfuscationTarget } from '../enums/ObfuscationTarget';
import { GlobalVariableNoEvalTemplate } from './common/templates/GlobalVariableNoEvalTemplate';
import { GlobalVariableServiceWorkerTemplate } from './common/templates/GlobalVariableServiceWorkerTemplate';

@injectable()
export abstract class AbstractCustomCodeHelper <
Expand Down Expand Up @@ -97,9 +100,16 @@ export abstract class AbstractCustomCodeHelper <
* @returns {string}
*/
protected getGlobalVariableTemplate (): string {
return this.randomGenerator
.getRandomGenerator()
.pickone(AbstractCustomCodeHelper.globalVariableTemplateFunctions);
switch (this.options.target) {
case ObfuscationTarget.BrowserNoEval:
return GlobalVariableNoEvalTemplate();
case ObfuscationTarget.ServiceWorker:
return GlobalVariableServiceWorkerTemplate();
default:
return this.randomGenerator
.getRandomGenerator()
.pickone(AbstractCustomCodeHelper.globalVariableTemplateFunctions);
}
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/**
* @returns {string}
*/
export function GlobalVariableServiceWorkerTemplate (): string {
return `const that = typeof global === 'object' ? global : this;`;
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@ import { ICustomCodeHelperObfuscator } from '../../interfaces/custom-code-helper
import { IOptions } from '../../interfaces/options/IOptions';
import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';

import { ObfuscationTarget } from '../../enums/ObfuscationTarget';

import { ConsoleOutputDisableTemplate } from './templates/ConsoleOutputDisableTemplate';
import { GlobalVariableNoEvalTemplate } from '../common/templates/GlobalVariableNoEvalTemplate';

import { initializable } from '../../decorators/Initializable';

Expand Down Expand Up @@ -78,14 +76,10 @@ export class ConsoleOutputDisableCodeHelper extends AbstractCustomCodeHelper {
* @returns {string}
*/
protected override getCodeHelperTemplate (): string {
const globalVariableTemplate: string = this.options.target !== ObfuscationTarget.BrowserNoEval
? this.getGlobalVariableTemplate()
: GlobalVariableNoEvalTemplate();

return this.customCodeHelperFormatter.formatTemplate(ConsoleOutputDisableTemplate(), {
callControllerFunctionName: this.callsControllerFunctionName,
consoleLogDisableFunctionName: this.consoleOutputDisableFunctionName,
globalVariableTemplate
globalVariableTemplate: this.getGlobalVariableTemplate(),
});
}
}
4 changes: 3 additions & 1 deletion src/enums/ObfuscationTarget.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@ export const ObfuscationTarget: Readonly<{
Browser: 'browser';
BrowserNoEval: 'browser-no-eval';
Node: 'node';
ServiceWorker: 'service-worker';
}> = Utils.makeEnum({
Browser: 'browser',
BrowserNoEval: 'browser-no-eval',
Node: 'node'
Node: 'node',
ServiceWorker: 'service-worker',
});
2 changes: 1 addition & 1 deletion src/options/Options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ export class Options implements IOptions {
/**
* @type {ObfuscationTarget}
*/
@IsIn([ObfuscationTarget.Browser, ObfuscationTarget.BrowserNoEval, ObfuscationTarget.Node])
@IsIn([ObfuscationTarget.Browser, ObfuscationTarget.BrowserNoEval, ObfuscationTarget.Node, ObfuscationTarget.ServiceWorker])
public readonly target!: TTypeFromEnum<typeof ObfuscationTarget>;

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,40 @@ describe('DebugProtectionFunctionCallTemplate', function () {
});
});

describe('Variant #5: obfuscated code with removed debug protection code', () => {
describe('Variant #5: correctly obfuscated code with target `ServiceWorker', () => {
const expectedEvaluationResult: number = 1;

let obfuscatedCode: string,
evaluationResult: number = 0;

beforeEach(() => {
const code: string = readFileAsString(__dirname + '/fixtures/input.js');

obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
debugProtection: true,
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();

return evaluateInWorker(obfuscatedCode, evaluationTimeout)
.then((result: string | null) => {
if (!result) {
return;
}

evaluationResult = parseInt(result, 10);
});
});

it('should correctly evaluate code with enabled debug protection', () => {
assert.equal(evaluationResult, expectedEvaluationResult);
});
});

describe('Variant #6: obfuscated code with removed debug protection code', () => {
const expectedEvaluationResult: number = 0;

let obfuscatedCode: string,
Expand Down Expand Up @@ -182,7 +215,7 @@ describe('DebugProtectionFunctionCallTemplate', function () {
});
});

describe('Variant #6: single call of debug protection code', () => {
describe('Variant #7: single call of debug protection code', () => {
const expectedEvaluationResult: number = 1;

let obfuscatedCode: string,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,87 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
});
});
});

describe('Variant #3: target `service-worker', () => {
describe('Variant #1: correct class name references in global scope', () => {
const classNameIdentifierRegExp: RegExp = /class A *\{/;
const outerClassNameReferenceRegExp: RegExp = /console\['log']\(A\);/;
const innerClassNameReferenceRegExp: RegExp = /return A;/;

let obfuscatedCode: string;

before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-global-scope.js');

obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();
});

it('match #1: shouldn\'t transform class name', () => {
assert.match(obfuscatedCode, classNameIdentifierRegExp);
});

it('match #2: shouldn\'t transform class name reference outside of class', () => {
assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
});

it('match #3: shouldn\'t transform class name reference inside class', () => {
assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
});
});

describe('Variant #2: correct class name references in function scope', () => {
const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;

let obfuscatedCode: string;
let classNameIdentifier: string;
let outerClassNameReferenceIdentifierName: string;
let innerClassNameReferenceIdentifierName: string;

before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-function-scope.js');

obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();

classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #1: should transform class name', () => {
assert.match(obfuscatedCode, classNameIdentifierRegExp);
});

it('match #2: should transform class name reference outside of class', () => {
assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
});

it('match #3: should transform class name reference inside class', () => {
assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #4: should generate same identifier names for class name and outer class name reference', () => {
assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
});

it('match #5: should generate same identifier names for class name and inner class name reference', () => {
assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
});
});
});
});

describe('Variant #2: `renameGlobals` option is enabled', () => {
Expand Down Expand Up @@ -479,6 +560,104 @@ describe('ScopeIdentifiersTransformer ClassDeclaration identifiers', () => {
});
});
});

describe('Variant #4: target: `service-worker', () => {
describe('Variant #1: correct class name references in global scope', () => {
const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;

let obfuscatedCode: string;
let classNameIdentifier: string;
let outerClassNameReferenceIdentifierName: string;
let innerClassNameReferenceIdentifierName: string;

before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-global-scope.js');

obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
renameGlobals: true,
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();

classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #1: should transform class name', () => {
assert.match(obfuscatedCode, classNameIdentifierRegExp);
});

it('match #2: should transform class name reference outside of class', () => {
assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
});

it('match #3: should transform class name reference inside class', () => {
assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #4: should generate same identifier names for class name and outer class name reference', () => {
assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
});

it('match #5: should generate same identifier names for class name and inner class name reference', () => {
assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
});
});

describe('Variant #2: correct class name references in function scope', () => {
const classNameIdentifierRegExp: RegExp = /class (_0x[a-f0-9]{4,6}) *\{/;
const outerClassNameReferenceRegExp: RegExp = /console\['log']\((_0x[a-f0-9]{4,6})\);/;
const innerClassNameReferenceRegExp: RegExp = /return (_0x[a-f0-9]{4,6});/;

let obfuscatedCode: string;
let classNameIdentifier: string;
let outerClassNameReferenceIdentifierName: string;
let innerClassNameReferenceIdentifierName: string;

before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/class-name-references-function-scope.js');

obfuscatedCode = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
renameGlobals: true,
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();

classNameIdentifier = getRegExpMatch(obfuscatedCode, classNameIdentifierRegExp);
outerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, outerClassNameReferenceRegExp);
innerClassNameReferenceIdentifierName = getRegExpMatch(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #1: should transform class name', () => {
assert.match(obfuscatedCode, classNameIdentifierRegExp);
});

it('match #2: should transform class name reference outside of class', () => {
assert.match(obfuscatedCode, outerClassNameReferenceRegExp);
});

it('match #3: should transform class name reference inside class', () => {
assert.match(obfuscatedCode, innerClassNameReferenceRegExp);
});

it('match #4: should generate same identifier names for class name and outer class name reference', () => {
assert.equal(classNameIdentifier, outerClassNameReferenceIdentifierName);
});

it('match #5: should generate same identifier names for class name and inner class name reference', () => {
assert.equal(classNameIdentifier, innerClassNameReferenceIdentifierName);
});
});
});
});
});

Expand Down
42 changes: 30 additions & 12 deletions test/functional-tests/options/domain-lock/Validation.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,22 +50,40 @@ describe('`domainLock` validation', () => {

describe('Variant #2: negative validation', () => {
const expectedError: string = 'This option allowed only for obfuscation targets';

let testFunc: () => string;

beforeEach(() => {
testFunc = () => JavaScriptObfuscator.obfuscate(
'',
{
...NO_ADDITIONAL_NODES_PRESET,
domainLock: ['www.example.com'],
target: ObfuscationTarget.Node
}
).getObfuscatedCode();
describe('Variant #1: obfuscation target: `node`', () => {
beforeEach(() => {
testFunc = () => JavaScriptObfuscator.obfuscate(
'',
{
...NO_ADDITIONAL_NODES_PRESET,
domainLock: ['www.example.com'],
target: ObfuscationTarget.Node
}
).getObfuscatedCode();
});

it('should not pass validation when obfuscation target is `node` and value is not default', () => {
assert.throws(testFunc, expectedError);
});
});

it('should not pass validation when obfuscation target is `node` and value is not default', () => {
assert.throws(testFunc, expectedError);
describe('Variant #1: obfuscation target: `service-worker`', () => {
beforeEach(() => {
testFunc = () => JavaScriptObfuscator.obfuscate(
'',
{
...NO_ADDITIONAL_NODES_PRESET,
domainLock: ['www.example.com'],
target: ObfuscationTarget.ServiceWorker
}
).getObfuscatedCode();
});

it('should not pass validation when obfuscation target is `service-worker` and value is not default', () => {
assert.throws(testFunc, expectedError);
});
});
});
});
Expand Down

0 comments on commit 54cbdf6

Please sign in to comment.