JavaScriptObfuscator.spec.ts 43 KB

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