mirror of
https://github.com/javascript-obfuscator/javascript-obfuscator
synced 2026-08-09 12:42:29 +00:00
Merge pull request #295 from javascript-obfuscator/reserved-strings-option
`reservedStrings` option
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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`
|
||||
|
||||
|
||||
Vendored
+2
-2
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "javascript-obfuscator",
|
||||
"version": "0.17.1",
|
||||
"version": "0.18.0",
|
||||
"description": "JavaScript obfuscator",
|
||||
"keywords": [
|
||||
"obfuscator",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
|
||||
Vendored
+1
@@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+4
@@ -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;
|
||||
|
||||
+19
@@ -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;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
*/
|
||||
|
||||
@@ -22,6 +22,7 @@ export const DEFAULT_PRESET: TInputOptions = Object.freeze({
|
||||
log: false,
|
||||
renameGlobals: false,
|
||||
reservedNames: [],
|
||||
reservedStrings: [],
|
||||
rotateStringArray: true,
|
||||
seed: 0,
|
||||
selfDefending: false,
|
||||
|
||||
@@ -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,
|
||||
|
||||
+155
@@ -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', () => {
|
||||
|
||||
+2
@@ -0,0 +1,2 @@
|
||||
const foo = 'foo';
|
||||
const bar = 'bar';
|
||||
Reference in New Issue
Block a user