JavaScriptObfuscator.spec.ts 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. import { assert } from 'chai';
  2. import { IObfuscationResult } from '../../../src/interfaces/IObfuscationResult';
  3. import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscator';
  4. import { NO_CUSTOM_NODES_PRESET } from '../../../src/options/presets/NoCustomNodes';
  5. import { readFileAsString } from '../../helpers/readFileAsString';
  6. import { RandomGeneratorUtils } from '../../../src/utils/RandomGeneratorUtils';
  7. describe('JavaScriptObfuscator', () => {
  8. describe('obfuscate (sourceCode: string, customOptions?: IObfuscatorOptions): IObfuscationResult', () => {
  9. beforeEach(() => {
  10. RandomGeneratorUtils.initializeRandomGenerator(0);
  11. });
  12. describe('correct source code', () => {
  13. let obfuscatedCode: string,
  14. sourceMap: string;
  15. beforeEach(() => {
  16. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  17. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  18. code,
  19. {
  20. ...NO_CUSTOM_NODES_PRESET
  21. }
  22. );
  23. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  24. sourceMap = obfuscationResult.getSourceMap();
  25. });
  26. it('should return correct obfuscated code', () => {
  27. assert.isOk(obfuscatedCode);
  28. });
  29. it('should return empty source map', () => {
  30. assert.isNotOk(sourceMap);
  31. });
  32. });
  33. describe('empty source code', () => {
  34. let obfuscatedCode: string;
  35. beforeEach(() => {
  36. const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
  37. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  38. code,
  39. );
  40. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  41. });
  42. it('should return an empty obfuscated code', () => {
  43. assert.isNotOk(obfuscatedCode);
  44. });
  45. });
  46. describe('empty source code with comments', () => {
  47. let obfuscatedCode: string;
  48. beforeEach(() => {
  49. const code: string = readFileAsString(__dirname + '/fixtures/comments-only.js');
  50. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  51. code,
  52. );
  53. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  54. });
  55. it('should return an empty obfuscated code', () => {
  56. assert.isNotOk(obfuscatedCode);
  57. });
  58. });
  59. describe('`sourceMap` option is `true`', () => {
  60. describe('`sourceMapMode` is `separate`', () => {
  61. let obfuscatedCode: string,
  62. sourceMap: string;
  63. beforeEach(() => {
  64. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  65. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  66. code,
  67. {
  68. ...NO_CUSTOM_NODES_PRESET,
  69. sourceMap: true
  70. }
  71. );
  72. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  73. sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
  74. });
  75. it('should return correct obfuscated code', () => {
  76. assert.isOk(obfuscatedCode);
  77. });
  78. it('should return correct source map', () => {
  79. assert.isOk(sourceMap);
  80. });
  81. });
  82. describe('`sourceMapMode` is `inline`', () => {
  83. const regExp: RegExp = /sourceMappingURL=data:application\/json;base64/;
  84. let obfuscatedCode: string,
  85. sourceMap: string;
  86. beforeEach(() => {
  87. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  88. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  89. code,
  90. {
  91. ...NO_CUSTOM_NODES_PRESET,
  92. sourceMap: true,
  93. sourceMapMode: 'inline'
  94. }
  95. );
  96. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  97. sourceMap = JSON.parse(obfuscationResult.getSourceMap()).mappings;
  98. });
  99. it('should return correct obfuscated code', () => {
  100. assert.isOk(obfuscatedCode);
  101. });
  102. it('should return obfuscated code with inline source map as Base64 string', () => {
  103. assert.match(obfuscatedCode, regExp);
  104. });
  105. it('should return correct source map', () => {
  106. assert.isOk(sourceMap);
  107. });
  108. });
  109. describe('empty source code', () => {
  110. let obfuscatedCode: string,
  111. sourceMapNames: string[],
  112. sourceMapSources: string[],
  113. sourceMapMappings: string;
  114. beforeEach(() => {
  115. const code: string = readFileAsString(__dirname + '/fixtures/empty-input.js');
  116. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  117. code,
  118. {
  119. sourceMap: true
  120. }
  121. );
  122. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  123. const sourceMapObject: any = JSON.parse(obfuscationResult.getSourceMap());
  124. sourceMapNames = sourceMapObject.names;
  125. sourceMapSources = sourceMapObject.sources;
  126. sourceMapMappings = sourceMapObject.mappings;
  127. });
  128. it('should return empty obfuscated code', () => {
  129. assert.isNotOk(obfuscatedCode);
  130. });
  131. it('should return empty source map property `names`', () => {
  132. assert.deepEqual(sourceMapNames, []);
  133. });
  134. it('should return empty source map property `sources`', () => {
  135. assert.deepEqual(sourceMapSources, []);
  136. });
  137. it('should return empty source map property `mappings`', () => {
  138. assert.isNotOk(sourceMapMappings);
  139. });
  140. });
  141. });
  142. describe('variable inside global scope', () => {
  143. const regExp: RegExp = /^var *test *= *0x\d+;$/;
  144. let obfuscatedCode: string;
  145. beforeEach(() => {
  146. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-1.js');
  147. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  148. code,
  149. {
  150. ...NO_CUSTOM_NODES_PRESET
  151. }
  152. );
  153. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  154. });
  155. it('should return correct obfuscated code', () => {
  156. assert.match(obfuscatedCode, regExp);
  157. });
  158. });
  159. describe('variable inside global scope', () => {
  160. const regExp: RegExp = /^\(function *\(\) *\{ *var *_0x[\w]+ *= *0x\d+; *\}(\(\)\)|\)\(\));?$/;
  161. let obfuscatedCode: string;
  162. beforeEach(() => {
  163. const code: string = readFileAsString(__dirname + '/fixtures/block-scope.js');
  164. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  165. code,
  166. {
  167. ...NO_CUSTOM_NODES_PRESET
  168. }
  169. );
  170. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  171. });
  172. it('should return correct obfuscated code', () => {
  173. assert.match(obfuscatedCode, regExp);
  174. });
  175. });
  176. describe('latin literal variable value', () => {
  177. const stringArrayLatinRegExp: RegExp = /^var _0x(\w){4} *= *\['abc'\];/;
  178. const stringArrayCallRegExp: RegExp = /var *test *= *_0x(\w){4}\('0x0'\);$/;
  179. let obfuscatedCode: string;
  180. beforeEach(() => {
  181. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-2.js');
  182. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  183. code,
  184. {
  185. ...NO_CUSTOM_NODES_PRESET,
  186. stringArray: true,
  187. stringArrayThreshold: 1
  188. }
  189. );
  190. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  191. });
  192. it('match #1: should return correct obfuscated code', () => {
  193. assert.match(obfuscatedCode, stringArrayLatinRegExp);
  194. });
  195. it('match #2: should return correct obfuscated code', () => {
  196. assert.match(obfuscatedCode, stringArrayCallRegExp);
  197. });
  198. });
  199. describe('cyrillic literal variable value', () => {
  200. const stringArrayCyrillicRegExp: RegExp = /^var _0x(\w){4} *= *\['(\\u\d+)+'\];/;
  201. const stringArrayCallRegExp: RegExp = /var *test *= *_0x(\w){4}\('0x0'\);$/;
  202. let obfuscatedCode: string;
  203. beforeEach(() => {
  204. const code: string = readFileAsString(__dirname + '/fixtures/simple-input-cyrillic.js');
  205. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  206. code,
  207. {
  208. ...NO_CUSTOM_NODES_PRESET,
  209. stringArray: true,
  210. stringArrayThreshold: 1
  211. }
  212. );
  213. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  214. });
  215. it('match #1: should return correct obfuscated code', () => {
  216. assert.match(obfuscatedCode, stringArrayCyrillicRegExp);
  217. });
  218. it('match #2: should return correct obfuscated code', () => {
  219. assert.match(obfuscatedCode, stringArrayCallRegExp);
  220. });
  221. });
  222. describe('seed', function () {
  223. this.timeout(60000);
  224. describe('same seed on each run', () => {
  225. const code: string = readFileAsString('./test/fixtures/sample.js');
  226. const samples: number = 100;
  227. let obfuscatedCode1: string,
  228. obfuscatedCode2: string,
  229. seed: number = 12345,
  230. equalsCount: number = 0;
  231. beforeEach(() => {
  232. for (let i: number = 0; i < samples; i++) {
  233. if (i % 20 === 0) {
  234. seed++;
  235. }
  236. const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  237. code,
  238. {
  239. seed: seed
  240. }
  241. );
  242. const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  243. code,
  244. {
  245. seed: seed
  246. }
  247. );
  248. obfuscatedCode1 = obfuscationResult1.getObfuscatedCode();
  249. obfuscatedCode2 = obfuscationResult2.getObfuscatedCode();
  250. if (obfuscatedCode1 === obfuscatedCode2) {
  251. equalsCount++;
  252. }
  253. }
  254. });
  255. it('should return same code every time with same `seed`', () => {
  256. assert.equal(equalsCount, samples);
  257. });
  258. });
  259. describe('variant #1: different seed on each run', () => {
  260. const code: string = readFileAsString('./test/fixtures/sample.js');
  261. let obfuscatedCode1: string,
  262. obfuscatedCode2: string;
  263. beforeEach(() => {
  264. const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  265. code,
  266. {
  267. seed: 12345
  268. }
  269. );
  270. const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  271. code,
  272. {
  273. seed: 12346
  274. }
  275. );
  276. obfuscatedCode1 = obfuscationResult1.getObfuscatedCode();
  277. obfuscatedCode2 = obfuscationResult2.getObfuscatedCode();
  278. });
  279. it('should return different obfuscated code with different `seed` option value', () => {
  280. assert.notEqual(obfuscatedCode1, obfuscatedCode2);
  281. });
  282. });
  283. describe('variant #2: different seed on each run', () => {
  284. const code: string = readFileAsString('./test/fixtures/sample.js');
  285. let obfuscatedCode1: string,
  286. obfuscatedCode2: string;
  287. beforeEach(() => {
  288. const obfuscationResult1: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  289. code,
  290. {
  291. seed: 0
  292. }
  293. );
  294. const obfuscationResult2: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  295. code,
  296. {
  297. seed: 0
  298. }
  299. );
  300. obfuscatedCode1 = obfuscationResult1.getObfuscatedCode();
  301. obfuscatedCode2 = obfuscationResult2.getObfuscatedCode();
  302. });
  303. it('should return different obfuscated code with different `seed` option value', () => {
  304. assert.notEqual(obfuscatedCode1, obfuscatedCode2);
  305. });
  306. });
  307. });
  308. describe('new.target MetaProperty', () => {
  309. const regExp: RegExp = /new\.target *=== *Foo/;
  310. let obfuscatedCode: string;
  311. beforeEach(() => {
  312. const code: string = readFileAsString(__dirname + '/fixtures/new-target.js');
  313. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  314. code,
  315. {
  316. ...NO_CUSTOM_NODES_PRESET
  317. }
  318. );
  319. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  320. });
  321. it('should keep new.target MetaProperty', () => {
  322. assert.match(obfuscatedCode, regExp);
  323. });
  324. });
  325. describe('mangle', () => {
  326. const regExp: RegExp = /var *a *= *0x1/;
  327. let obfuscatedCode: string;
  328. beforeEach(() => {
  329. const code: string = readFileAsString(__dirname + '/fixtures/mangle.js');
  330. const obfuscationResult: IObfuscationResult = JavaScriptObfuscator.obfuscate(
  331. code,
  332. {
  333. mangle: true
  334. }
  335. );
  336. obfuscatedCode = obfuscationResult.getObfuscatedCode();
  337. });
  338. it('should mangle obfuscated code', () => {
  339. assert.match(obfuscatedCode, regExp);
  340. });
  341. });
  342. afterEach(() => {
  343. RandomGeneratorUtils.initializeRandomGenerator(0);
  344. });
  345. });
  346. });