JavaScriptObfuscator.spec.ts 39 KB

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