Skip to content

Commit 77ff574

Browse files
committed
Fixed isIncrementedMangledName algorithm
1 parent ae52c1d commit 77ff574

File tree

11 files changed

+314
-157
lines changed

11 files changed

+314
-157
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
Change Log
22

3+
v2.3.1
4+
---
5+
* Fixed a rare bug with `identifierNamesGenerator: 'mangled'` option that causes wrong identifier names generation
6+
37
v2.3.0
48
---
59
* **New option:** `stringArrayWrappersType` allows to select a type of the wrappers that are appending by the `stringArrayWrappersCount` option

dist/index.browser.js

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.cli.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "javascript-obfuscator",
3-
"version": "2.3.0",
3+
"version": "2.3.1",
44
"description": "JavaScript obfuscator",
55
"keywords": [
66
"obfuscator",
@@ -54,7 +54,7 @@
5454
"@types/mkdirp": "1.0.1",
5555
"@types/mocha": "8.0.3",
5656
"@types/multimatch": "4.0.0",
57-
"@types/node": "14.10.3",
57+
"@types/node": "14.11.1",
5858
"@types/rimraf": "3.0.0",
5959
"@types/sinon": "9.0.5",
6060
"@types/string-template": "1.0.2",
@@ -69,7 +69,7 @@
6969
"eslint-plugin-jsdoc": "30.5.1",
7070
"eslint-plugin-no-null": "1.0.2",
7171
"eslint-plugin-prefer-arrow": "1.2.2",
72-
"eslint-plugin-unicorn": "21.0.0",
72+
"eslint-plugin-unicorn": "22.0.0",
7373
"fork-ts-checker-notifier-webpack-plugin": "3.0.0",
7474
"fork-ts-checker-webpack-plugin": "5.2.0",
7575
"mocha": "8.1.3",
@@ -79,10 +79,10 @@
7979
"rimraf": "3.0.2",
8080
"sinon": "9.0.3",
8181
"threads": "1.6.3",
82-
"ts-loader": "8.0.3",
82+
"ts-loader": "8.0.4",
8383
"ts-node": "9.0.0",
84-
"typescript": "4.0.2",
85-
"webpack": "4.44.1",
84+
"typescript": "4.0.3",
85+
"webpack": "4.44.2",
8686
"webpack-cli": "3.3.12",
8787
"webpack-node-externals": "2.5.2"
8888
},

src/generators/identifier-names-generators/MangledIdentifierNamesGenerator.ts

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
6464
* @param {string} prevName
6565
* @returns {boolean}
6666
*/
67+
// eslint-disable-next-line complexity
6768
public static isIncrementedMangledName (nextName: string, prevName: string): boolean {
6869
if (nextName === prevName) {
6970
return false;
@@ -76,6 +77,8 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
7677
return nextNameLength > prevNameLength;
7778
}
7879

80+
let isIncrementedPrevCharacter: boolean = false;
81+
7982
for (let i: number = 0; i < nextNameLength; i++) {
8083
const nextNameCharacter: string = nextName[i];
8184
const prevNameCharacter: string = prevName[i];
@@ -84,6 +87,17 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
8487
continue;
8588
}
8689

90+
const isDigitNextNameCharacter: boolean = MangledIdentifierNamesGenerator.isDigitCharacter(nextNameCharacter);
91+
const isDigitPrevNameCharacter: boolean = MangledIdentifierNamesGenerator.isDigitCharacter(prevNameCharacter);
92+
93+
if (
94+
isIncrementedPrevCharacter
95+
&& isDigitNextNameCharacter
96+
&& !isDigitPrevNameCharacter
97+
) {
98+
return true;
99+
}
100+
87101
const isUpperCaseNextNameCharacter: boolean = MangledIdentifierNamesGenerator.isUpperCaseCharacter(nextNameCharacter);
88102
const isUpperCasePrevNameCharacter: boolean = MangledIdentifierNamesGenerator.isUpperCaseCharacter(prevNameCharacter);
89103

@@ -98,6 +112,12 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
98112
) {
99113
return false;
100114
}
115+
116+
isIncrementedPrevCharacter = nextNameCharacter > prevNameCharacter;
117+
118+
if (nextNameCharacter < prevNameCharacter) {
119+
return false;
120+
}
101121
}
102122

103123
return nextName > prevName;
@@ -111,6 +131,14 @@ export class MangledIdentifierNamesGenerator extends AbstractIdentifierNamesGene
111131
return /^[A-Z]*$/.test(string);
112132
}
113133

134+
/**
135+
* @param {string} character
136+
* @returns {boolean}
137+
*/
138+
private static isDigitCharacter (string: string): boolean {
139+
return /^[0-9]*$/.test(string);
140+
}
141+
114142
/**
115143
* Generates next name based on a global previous mangled name
116144
* We can ignore nameLength parameter here, it hasn't sense with this generator

test/functional-tests/javascript-obfuscator/JavaScriptObfuscator.spec.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -869,6 +869,52 @@ describe('JavaScriptObfuscator', () => {
869869
});
870870
});
871871

872+
describe('Eval `Hello World`', function () {
873+
this.timeout(20000);
874+
875+
const samplesCount: number = 100;
876+
const expectedEvaluationResult: string = 'aaabbbcccdddeee';
877+
let isEvaluationSuccessful: boolean = true;
878+
879+
before(() => {
880+
const code: string = readFileAsString(__dirname + '/fixtures/eval-hello-world.js');
881+
882+
for (let i = 0; i < samplesCount; i++) {
883+
const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
884+
code,
885+
{
886+
...NO_ADDITIONAL_NODES_PRESET,
887+
compact: false,
888+
controlFlowFlattening: true,
889+
controlFlowFlatteningThreshold: 1,
890+
deadCodeInjection: true,
891+
deadCodeInjectionThreshold: 1,
892+
disableConsoleOutput: true,
893+
identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator,
894+
renameProperties: true,
895+
simplify: false,
896+
stringArray: true,
897+
stringArrayThreshold: 1,
898+
stringArrayWrappersChainedCalls: true,
899+
stringArrayWrappersCount: 1,
900+
stringArrayWrappersType: StringArrayWrappersType.Variable
901+
}
902+
).getObfuscatedCode();
903+
904+
const evaluationResult: string = eval(obfuscatedCode);
905+
906+
if (evaluationResult !== expectedEvaluationResult) {
907+
isEvaluationSuccessful = false;
908+
break;
909+
}
910+
}
911+
});
912+
913+
it('should correctly evaluate obfuscated code', () => {
914+
assert.equal(isEvaluationSuccessful, true);
915+
});
916+
});
917+
872918
describe('Identifier names collision between base code and appended string array nodes', function () {
873919
this.timeout(10000);
874920

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
function func () {
2+
var foo = 'aaa';
3+
var bar = 'bbb';
4+
var baz = 'ccc';
5+
var bark = 'ddd';
6+
var hawk = 'eee';
7+
8+
return foo + bar + baz + bark + hawk;
9+
}
10+
11+
func();

test/functional-tests/node-transformers/string-array-transformers/string-array-scope-calls-wrapper-transformer/StringArrayScopeCallsWrapperTransformer.spec.ts

Lines changed: 52 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
4949

5050
describe('Variant #2: option value is bigger then count `literal` nodes in the scope', () => {
5151
const stringArrayCallRegExp: RegExp = new RegExp(
52-
'return _0x([a-f0-9]){4,6};' +
52+
'return _0x([a-f0-9]){4,6};' +
5353
'};' +
5454
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
5555
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
@@ -116,11 +116,11 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
116116
describe('Variant #1: option value is lower then count `literal` nodes in the scope', () => {
117117
const stringArrayCallRegExp: RegExp = new RegExp(
118118
'function test *\\( *\\) *{' +
119-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
120-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
121-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
122-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
123-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
119+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
120+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
121+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
122+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
123+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
124124
'}'
125125
);
126126

@@ -148,12 +148,12 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
148148
describe('Variant #2: option value is bigger then count `literal` nodes in the scope', () => {
149149
const stringArrayCallRegExp: RegExp = new RegExp(
150150
'function test *\\(\\) *{' +
151-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
152-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
153-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
154-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
155-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
156-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
151+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
152+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
153+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
154+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
155+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
156+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
157157
'}'
158158
);
159159

@@ -181,11 +181,11 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
181181
describe('Variant #3: correct wrappers order', () => {
182182
const stringArrayCallRegExp: RegExp = new RegExp(
183183
'function test *\\( *\\) *{' +
184-
'const h *= *b;' +
185-
'const i *= *b;' +
186-
'const c *= *[h|i]\\(\'0x3\'\\);' +
187-
'const d *= *[h|i]\\(\'0x4\'\\);' +
188-
'const e *= *[h|i]\\(\'0x5\'\\);' +
184+
'const h *= *b;' +
185+
'const i *= *b;' +
186+
'const c *= *[h|i]\\(\'0x3\'\\);' +
187+
'const d *= *[h|i]\\(\'0x4\'\\);' +
188+
'const e *= *[h|i]\\(\'0x5\'\\);' +
189189
'}'
190190
);
191191

@@ -249,7 +249,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
249249
const stringArrayCallRegExp: RegExp = new RegExp(
250250
'var c *= *b;' +
251251
'if *\\(!!\\[]\\) *{' +
252-
'var foo *= *c\\(\'0x0\'\\);' +
252+
'var foo *= *c\\(\'0x0\'\\);' +
253253
'}'
254254
);
255255

@@ -306,7 +306,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
306306

307307
describe('Variant #4: prevailing kind of variables', () => {
308308
const stringArrayCallRegExp: RegExp = new RegExp(
309-
'return _0x([a-f0-9]){4,6};' +
309+
'return _0x([a-f0-9]){4,6};' +
310310
'};' +
311311
'var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
312312
'var _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
@@ -368,22 +368,22 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
368368
'const q *= *b;' +
369369
'const foo *= *q\\(\'0x0\'\\);' +
370370
'function test\\(c, *d\\) *{' +
371-
'const r *= *q;' +
372-
'const e *= *r\\(\'0x1\'\\);' +
373-
'const f *= *r\\(\'0x2\'\\);' +
374-
'function g\\(h, *i\\) *{' +
375-
'const s *= *r;' +
376-
'const j *= *s\\(\'0x3\'\\);' +
377-
'const k *= *s\\(\'0x4\'\\);' +
378-
'function l\\(m, *n *\\) *{' +
379-
'const t *= *s;' +
380-
'const o *= *t\\(\'0x3\'\\);' +
381-
'const p *= *t\\(\'0x4\'\\);' +
382-
'return o *\\+ *p;' +
383-
'}' +
384-
'return j *\\+ *k;' +
385-
'}' +
386-
'return e *\\+ *f *\\+ *g\\(\\);' +
371+
'const r *= *q;' +
372+
'const e *= *r\\(\'0x1\'\\);' +
373+
'const f *= *r\\(\'0x2\'\\);' +
374+
'function g\\(h, *i\\) *{' +
375+
'const s *= *r;' +
376+
'const j *= *s\\(\'0x3\'\\);' +
377+
'const k *= *s\\(\'0x4\'\\);' +
378+
'function l\\(m, *n *\\) *{' +
379+
'const t *= *s;' +
380+
'const o *= *t\\(\'0x3\'\\);' +
381+
'const p *= *t\\(\'0x4\'\\);' +
382+
'return o *\\+ *p;' +
383+
'}' +
384+
'return j *\\+ *k;' +
385+
'}' +
386+
'return e *\\+ *f *\\+ *g\\(\\);' +
387387
'}'
388388
);
389389

@@ -869,7 +869,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
869869
describe('Variant #1: root scope', () => {
870870
describe('Variant #1: `1` scope calls wrapper for each encoding type', () => {
871871
const stringArrayWrappersRegExp: RegExp = new RegExp(
872-
'return _0x([a-f0-9]){4,6};' +
872+
'return _0x([a-f0-9]){4,6};' +
873873
'};' +
874874
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
875875
// this one may be added or not depends on:
@@ -907,7 +907,7 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
907907

908908
describe('Variant #2: `2` scope calls wrappers for each encoding type', () => {
909909
const stringArrayWrappersRegExp: RegExp = new RegExp(
910-
'return _0x([a-f0-9]){4,6};' +
910+
'return _0x([a-f0-9]){4,6};' +
911911
'};' +
912912
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
913913
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
@@ -949,13 +949,13 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
949949
describe('Variant #1: `1` scope calls wrapper for each encoding type', () => {
950950
const stringArrayWrappersRegExp: RegExp = new RegExp(
951951
'function test *\\( *\\) *{' +
952-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
953-
// this one may be added or not depends on:
954-
// if all literal values encoded with a single encoding or not
955-
'(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
956-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
957-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
958-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
952+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
953+
// this one may be added or not depends on:
954+
// if all literal values encoded with a single encoding or not
955+
'(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
956+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
957+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
958+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
959959
'}'
960960
);
961961

@@ -987,14 +987,14 @@ describe('StringArrayScopeCallsWrapperTransformer', function () {
987987
describe('Variant #2: `2` scope calls wrappers for each encoding type', () => {
988988
const stringArrayWrappersRegExp: RegExp = new RegExp(
989989
'function test *\\( *\\) *{' +
990-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
991-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
992-
// this one may be added or not depends on:
993-
// if all literal values encoded with a single encoding or not
994-
'(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
995-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
996-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
997-
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
990+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
991+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};' +
992+
// this one may be added or not depends on:
993+
// if all literal values encoded with a single encoding or not
994+
'(?:const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4};)?' +
995+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x3\'\\);' +
996+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x4\'\\);' +
997+
'const _0x([a-f0-9]){4,6} *= *_0x([a-f0-9]){4,6}\\(\'0x5\'\\);' +
998998
'}'
999999
);
10001000

0 commit comments

Comments
 (0)