Merge pull request #295 from javascript-obfuscator/reserved-strings-option

`reservedStrings` option
This commit is contained in:
Timofey Kachalov
2018-07-13 23:10:04 +03:00
committed by GitHub
17 changed files with 249 additions and 13 deletions
+4
View File
@@ -1,5 +1,9 @@
Change Log
===
v0.18.0
---
* **New option:** `reservedStrings` disables transformation of string literals, which being matched by passed RegExp patterns
v0.17.1
---
* Fixed https://github.com/javascript-obfuscator/javascript-obfuscator/issues/293
+18
View File
@@ -297,6 +297,7 @@ Following options are available for the JS Obfuscator:
log: false,
renameGlobals: false,
reservedNames: [],
reservedStrings: [],
rotateStringArray: true,
seed: 0,
selfDefending: false,
@@ -336,6 +337,7 @@ Following options are available for the JS Obfuscator:
--log <boolean>
--rename-globals <boolean>
--reserved-names '<list>' (comma separated)
--reserved-strings '<list>' (comma separated)
--rotate-string-array <boolean>
--seed <number>
--self-defending <boolean>
@@ -633,6 +635,22 @@ Example:
}
```
### `reservedStrings`
Type: `string[]` Default: `[]`
Disables transformation of string literals, which being matched by passed RegExp patterns.
Example:
```ts
{
reservedStrings: [
'react-native',
'\.\/src\/test',
'some-string_\d'
]
}
```
### `rotateStringArray`
Type: `boolean` Default: `true`
+2 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "javascript-obfuscator",
"version": "0.17.1",
"version": "0.18.0",
"description": "JavaScript obfuscator",
"keywords": [
"obfuscator",
+5
View File
@@ -268,6 +268,11 @@ export class JavaScriptObfuscatorCLI implements IInitializable {
'Disables obfuscation and generation of identifiers, which being matched by passed RegExp patterns (comma separated)',
ArraySanitizer
)
.option(
'--reserved-strings <list> (comma separated, without whitespaces)',
'Disables transformation of string literals, which being matched by passed RegExp patterns (comma separated)',
ArraySanitizer
)
.option(
'--rename-globals <boolean>', 'Allows to enable obfuscation of global variable and function names with declaration.',
BooleanSanitizer
+1 -1
View File
@@ -6,7 +6,7 @@ import { TCLISanitizer } from '../../types/cli/TCLISanitizer';
*/
export const ArraySanitizer: TCLISanitizer <string[]> = (value: string): string[] => {
if (/,$/.test(value)) {
throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --option-name 'value1, value2'`);
throw new SyntaxError(`Multiple <list> values should be wrapped inside quotes: --option-name 'value1','value2'`);
}
return value.split(',').map((string: string) => string.trim());
+1
View File
@@ -20,6 +20,7 @@ export interface IOptions {
readonly log: boolean;
readonly renameGlobals: boolean;
readonly reservedNames: string[];
readonly reservedStrings: string[];
readonly rotateStringArray: boolean;
readonly seed: number;
readonly selfDefending: boolean;
@@ -15,6 +15,7 @@ import { TransformationStage } from '../../enums/node-transformers/Transformatio
import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
import { NodeGuards } from '../../node/NodeGuards';
import { NodeMetadata } from '../../node/NodeMetadata';
import { NodeUtils } from '../../node/NodeUtils';
@injectable()
export class LiteralTransformer extends AbstractNodeTransformer {
@@ -69,22 +70,37 @@ export class LiteralTransformer extends AbstractNodeTransformer {
return literalNode;
}
let newLiteralNode: ESTree.Node;
switch (typeof literalNode.value) {
case 'boolean':
return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.BooleanLiteralObfuscatingReplacer)
.replace(<boolean>literalNode.value);
newLiteralNode = this.literalObfuscatingReplacerFactory(
LiteralObfuscatingReplacer.BooleanLiteralObfuscatingReplacer
).replace(<boolean>literalNode.value);
break;
case 'number':
return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.NumberLiteralObfuscatingReplacer)
.replace(<number>literalNode.value);
newLiteralNode = this.literalObfuscatingReplacerFactory(
LiteralObfuscatingReplacer.NumberLiteralObfuscatingReplacer
).replace(<number>literalNode.value);
break;
case 'string':
return this.literalObfuscatingReplacerFactory(LiteralObfuscatingReplacer.StringLiteralObfuscatingReplacer)
.replace(<string>literalNode.value);
newLiteralNode = this.literalObfuscatingReplacerFactory(
LiteralObfuscatingReplacer.StringLiteralObfuscatingReplacer
).replace(<string>literalNode.value);
break;
default:
return literalNode;
newLiteralNode = literalNode;
}
NodeUtils.parentizeNode(newLiteralNode, parentNode);
return newLiteralNode;
}
/**
@@ -107,6 +107,10 @@ export class BaseIdentifierObfuscatingReplacer extends AbstractObfuscatingReplac
* @returns {boolean}
*/
private isReservedName (name: string): boolean {
if (!this.options.reservedStrings.length) {
return false;
}
return this.options.reservedNames
.some((reservedName: string) => {
return new RegExp(reservedName, 'g').exec(name) !== null;
@@ -133,6 +133,10 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
* @returns {Node}
*/
public replace (nodeValue: string): ESTree.Node {
if (this.isReservedString(nodeValue)) {
return NodeFactory.literalNode(nodeValue);
}
const useStringArray: boolean = this.canUseStringArray(nodeValue);
const cacheKey: string = `${nodeValue}-${String(useStringArray)}`;
const useCacheValue: boolean = this.nodesCache.has(cacheKey) && this.options.stringArrayEncoding !== StringArrayEncoding.Rc4;
@@ -263,4 +267,19 @@ export class StringLiteralObfuscatingReplacer extends AbstractObfuscatingReplace
callExpressionArgs
);
}
/**
* @param {string} value
* @returns {boolean}
*/
private isReservedString (value: string): boolean {
if (!this.options.reservedStrings.length) {
return false;
}
return this.options.reservedStrings
.some((reservedString: string) => {
return new RegExp(reservedString, 'g').exec(value) !== null;
});
}
}
+10
View File
@@ -146,6 +146,16 @@ export class Options implements IOptions {
})
public readonly reservedNames!: string[];
/**
* @type {string[]}
*/
@IsArray()
@ArrayUnique()
@IsString({
each: true
})
public readonly reservedStrings!: string[];
/**
* @type {boolean}
*/
+1
View File
@@ -22,6 +22,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
log: false,
renameGlobals: false,
reservedNames: [],
reservedStrings: [],
rotateStringArray: true,
seed: 0,
selfDefending: false,
+1
View File
@@ -21,6 +21,7 @@ export const NO_ADDITIONAL_NODES_PRESET: TInputOptions = Object.freeze({
log: false,
renameGlobals: false,
reservedNames: [],
reservedStrings: [],
rotateStringArray: false,
seed: 0,
selfDefending: false,
@@ -315,6 +315,161 @@ describe('LiteralTransformer', () => {
assert.match(obfuscatedCode, regExp);
});
});
describe('Variant #12: `reservedNames` option is enabled', () => {
describe('Variant #1: base `reservedStrings` values', () => {
describe('Variant #1: single reserved string value', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *_0x([a-f0-9]){4}\('0x0'\);/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
reservedStrings: ['foo']
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('match #1: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform non-reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
describe('Variant #2: two reserved string values', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
reservedStrings: ['foo', 'bar']
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('match #1: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
describe('Variant #2: RegExp `reservedStrings` values', () => {
describe('Variant #1: single reserved string value', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *_0x([a-f0-9]){4}\('0x0'\);/;
const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
reservedStrings: ['ar$']
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('match #1: should transform non-reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
describe('Variant #2: two reserved string values', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *'bar';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
stringArray: true,
stringArrayThreshold: 1,
reservedStrings: ['^fo', '.ar']
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('match #1: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
describe('Variant #3: `unicodeEscapeSequence` option is enabled', () => {
const stringLiteralRegExp1: RegExp = /const foo *= *'foo';/;
const stringLiteralRegExp2: RegExp = /const bar *= *'\\x62\\x61\\x72';/;
let obfuscatedCode: string;
before(() => {
const code: string = readFileAsString(__dirname + '/fixtures/reserved-strings-option.js');
const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
code,
{
...NO_ADDITIONAL_NODES_PRESET,
reservedStrings: ['foo'],
unicodeEscapeSequence: true
}
);
obfuscatedCode = obfuscationResult.getObfuscatedCode();
});
it('match #1: should ignore reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp1);
});
it('match #2: should transform non-reserved strings', () => {
assert.match(obfuscatedCode, stringLiteralRegExp2);
});
});
});
});
describe('transformation of literal node with boolean value', () => {
@@ -0,0 +1,2 @@
const foo = 'foo';
const bar = 'bar';