JavaScriptObfuscatorCLI.spec.ts 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662
  1. import * as fs from 'fs';
  2. import * as mkdirp from 'mkdirp';
  3. import * as rimraf from 'rimraf';
  4. import * as sinon from 'sinon';
  5. import { assert } from 'chai';
  6. import { StdoutWriteMock } from '../../mocks/StdoutWriteMock';
  7. import { JavaScriptObfuscator } from '../../../src/JavaScriptObfuscatorFacade';
  8. describe('JavaScriptObfuscatorCLI', function (): void {
  9. this.timeout(100000);
  10. const fixturesDirName: string = 'test/fixtures';
  11. const fixtureFileName: string = 'sample.js';
  12. const fixtureFilePath: string = `${fixturesDirName}/${fixtureFileName}`;
  13. const outputDirName: string = 'test/tmp';
  14. const outputFileName: string = 'sample-obfuscated.js';
  15. const outputFilePath: string = `${outputDirName}/${outputFileName}`;
  16. const configDirName: string = 'test/fixtures';
  17. const configFileName: string = 'config.js';
  18. const configFilePath: string = `${configDirName}/${configFileName}`;
  19. describe('run (): void', () => {
  20. before(() => {
  21. mkdirp.sync(outputDirName);
  22. });
  23. describe('Variant #1: obfuscation of single file', () => {
  24. describe('`--output` option is set', () => {
  25. let isFileExist: boolean;
  26. before(() => {
  27. JavaScriptObfuscator.runCLI([
  28. 'node',
  29. 'javascript-obfuscator',
  30. fixtureFilePath,
  31. '--output',
  32. outputFilePath,
  33. '--compact',
  34. 'true',
  35. '--self-defending',
  36. '0'
  37. ]);
  38. isFileExist = fs.existsSync(outputFilePath);
  39. });
  40. it('should create file with obfuscated code in `--output` directory', () => {
  41. assert.equal(isFileExist, true);
  42. });
  43. after(() => {
  44. fs.unlinkSync(outputFilePath);
  45. });
  46. });
  47. describe('`--output` option isn\'t set', () => {
  48. describe('variant #1: default behaviour', () => {
  49. let outputFixturesFilePath: string,
  50. isFileExist: boolean;
  51. before(() => {
  52. outputFixturesFilePath = `${fixturesDirName}/${outputFileName}`;
  53. JavaScriptObfuscator.runCLI([
  54. 'node',
  55. 'javascript-obfuscator',
  56. fixtureFilePath
  57. ]);
  58. isFileExist = fs.existsSync(outputFixturesFilePath);
  59. });
  60. it(`should create file \`${outputFileName}\` with obfuscated code in \`${fixturesDirName}\` directory`, () => {
  61. assert.equal(isFileExist, true);
  62. });
  63. after(() => {
  64. fs.unlinkSync(outputFixturesFilePath);
  65. });
  66. });
  67. describe('variant #2: invalid input file path', () => {
  68. const expectedError: ReferenceErrorConstructor = ReferenceError;
  69. let testFunc: () => void;
  70. before(() => {
  71. testFunc = () => JavaScriptObfuscator.runCLI([
  72. 'node',
  73. 'javascript-obfuscator',
  74. 'wrong/file/path'
  75. ]);
  76. });
  77. it(`should throw an error`, () => {
  78. assert.throws(testFunc, expectedError);
  79. });
  80. });
  81. describe('variant #3: input file extension isn\'t `.js`', () => {
  82. const expectedError: ReferenceErrorConstructor = ReferenceError;
  83. const outputFileName: string = 'sample-obfuscated.ts';
  84. const outputFilePath: string = `${outputDirName}/${outputFileName}`;
  85. let testFunc: () => void;
  86. before(() => {
  87. fs.writeFileSync(outputFilePath, 'data');
  88. testFunc = () => JavaScriptObfuscator.runCLI([
  89. 'node',
  90. 'javascript-obfuscator',
  91. outputFilePath
  92. ]);
  93. });
  94. it(`should throw an error`, () => {
  95. assert.throws(testFunc, expectedError);
  96. });
  97. after(() => {
  98. fs.unlinkSync(outputFilePath);
  99. });
  100. });
  101. });
  102. });
  103. describe('Variant #2: obfuscation of directory', () => {
  104. const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
  105. const outputFileName1: string = 'foo-obfuscated.js';
  106. const outputFileName2: string = 'bar-obfuscated.js';
  107. const outputFileName3: string = 'baz-obfuscated.js';
  108. const readFileEncoding: string = 'utf8';
  109. const regExp1: RegExp = /^var *a1_0x(\w){4,6} *= *0x1;$/;
  110. const regExp2: RegExp = /^var *a0_0x(\w){4,6} *= *0x2;$/;
  111. let outputFixturesFilePath1: string,
  112. outputFixturesFilePath2: string,
  113. outputFixturesFilePath3: string,
  114. isFileExist1: boolean,
  115. isFileExist2: boolean,
  116. isFileExist3: boolean,
  117. fileContent1: string,
  118. fileContent2: string;
  119. before(() => {
  120. outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
  121. outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
  122. outputFixturesFilePath3 = `${directoryPath}/${outputFileName3}`;
  123. JavaScriptObfuscator.runCLI([
  124. 'node',
  125. 'javascript-obfuscator',
  126. directoryPath,
  127. '--rename-globals',
  128. 'true'
  129. ]);
  130. isFileExist1 = fs.existsSync(outputFixturesFilePath1);
  131. isFileExist2 = fs.existsSync(outputFixturesFilePath2);
  132. isFileExist3 = fs.existsSync(outputFixturesFilePath3);
  133. fileContent1 = fs.readFileSync(outputFixturesFilePath1, readFileEncoding);
  134. fileContent2 = fs.readFileSync(outputFixturesFilePath2, readFileEncoding);
  135. });
  136. it(`should create file \`${outputFileName1}\` with obfuscated code in \`${fixturesDirName}\` directory`, () => {
  137. assert.equal(isFileExist1, true);
  138. });
  139. it(`should create file \`${outputFileName2}\` with obfuscated code in \`${fixturesDirName}\` directory`, () => {
  140. assert.equal(isFileExist2, true);
  141. });
  142. it(`shouldn't create file \`${outputFileName3}\` in \`${fixturesDirName}\` directory`, () => {
  143. assert.equal(isFileExist3, false);
  144. });
  145. it(`match #1: should create file with obfuscated code with prefixed identifier`, () => {
  146. assert.match(fileContent1, regExp1);
  147. });
  148. it(`match #2: should create file with obfuscated code with prefixed identifier`, () => {
  149. assert.match(fileContent2, regExp2);
  150. });
  151. after(() => {
  152. rimraf.sync(outputFixturesFilePath1);
  153. rimraf.sync(outputFixturesFilePath2);
  154. });
  155. });
  156. describe('Variant #3: obfuscation of directory with `identifiersPrefix` option value', () => {
  157. const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
  158. const identifiersPrefix: string = 'foo';
  159. const outputFileName1: string = 'foo-obfuscated.js';
  160. const outputFileName2: string = 'bar-obfuscated.js';
  161. const readFileEncoding: string = 'utf8';
  162. const regExp1: RegExp = /^var *foo1_0x(\w){4,6} *= *0x1;$/;
  163. const regExp2: RegExp = /^var *foo0_0x(\w){4,6} *= *0x2;$/;
  164. let outputFixturesFilePath1: string,
  165. outputFixturesFilePath2: string,
  166. isFileExist1: boolean,
  167. isFileExist2: boolean,
  168. fileContent1: string,
  169. fileContent2: string;
  170. before(() => {
  171. outputFixturesFilePath1 = `${directoryPath}/${outputFileName1}`;
  172. outputFixturesFilePath2 = `${directoryPath}/${outputFileName2}`;
  173. JavaScriptObfuscator.runCLI([
  174. 'node',
  175. 'javascript-obfuscator',
  176. directoryPath,
  177. '--identifiers-prefix',
  178. identifiersPrefix,
  179. '--rename-globals',
  180. 'true'
  181. ]);
  182. isFileExist1 = fs.existsSync(outputFixturesFilePath1);
  183. isFileExist2 = fs.existsSync(outputFixturesFilePath2);
  184. fileContent1 = fs.readFileSync(outputFixturesFilePath1, readFileEncoding);
  185. fileContent2 = fs.readFileSync(outputFixturesFilePath2, readFileEncoding);
  186. });
  187. it(`should create file \`${outputFileName1}\` with obfuscated code in \`${fixturesDirName}\` directory`, () => {
  188. assert.equal(isFileExist1, true);
  189. });
  190. it(`should create file \`${outputFileName2}\` with obfuscated code in \`${fixturesDirName}\` directory`, () => {
  191. assert.equal(isFileExist2, true);
  192. });
  193. it(`match #1: should create file with obfuscated code with prefixed identifier`, () => {
  194. assert.match(fileContent1, regExp1);
  195. });
  196. it(`match #2: should create file with obfuscated code with prefixed identifier`, () => {
  197. assert.match(fileContent2, regExp2);
  198. });
  199. after(() => {
  200. rimraf.sync(outputFixturesFilePath1);
  201. rimraf.sync(outputFixturesFilePath2);
  202. });
  203. });
  204. describe('Variant #4: obfuscation of directory with `output` option', () => {
  205. const directoryPath: string = `${fixturesDirName}/directory-obfuscation`;
  206. const outputDirectoryName: string = 'obfuscated';
  207. const outputDirectoryPath: string = `${directoryPath}/${outputDirectoryName}`;
  208. const outputFileName1: string = 'foo.js';
  209. const outputFileName2: string = 'bar.js';
  210. const outputFileName3: string = 'baz.js';
  211. let outputFixturesFilePath1: string,
  212. outputFixturesFilePath2: string,
  213. outputFixturesFilePath3: string,
  214. isFileExist1: boolean,
  215. isFileExist2: boolean,
  216. isFileExist3: boolean;
  217. before(() => {
  218. outputFixturesFilePath1 = `${outputDirectoryPath}/${directoryPath}/${outputFileName1}`;
  219. outputFixturesFilePath2 = `${outputDirectoryPath}/${directoryPath}/${outputFileName2}`;
  220. outputFixturesFilePath3 = `${outputDirectoryPath}/${directoryPath}/${outputFileName3}`;
  221. JavaScriptObfuscator.runCLI([
  222. 'node',
  223. 'javascript-obfuscator',
  224. directoryPath,
  225. '--output',
  226. outputDirectoryPath
  227. ]);
  228. isFileExist1 = fs.existsSync(outputFixturesFilePath1);
  229. isFileExist2 = fs.existsSync(outputFixturesFilePath2);
  230. isFileExist3 = fs.existsSync(outputFixturesFilePath3);
  231. });
  232. it(
  233. `should create file \`${outputFileName1}\` with obfuscated code in ` +
  234. `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
  235. () => {
  236. assert.equal(isFileExist1, true);
  237. }
  238. );
  239. it(
  240. `should create file \`${outputFileName2}\` with obfuscated code in ` +
  241. `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
  242. () => {
  243. assert.equal(isFileExist2, true);
  244. }
  245. );
  246. it(
  247. `shouldn't create file \`${outputFileName3}\` in ` +
  248. `\`${fixturesDirName}/${outputDirectoryName}\` directory`,
  249. () => {
  250. assert.equal(isFileExist3, false);
  251. }
  252. );
  253. after(() => {
  254. rimraf.sync(outputDirectoryPath);
  255. });
  256. });
  257. describe('`--sourceMap` option is set', () => {
  258. const outputSourceMapPath: string = `${outputFilePath}.map`;
  259. describe('variant #1: `--sourceMapMode` option value is `separate`', () => {
  260. describe('variant #1: default behaviour', () => {
  261. let isFileExist: boolean,
  262. sourceMapObject: any;
  263. before(() => {
  264. JavaScriptObfuscator.runCLI([
  265. 'node',
  266. 'javascript-obfuscator',
  267. fixtureFilePath,
  268. '--output',
  269. outputFilePath,
  270. '--compact',
  271. 'true',
  272. '--self-defending',
  273. '0',
  274. '--source-map',
  275. 'true'
  276. ]);
  277. try {
  278. const content: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
  279. isFileExist = true;
  280. sourceMapObject = JSON.parse(content);
  281. } catch (e) {
  282. isFileExist = false;
  283. }
  284. });
  285. it('should create file with source map in the same directory as output file', () => {
  286. assert.equal(isFileExist, true);
  287. });
  288. it('source map from created file should contains property `version`', () => {
  289. assert.property(sourceMapObject, 'version');
  290. });
  291. it('source map from created file should contains property `sources`', () => {
  292. assert.property(sourceMapObject, 'sources');
  293. });
  294. it('source map from created file should contains property `names`', () => {
  295. assert.property(sourceMapObject, 'names');
  296. });
  297. after(() => {
  298. rimraf.sync(outputFilePath);
  299. rimraf.sync(outputSourceMapPath);
  300. });
  301. });
  302. describe('variant #2: `sourceMapBaseUrl` option is set', () => {
  303. let isFileExist: boolean,
  304. sourceMapObject: any;
  305. before(() => {
  306. JavaScriptObfuscator.runCLI([
  307. 'node',
  308. 'javascript-obfuscator',
  309. fixtureFilePath,
  310. '--output',
  311. outputFilePath,
  312. '--compact',
  313. 'true',
  314. '--self-defending',
  315. '0',
  316. '--source-map',
  317. 'true',
  318. '--source-map-base-url',
  319. 'http://localhost:9000/'
  320. ]);
  321. try {
  322. const content: string = fs.readFileSync(outputSourceMapPath, { encoding: 'utf8' });
  323. isFileExist = true;
  324. sourceMapObject = JSON.parse(content);
  325. } catch (e) {
  326. isFileExist = false;
  327. }
  328. });
  329. it('should create file with source map in the same directory as output file', () => {
  330. assert.equal(isFileExist, true);
  331. });
  332. it('source map from created file should contains property `version`', () => {
  333. assert.property(sourceMapObject, 'version');
  334. });
  335. it('source map from created file should contains property `sources`', () => {
  336. assert.property(sourceMapObject, 'sources');
  337. });
  338. it('source map from created file should contains property `names`', () => {
  339. assert.property(sourceMapObject, 'names');
  340. });
  341. after(() => {
  342. fs.unlinkSync(outputFilePath);
  343. fs.unlinkSync(outputSourceMapPath);
  344. });
  345. });
  346. describe('variant #3: `--sourceMapFileName` option is set', () => {
  347. const sourceMapFileName: string = 'test';
  348. const sourceMapFilePath: string = `${sourceMapFileName}.js.map`;
  349. const outputSourceMapFilePath: string = `${outputDirName}/${sourceMapFilePath}`;
  350. let isFileExist: boolean,
  351. sourceMapObject: any;
  352. before(() => {
  353. JavaScriptObfuscator.runCLI([
  354. 'node',
  355. 'javascript-obfuscator',
  356. fixtureFilePath,
  357. '--output',
  358. outputFilePath,
  359. '--compact',
  360. 'true',
  361. '--self-defending',
  362. '0',
  363. '--source-map',
  364. 'true',
  365. '--source-map-file-name',
  366. sourceMapFileName
  367. ]);
  368. try {
  369. const content: string = fs.readFileSync(outputSourceMapFilePath, { encoding: 'utf8' });
  370. isFileExist = true;
  371. sourceMapObject = JSON.parse(content);
  372. } catch (e) {
  373. isFileExist = false;
  374. }
  375. });
  376. it('should create source map file with given name in the same directory as output file', () => {
  377. assert.equal(isFileExist, true);
  378. });
  379. it('source map from created file should contains property `version`', () => {
  380. assert.property(sourceMapObject, 'version');
  381. });
  382. it('source map from created file should contains property `sources`', () => {
  383. assert.property(sourceMapObject, 'sources');
  384. });
  385. it('source map from created file should contains property `names`', () => {
  386. assert.property(sourceMapObject, 'names');
  387. });
  388. after(() => {
  389. fs.unlinkSync(outputFilePath);
  390. fs.unlinkSync(outputSourceMapFilePath);
  391. });
  392. });
  393. });
  394. describe('variant #2: `--sourceMapMode` option is `inline`', () => {
  395. let isFileExist: boolean;
  396. before(() => {
  397. JavaScriptObfuscator.runCLI([
  398. 'node',
  399. 'javascript-obfuscator',
  400. fixtureFilePath,
  401. '--output',
  402. outputFilePath,
  403. '--compact',
  404. 'true',
  405. '--self-defending',
  406. '0',
  407. '--source-map',
  408. 'true',
  409. '--source-map-mode',
  410. 'inline'
  411. ]);
  412. isFileExist = fs.existsSync(outputSourceMapPath);
  413. });
  414. it('shouldn\'t create file with source map', () => {
  415. assert.equal(isFileExist, false);
  416. });
  417. after(() => {
  418. fs.unlinkSync(outputFilePath);
  419. });
  420. });
  421. });
  422. describe('help output', () => {
  423. let callback: sinon.SinonSpy,
  424. stdoutWriteMock: StdoutWriteMock;
  425. beforeEach(() => {
  426. callback = sinon.spy(console, 'log');
  427. stdoutWriteMock = new StdoutWriteMock(process.stdout.write);
  428. });
  429. describe('`--help` option is set', () => {
  430. let isConsoleLogCalled: boolean;
  431. beforeEach(() => {
  432. stdoutWriteMock.mute();
  433. JavaScriptObfuscator.runCLI([
  434. 'node',
  435. 'javascript-obfuscator',
  436. fixtureFilePath,
  437. '--help'
  438. ]);
  439. stdoutWriteMock.restore();
  440. isConsoleLogCalled = callback.called;
  441. });
  442. it('should print `console.log` help', () => {
  443. assert.equal(isConsoleLogCalled, true);
  444. });
  445. });
  446. describe('no arguments passed', () => {
  447. let isConsoleLogCalled: boolean;
  448. beforeEach(() => {
  449. stdoutWriteMock.mute();
  450. JavaScriptObfuscator.runCLI([
  451. 'node',
  452. 'javascript-obfuscator'
  453. ]);
  454. stdoutWriteMock.restore();
  455. isConsoleLogCalled = callback.called;
  456. });
  457. it('should print `console.log` help', () => {
  458. assert.equal(isConsoleLogCalled, true);
  459. });
  460. });
  461. afterEach(() => {
  462. callback.restore();
  463. });
  464. });
  465. describe('`--config` option is set', () => {
  466. const outputSourceMapPath: string = `${outputFilePath}.map`;
  467. let isFileExist: boolean,
  468. sourceMapObject: any;
  469. before(() => {
  470. JavaScriptObfuscator.runCLI([
  471. 'node',
  472. 'javascript-obfuscator',
  473. fixtureFilePath,
  474. '--output',
  475. outputFilePath,
  476. '--config',
  477. configFilePath
  478. ]);
  479. try {
  480. const content: string = fs.readFileSync(outputSourceMapPath, {encoding: 'utf8'});
  481. isFileExist = true;
  482. sourceMapObject = JSON.parse(content);
  483. } catch (e) {
  484. isFileExist = false;
  485. }
  486. });
  487. it('should create file with source map in the same directory as output file', () => {
  488. assert.equal(isFileExist, true);
  489. });
  490. it('source map from created file should contains property `version`', () => {
  491. assert.property(sourceMapObject, 'version');
  492. });
  493. it('source map from created file should contains property `sources`', () => {
  494. assert.property(sourceMapObject, 'sources');
  495. });
  496. it('source map from created file should contains property `names`', () => {
  497. assert.property(sourceMapObject, 'names');
  498. });
  499. after(() => {
  500. fs.unlinkSync(outputFilePath);
  501. fs.unlinkSync(outputSourceMapPath);
  502. });
  503. });
  504. describe('`--config` option is set but overridden by CLI option', () => {
  505. const outputSourceMapPath: string = `${outputFilePath}.map`;
  506. let isFileExist: boolean;
  507. before(() => {
  508. JavaScriptObfuscator.runCLI([
  509. 'node',
  510. 'javascript-obfuscator',
  511. fixtureFilePath,
  512. '--output',
  513. outputFilePath,
  514. '--config',
  515. configFilePath,
  516. '--source-map',
  517. 'false',
  518. ]);
  519. try {
  520. fs.readFileSync(outputSourceMapPath, {encoding: 'utf8'});
  521. isFileExist = true;
  522. } catch (e) {
  523. isFileExist = false;
  524. }
  525. });
  526. it('should create file without source map in the same directory as output file', () => {
  527. assert.equal(isFileExist, false);
  528. });
  529. after(() => {
  530. fs.unlinkSync(outputFilePath);
  531. });
  532. });
  533. after(() => {
  534. rimraf.sync(outputDirName);
  535. });
  536. });
  537. });