JavaScriptObfuscator.spec.ts 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957
  1. import { assert } from 'chai';
  2. import { TypeFromEnum } from '@gradecam/tsenum';
  3. import { TInputOptions } from '../../../src/types/options/TInputOptions';
  4. import { TObject } from '../../../src/types/TObject';
  5. import { IObfuscatedCode } from '../../../src/interfaces/source-code/IObfuscatedCode';
  6. import { SourceMapMode } from '../../../src/enums/source-map/SourceMapMode';
  7. import { StringArrayEncoding } from '../../../src/enums/StringArrayEncoding';
  8. import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscatorFacade';
  9. import { NO_ADDITIONAL_NODES_PRESET } from '../../../src/options/presets/NoCustomNodes';
  10. import { IdentifierNamesGenerator } from '../../../src/enums/generators/identifier-names-generators/IdentifierNamesGenerator';
  11. import { buildLargeCode } from '../../helpers/buildLargeCode';
  12. import { getRegExpMatch } from '../../helpers/getRegExpMatch';
  13. import { readFileAsString } from '../../helpers/readFileAsString';
  14. describe('JavaScriptObfuscator', () => {
  15. describe('obfuscate', () => {
  16. describe('correct source code', () => {
  17. let obfuscatedCode: string,
  18. sourceMap: string;
  19. beforeEach(() => {
  20. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  21. const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
  22. code,
  23. {
  24. ...NO_ADDITIONAL_NODES_PRESET
  25. }
  26. );
  27. obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
  28. sourceMap = obfuscatedCodeObject.getSourceMap();
  29. });
  30. it('should return correct obfuscated code', () => {
  31. assert.isOk(obfuscatedCode);
  32. });
  33. it('should return empty source map', () => {
  34. assert.isNotOk(sourceMap);
  35. });
  36. });
  37. describe('empty source code', () => {
  38. let obfuscatedCode: string;
  39. beforeEach(() => {
  40. const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
  41. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  42. code,
  43. ).getObfuscatedCode();
  44. });
  45. it('should return an empty obfuscated code', () => {
  46. assert.isNotOk(obfuscatedCode);
  47. });
  48. });
  49. describe('empty source code with comments', () => {
  50. let obfuscatedCode: string;
  51. beforeEach(() => {
  52. const code: string = readFileAsString(__dirname + '/fixtures/comments-only.js');
  53. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  54. code,
  55. {
  56. controlFlowFlattening: true,
  57. deadCodeInjection: true
  58. }
  59. ).getObfuscatedCode();
  60. });
  61. it('should return an empty obfuscated code', () => {
  62. assert.isNotOk(obfuscatedCode);
  63. });
  64. });
  65. describe('`sourceMap` option is `true`', () => {
  66. describe('`sourceMapMode` is `separate`', () => {
  67. let obfuscatedCode: string,
  68. sourceMap: string;
  69. beforeEach(() => {
  70. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  71. const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
  72. code,
  73. {
  74. ...NO_ADDITIONAL_NODES_PRESET,
  75. sourceMap: true
  76. }
  77. );
  78. obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
  79. sourceMap = JSON.parse(obfuscatedCodeObject.getSourceMap()).mappings;
  80. });
  81. it('should return correct obfuscated code', () => {
  82. assert.isOk(obfuscatedCode);
  83. });
  84. it('should return correct source map', () => {
  85. assert.isOk(sourceMap);
  86. });
  87. });
  88. describe('`sourceMapMode` is `inline`', () => {
  89. const regExp: RegExp = /sourceMappingURL=data:application\/json;base64/;
  90. let obfuscatedCode: string,
  91. sourceMap: string;
  92. beforeEach(() => {
  93. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  94. const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
  95. code,
  96. {
  97. ...NO_ADDITIONAL_NODES_PRESET,
  98. sourceMap: true,
  99. sourceMapMode: SourceMapMode.Inline
  100. }
  101. );
  102. obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
  103. sourceMap = JSON.parse(obfuscatedCodeObject.getSourceMap()).mappings;
  104. });
  105. it('should return correct obfuscated code', () => {
  106. assert.isOk(obfuscatedCode);
  107. });
  108. it('should return obfuscated code with inline source map as Base64 string', () => {
  109. assert.match(obfuscatedCode, regExp);
  110. });
  111. it('should return correct source map', () => {
  112. assert.isOk(sourceMap);
  113. });
  114. });
  115. describe('empty source code', () => {
  116. let obfuscatedCode: string,
  117. sourceMapNames: string[],
  118. sourceMapSources: string[],
  119. sourceMapMappings: string;
  120. beforeEach(() => {
  121. const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
  122. const obfuscatedCodeObject: IObfuscatedCode = JavaScriptObfuscator.obfuscate(
  123. code,
  124. {
  125. sourceMap: true
  126. }
  127. );
  128. obfuscatedCode = obfuscatedCodeObject.getObfuscatedCode();
  129. const sourceMapObject: any = JSON.parse(obfuscatedCodeObject.getSourceMap());
  130. sourceMapNames = sourceMapObject.names;
  131. sourceMapSources = sourceMapObject.sources;
  132. sourceMapMappings = sourceMapObject.mappings;
  133. });
  134. it('should return empty obfuscated code', () => {
  135. assert.isNotOk(obfuscatedCode);
  136. });
  137. it('should return empty source map property `names`', () => {
  138. assert.deepEqual(sourceMapNames, []);
  139. });
  140. it('should return empty source map property `sources`', () => {
  141. assert.deepEqual(sourceMapSources, []);
  142. });
  143. it('should return empty source map property `mappings`', () => {
  144. assert.isNotOk(sourceMapMappings);
  145. });
  146. });
  147. });
  148. describe('variable inside global scope', () => {
  149. describe('Variant #1: without `renameGlobals` option', () => {
  150. const regExp: RegExp = /^var test *= *0x\d+;$/;
  151. let obfuscatedCode: string;
  152. beforeEach(() => {
  153. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  154. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  155. code,
  156. {
  157. ...NO_ADDITIONAL_NODES_PRESET
  158. }
  159. ).getObfuscatedCode();
  160. });
  161. it('should return correct obfuscated code', () => {
  162. assert.match(obfuscatedCode, regExp);
  163. });
  164. });
  165. describe('Variant #2: with `renameGlobals` option', () => {
  166. const regExp: RegExp = /^var _0x(\w){4,6} *= *0x\d+;$/;
  167. let obfuscatedCode: string;
  168. beforeEach(() => {
  169. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  170. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  171. code,
  172. {
  173. ...NO_ADDITIONAL_NODES_PRESET,
  174. renameGlobals: true
  175. }
  176. ).getObfuscatedCode();
  177. });
  178. it('should return correct obfuscated code', () => {
  179. assert.match(obfuscatedCode, regExp);
  180. });
  181. });
  182. describe('Variant #3: with `renameGlobals` and `identifiersPrefix` options', () => {
  183. const regExp: RegExp = /^var foo_0x(\w){4,6} *= *0x\d+;$/;
  184. let obfuscatedCode: string;
  185. beforeEach(() => {
  186. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  187. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  188. code,
  189. {
  190. ...NO_ADDITIONAL_NODES_PRESET,
  191. renameGlobals: true,
  192. identifiersPrefix: 'foo'
  193. }
  194. ).getObfuscatedCode();
  195. });
  196. it('should return correct obfuscated code', () => {
  197. assert.match(obfuscatedCode, regExp);
  198. });
  199. });
  200. describe('Variant #4: with `stringArray`, `renameGlobals` and `identifiersPrefix` options', () => {
  201. const stringArrayRegExp: RegExp = /^var foo_0x(\w){4} *= *\['abc'\];/;
  202. const stringArrayCallRegExp: RegExp = /var foo_0x(\w){4,6} *= *foo_0x(\w){4}\('0x0'\);$/;
  203. let obfuscatedCode: string;
  204. beforeEach(() => {
  205. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-2.js');
  206. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  207. code,
  208. {
  209. ...NO_ADDITIONAL_NODES_PRESET,
  210. renameGlobals: true,
  211. identifiersPrefix: 'foo',
  212. stringArray: true,
  213. stringArrayThreshold: 1
  214. }
  215. ).getObfuscatedCode();
  216. });
  217. it('match #1: should return correct obfuscated code', () => {
  218. assert.match(obfuscatedCode, stringArrayRegExp);
  219. });
  220. it('match #2: should return correct obfuscated code', () => {
  221. assert.match(obfuscatedCode, stringArrayCallRegExp);
  222. });
  223. });
  224. });
  225. describe('variable inside block scope', () => {
  226. const regExp: RegExp = /^\(function *\(\) *\{ *var _0x[\w]+ *= *0x\d+; *\}(\(\)\)|\)\(\));?$/;
  227. let obfuscatedCode: string;
  228. beforeEach(() => {
  229. const code: string = readFileAsString(__dirname + '/fixtures/block-scope.js');
  230. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  231. code,
  232. {
  233. ...NO_ADDITIONAL_NODES_PRESET
  234. }
  235. ).getObfuscatedCode();
  236. });
  237. it('should return correct obfuscated code', () => {
  238. assert.match(obfuscatedCode, regExp);
  239. });
  240. });
  241. describe('variables inside global and block scopes', () => {
  242. describe('Variant #1: with `renameGlobals` and `identifiersPrefix` options', () => {
  243. const variableDeclaration1: RegExp = /var foo_0x(\w){4,6} *= *0x1;/;
  244. const variableDeclaration2: RegExp = /var foo_0x(\w){4,6} *= *0x2;/;
  245. const variableDeclaration3: RegExp = /var _0x(\w){4,6} *= *foo_0x(\w){4,6} *\+ *foo_0x(\w){4,6}/;
  246. const functionDeclaration: RegExp = /var foo_0x(\w){4,6} *= *function/;
  247. let obfuscatedCode: string;
  248. beforeEach(() => {
  249. const code: string = readFileAsString(__dirname + '/fixtures/identifiers-prefix.js');
  250. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  251. code,
  252. {
  253. ...NO_ADDITIONAL_NODES_PRESET,
  254. renameGlobals: true,
  255. identifiersPrefix: 'foo'
  256. }
  257. ).getObfuscatedCode();
  258. });
  259. it('match #1: should return correct obfuscated code', () => {
  260. assert.match(obfuscatedCode, variableDeclaration1);
  261. });
  262. it('match #2: should return correct obfuscated code', () => {
  263. assert.match(obfuscatedCode, variableDeclaration2);
  264. });
  265. it('match #3: should return correct obfuscated code', () => {
  266. assert.match(obfuscatedCode, variableDeclaration3);
  267. });
  268. it('match #4: should return correct obfuscated code', () => {
  269. assert.match(obfuscatedCode, functionDeclaration);
  270. });
  271. });
  272. });
  273. describe('latin literal variable value', () => {
  274. const stringArrayLatinRegExp: RegExp = /^var _0x(\w){4} *= *\['abc'\];/;
  275. const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\('0x0'\);$/;
  276. let obfuscatedCode: string;
  277. beforeEach(() => {
  278. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-2.js');
  279. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  280. code,
  281. {
  282. ...NO_ADDITIONAL_NODES_PRESET,
  283. stringArray: true,
  284. stringArrayThreshold: 1
  285. }
  286. ).getObfuscatedCode();
  287. });
  288. it('match #1: should return correct obfuscated code', () => {
  289. assert.match(obfuscatedCode, stringArrayLatinRegExp);
  290. });
  291. it('match #2: should return correct obfuscated code', () => {
  292. assert.match(obfuscatedCode, stringArrayCallRegExp);
  293. });
  294. });
  295. describe('cyrillic literal variable value', () => {
  296. const stringArrayCyrillicRegExp: RegExp = /^var _0x(\w){4} *= *\['абц'\];/;
  297. const stringArrayCallRegExp: RegExp = /var test *= *_0x(\w){4}\('0x0'\);$/;
  298. let obfuscatedCode: string;
  299. beforeEach(() => {
  300. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-cyrillic.js');
  301. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  302. code,
  303. {
  304. ...NO_ADDITIONAL_NODES_PRESET,
  305. stringArray: true,
  306. stringArrayThreshold: 1
  307. }
  308. ).getObfuscatedCode();
  309. });
  310. it('match #1: should return correct obfuscated code', () => {
  311. assert.match(obfuscatedCode, stringArrayCyrillicRegExp);
  312. });
  313. it('match #2: should return correct obfuscated code', () => {
  314. assert.match(obfuscatedCode, stringArrayCallRegExp);
  315. });
  316. });
  317. describe('seed', function () {
  318. this.timeout(60000);
  319. describe('same seed on each run', () => {
  320. const code: string = readFileAsString('./test/fixtures/sample.js');
  321. const samples: number = 100;
  322. let obfuscatedCode1: string,
  323. obfuscatedCode2: string,
  324. seed: number = 12345,
  325. equalsCount: number = 0;
  326. beforeEach(() => {
  327. for (let i: number = 0; i < samples; i++) {
  328. if (i % 20 === 0) {
  329. seed++;
  330. }
  331. obfuscatedCode1 = JavaScriptObfuscator.obfuscate(
  332. code,
  333. {
  334. seed: seed
  335. }
  336. ).getObfuscatedCode();
  337. obfuscatedCode2 = JavaScriptObfuscator.obfuscate(
  338. code,
  339. {
  340. seed: seed
  341. }
  342. ).getObfuscatedCode();
  343. if (obfuscatedCode1 === obfuscatedCode2) {
  344. equalsCount++;
  345. }
  346. }
  347. });
  348. it('should return same code every time with same `seed`', () => {
  349. assert.equal(equalsCount, samples);
  350. });
  351. });
  352. describe('Variant #1: different seed on each run', () => {
  353. const code: string = readFileAsString('./test/fixtures/sample.js');
  354. let obfuscatedCode1: string,
  355. obfuscatedCode2: string;
  356. beforeEach(() => {
  357. obfuscatedCode1 = JavaScriptObfuscator.obfuscate(
  358. code,
  359. {
  360. seed: 12345
  361. }
  362. ).getObfuscatedCode();
  363. obfuscatedCode2 = JavaScriptObfuscator.obfuscate(
  364. code,
  365. {
  366. seed: 12346
  367. }
  368. ).getObfuscatedCode();
  369. });
  370. it('should return different obfuscated code with different `seed` option value', () => {
  371. assert.notEqual(obfuscatedCode1, obfuscatedCode2);
  372. });
  373. });
  374. describe('Variant #2: different seed on each run', () => {
  375. const code: string = readFileAsString('./test/fixtures/sample.js');
  376. let obfuscatedCode1: string,
  377. obfuscatedCode2: string;
  378. beforeEach(() => {
  379. obfuscatedCode1 = JavaScriptObfuscator.obfuscate(
  380. code,
  381. {
  382. seed: 0
  383. }
  384. ).getObfuscatedCode();
  385. obfuscatedCode2 = JavaScriptObfuscator.obfuscate(
  386. code,
  387. {
  388. seed: 0
  389. }
  390. ).getObfuscatedCode();
  391. });
  392. it('should return different obfuscated code with different `seed` option value', () => {
  393. assert.notEqual(obfuscatedCode1, obfuscatedCode2);
  394. });
  395. });
  396. describe('Variant #3: same seed for different source code', () => {
  397. const code1: string = readFileAsString(__dirname + '/fixtures/simple-input-cyrillic.js');
  398. const code2: string = readFileAsString(__dirname + '/fixtures/simple-input-2.js');
  399. const regExp: RegExp = /var (_0x(\w){4}) *= *\['.*'\];/;
  400. let match1: string,
  401. match2: string;
  402. beforeEach(() => {
  403. const obfuscatedCode1: string = JavaScriptObfuscator.obfuscate(
  404. code1,
  405. {
  406. seed: 123,
  407. stringArrayThreshold: 1
  408. }
  409. ).getObfuscatedCode();
  410. const obfuscatedCode2: string = JavaScriptObfuscator.obfuscate(
  411. code2,
  412. {
  413. seed: 123,
  414. stringArrayThreshold: 1
  415. }
  416. ).getObfuscatedCode();
  417. match1 = getRegExpMatch(obfuscatedCode1, regExp);
  418. match2 = getRegExpMatch(obfuscatedCode2, regExp);
  419. });
  420. it('should return different String Array names for different source code with same seed', () => {
  421. assert.notEqual(match1, match2);
  422. });
  423. });
  424. });
  425. describe('new.target MetaProperty', () => {
  426. const regExp: RegExp = /new\.target *=== *Foo/;
  427. let obfuscatedCode: string;
  428. beforeEach(() => {
  429. const code: string = readFileAsString(__dirname + '/fixtures/new-target.js');
  430. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  431. code,
  432. {
  433. ...NO_ADDITIONAL_NODES_PRESET
  434. }
  435. ).getObfuscatedCode();
  436. });
  437. it('should keep new.target MetaProperty', () => {
  438. assert.match(obfuscatedCode, regExp);
  439. });
  440. });
  441. describe('import.meta support', () => {
  442. const regExp: RegExp = /console\['log']\(import\.meta\['url']\);/;
  443. let obfuscatedCode: string;
  444. beforeEach(() => {
  445. const code: string = readFileAsString(__dirname + '/fixtures/import-meta.js');
  446. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  447. code,
  448. {
  449. ...NO_ADDITIONAL_NODES_PRESET
  450. }
  451. ).getObfuscatedCode();
  452. });
  453. it('should support `import.meta`', () => {
  454. assert.match(obfuscatedCode, regExp);
  455. });
  456. });
  457. /**
  458. * https://github.com/estools/escodegen/pull/407
  459. */
  460. describe('valid exponentiation operator precedence', () => {
  461. const regExp: RegExp = /var foo *= *\( *0x1 *- *0x2 *\) *\*\* *0x2;/;
  462. let obfuscatedCode: string;
  463. beforeEach(() => {
  464. const code: string = readFileAsString(__dirname + '/fixtures/exponentiation-operator-precedence.js');
  465. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  466. code,
  467. {
  468. ...NO_ADDITIONAL_NODES_PRESET
  469. }
  470. ).getObfuscatedCode();
  471. });
  472. it('should support exponentiation operator', () => {
  473. assert.match(obfuscatedCode, regExp);
  474. });
  475. });
  476. describe('mangled identifier names generator', () => {
  477. const regExp: RegExp = /var c *= *0x1/;
  478. let obfuscatedCode: string;
  479. beforeEach(() => {
  480. const code: string = readFileAsString(__dirname + '/fixtures/mangle.js');
  481. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  482. code,
  483. {
  484. identifierNamesGenerator: IdentifierNamesGenerator.MangledIdentifierNamesGenerator
  485. }
  486. ).getObfuscatedCode();
  487. });
  488. it('should mangle obfuscated code', () => {
  489. assert.match(obfuscatedCode, regExp);
  490. });
  491. });
  492. describe('dictionary identifier names generator', () => {
  493. const regExp1: RegExp = /var [abc] *= *0x1; *var [abc] *= *0x2; *var [abc] *= *0x3;/;
  494. const regExp2: RegExp = /var [ABC] *= *0x4; *var [ABC] *= *0x5; *var [ABC] *= *0x6;/;
  495. let obfuscatedCode: string;
  496. beforeEach(() => {
  497. const code: string = readFileAsString(__dirname + '/fixtures/dictionary-identifiers.js');
  498. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  499. code,
  500. {
  501. ...NO_ADDITIONAL_NODES_PRESET,
  502. identifierNamesGenerator: IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
  503. identifiersDictionary: ['a', 'b', 'c']
  504. }
  505. ).getObfuscatedCode();
  506. });
  507. it('Match #1: should generate identifier based on the dictionary', () => {
  508. assert.match(obfuscatedCode, regExp1);
  509. });
  510. it('Match #2: should generate identifier based on the dictionary', () => {
  511. assert.match(obfuscatedCode, regExp2);
  512. });
  513. });
  514. describe('parse module', () => {
  515. describe('Variant #1: import', () => {
  516. const importRegExp: RegExp = /import *{foo} *from *'.\/foo';/;
  517. const variableDeclarationRegExp: RegExp = /var test *= *0x1/;
  518. let obfuscatedCode: string;
  519. beforeEach(() => {
  520. const code: string = readFileAsString(__dirname + '/fixtures/parse-module-1.js');
  521. obfuscatedCode = JavaScriptObfuscator.obfuscate(code).getObfuscatedCode();
  522. });
  523. it('Match #!: should correctly obfuscate a import', () => {
  524. assert.match(obfuscatedCode, importRegExp);
  525. });
  526. it('Match #2: should correctly obfuscate a module', () => {
  527. assert.match(obfuscatedCode, variableDeclarationRegExp);
  528. });
  529. });
  530. describe('Variant #2: export', () => {
  531. const regExp: RegExp = /export *const foo *= *0x1;/;
  532. let obfuscatedCode: string;
  533. beforeEach(() => {
  534. const code: string = readFileAsString(__dirname + '/fixtures/parse-module-2.js');
  535. obfuscatedCode = JavaScriptObfuscator.obfuscate(code).getObfuscatedCode();
  536. });
  537. it('should correctly obfuscate a module', () => {
  538. assert.match(obfuscatedCode, regExp);
  539. });
  540. });
  541. });
  542. describe('3.5k variables', function () {
  543. this.timeout(200000);
  544. const expectedValue: number = 3500;
  545. let result: number;
  546. beforeEach(() => {
  547. const code: string = buildLargeCode(expectedValue);
  548. const obfuscatedCode: string = JavaScriptObfuscator.obfuscate(
  549. code,
  550. {
  551. compact: true,
  552. controlFlowFlattening: true,
  553. controlFlowFlatteningThreshold: 1,
  554. deadCodeInjection: true,
  555. deadCodeInjectionThreshold: 1,
  556. disableConsoleOutput: false,
  557. rotateStringArray: true,
  558. stringArray: true,
  559. stringArrayEncoding: StringArrayEncoding.Rc4,
  560. stringArrayThreshold: 1,
  561. transformObjectKeys: true,
  562. unicodeEscapeSequence: false
  563. }
  564. ).getObfuscatedCode();
  565. result = eval(obfuscatedCode);
  566. });
  567. it('should correctly obfuscate 3.5k variables', () => {
  568. assert.equal(result, expectedValue);
  569. });
  570. });
  571. describe('Identifier names collision between base code and appended string array nodes', function () {
  572. this.timeout(10000);
  573. const samplesCount: number = 30;
  574. let areCollisionsExists: boolean = false;
  575. let obfuscateFunc: (identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>) => string;
  576. before(() => {
  577. const code: string = readFileAsString(__dirname + '/fixtures/custom-nodes-identifier-names-collision.js');
  578. obfuscateFunc = (identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>) => {
  579. const obfuscatedCode = JavaScriptObfuscator.obfuscate(
  580. code,
  581. {
  582. identifierNamesGenerator,
  583. compact: false,
  584. renameGlobals: true,
  585. identifiersDictionary: ['foo', 'bar', 'baz', 'bark', 'hawk', 'foozmos', 'cow', 'chikago'],
  586. stringArray: true
  587. }
  588. ).getObfuscatedCode();
  589. return obfuscatedCode;
  590. };
  591. [
  592. IdentifierNamesGenerator.DictionaryIdentifierNamesGenerator,
  593. IdentifierNamesGenerator.MangledIdentifierNamesGenerator
  594. ].forEach((identifierNamesGenerator: TypeFromEnum<typeof IdentifierNamesGenerator>) => {
  595. for (let i = 0; i < samplesCount; i++) {
  596. try {
  597. eval(obfuscateFunc(identifierNamesGenerator));
  598. } catch {
  599. areCollisionsExists = true;
  600. break;
  601. }
  602. }
  603. });
  604. });
  605. it('It does not create identifier names collision', () => {
  606. assert.equal(areCollisionsExists, false);
  607. });
  608. });
  609. describe('Prevailing kind of variables', () => {
  610. const baseParams: TInputOptions = {
  611. compact: true,
  612. controlFlowFlattening: true,
  613. controlFlowFlatteningThreshold: 1,
  614. deadCodeInjection: true,
  615. deadCodeInjectionThreshold: 1,
  616. debugProtection: true,
  617. debugProtectionInterval: true,
  618. disableConsoleOutput: false,
  619. rotateStringArray: true,
  620. selfDefending: true,
  621. stringArray: true,
  622. stringArrayThreshold: 1,
  623. transformObjectKeys: true,
  624. unicodeEscapeSequence: false
  625. };
  626. describe('`var` kind', function () {
  627. let obfuscatedCode: string;
  628. before(() => {
  629. const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-var.js');
  630. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  631. code,
  632. {
  633. ...NO_ADDITIONAL_NODES_PRESET,
  634. ...baseParams,
  635. stringArrayEncoding: StringArrayEncoding.Rc4
  636. }
  637. ).getObfuscatedCode();
  638. });
  639. it('does not break on run', () => {
  640. assert.doesNotThrow(() => eval(obfuscatedCode));
  641. });
  642. });
  643. describe('`const` kind', function () {
  644. describe('Variant #1: StringArrayEncoding: rc4', () => {
  645. let obfuscatedCode: string;
  646. before(() => {
  647. const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
  648. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  649. code,
  650. {
  651. ...NO_ADDITIONAL_NODES_PRESET,
  652. ...baseParams,
  653. stringArrayEncoding: StringArrayEncoding.Rc4
  654. }
  655. ).getObfuscatedCode();
  656. });
  657. it('does not break on run', () => {
  658. assert.doesNotThrow(() => eval(obfuscatedCode));
  659. });
  660. });
  661. describe('Variant #2: StringArrayEncoding: base64', () => {
  662. let obfuscatedCode: string;
  663. before(() => {
  664. const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-const.js');
  665. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  666. code,
  667. {
  668. ...NO_ADDITIONAL_NODES_PRESET,
  669. ...baseParams,
  670. stringArrayEncoding: StringArrayEncoding.Rc4
  671. }
  672. ).getObfuscatedCode();
  673. });
  674. it('does not break on run', () => {
  675. assert.doesNotThrow(() => eval(obfuscatedCode));
  676. });
  677. });
  678. });
  679. describe('`let` kind', function () {
  680. describe('Variant #1: StringArrayEncoding: rc4', () => {
  681. let obfuscatedCode: string;
  682. before(() => {
  683. const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
  684. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  685. code,
  686. {
  687. ...NO_ADDITIONAL_NODES_PRESET,
  688. ...baseParams,
  689. stringArrayEncoding: StringArrayEncoding.Rc4
  690. }
  691. ).getObfuscatedCode();
  692. });
  693. it('does not break on run', () => {
  694. assert.doesNotThrow(() => eval(obfuscatedCode));
  695. });
  696. });
  697. describe('Variant #2: StringArrayEncoding: base64', () => {
  698. let obfuscatedCode: string;
  699. before(() => {
  700. const code: string = readFileAsString(__dirname + '/fixtures/prevailing-kind-of-variables-let.js');
  701. obfuscatedCode = JavaScriptObfuscator.obfuscate(
  702. code,
  703. {
  704. ...NO_ADDITIONAL_NODES_PRESET,
  705. ...baseParams,
  706. stringArrayEncoding: StringArrayEncoding.Base64
  707. }
  708. ).getObfuscatedCode();
  709. });
  710. it('does not break on run', () => {
  711. assert.doesNotThrow(() => eval(obfuscatedCode));
  712. });
  713. });
  714. });
  715. });
  716. });
  717. describe('obfuscateMultiple', () => {
  718. describe('multiple source codes', () => {
  719. const regExp1: RegExp = /var _0x(\w){4,6} *= *0x1;/;
  720. const regExp2: RegExp = /var _0x(\w){4,6} *= *'abc';/;
  721. let obfuscatedCode1: string;
  722. let obfuscatedCode2: string;
  723. beforeEach(() => {
  724. const sourceCode1: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  725. const sourceCode2: string = readFileAsString(__dirname + '/fixtures/simple-input-2.js');
  726. const obfuscationResultsObject = JavaScriptObfuscator.obfuscateMultiple(
  727. {
  728. sourceCode1,
  729. sourceCode2
  730. },
  731. {
  732. ...NO_ADDITIONAL_NODES_PRESET,
  733. renameGlobals: true
  734. }
  735. );
  736. obfuscatedCode1 = obfuscationResultsObject.sourceCode1.getObfuscatedCode();
  737. obfuscatedCode2 = obfuscationResultsObject.sourceCode2.getObfuscatedCode();
  738. });
  739. it('Match #1: should return correct obfuscated code', () => {
  740. assert.match(obfuscatedCode1, regExp1);
  741. });
  742. it('Match #2: should return correct obfuscated code', () => {
  743. assert.match(obfuscatedCode2, regExp2);
  744. });
  745. });
  746. describe('invalid source codes object', () => {
  747. let testFunc: () => TObject<IObfuscatedCode>;
  748. beforeEach(() => {
  749. testFunc = () => JavaScriptObfuscator.obfuscateMultiple(
  750. 'foo' as any,
  751. {
  752. ...NO_ADDITIONAL_NODES_PRESET,
  753. renameGlobals: true
  754. }
  755. );
  756. });
  757. it('Should throw an error if source codes object is not a plain object', () => {
  758. assert.throw(testFunc, Error);
  759. });
  760. });
  761. });
  762. });