StringLiteralReplacer.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../../container/ServiceIdentifiers';
  3. import { ICustomNodeGroup } from '../../../interfaces/custom-nodes/ICustomNodeGroup';
  4. import { IEncodedValue } from '../../../interfaces/node-transformers/IEncodedValue';
  5. import { IOptions } from '../../../interfaces/options/IOptions';
  6. import { IStorage } from '../../../interfaces/storages/IStorage';
  7. import { StringArrayEncoding } from '../../../enums/StringArrayEncoding';
  8. import { AbstractReplacer } from './AbstractReplacer';
  9. import { CryptUtils } from '../../../utils/CryptUtils';
  10. import { RandomGeneratorUtils } from '../../../utils/RandomGeneratorUtils';
  11. import { Utils } from '../../../utils/Utils';
  12. @injectable()
  13. export class StringLiteralReplacer extends AbstractReplacer {
  14. /**
  15. * @type {number}
  16. */
  17. private static readonly minimumLengthForStringArray: number = 3;
  18. /**
  19. * @type {IStorage<ICustomNodeGroup>}
  20. */
  21. private readonly customNodeGroupStorage: IStorage<ICustomNodeGroup>;
  22. /**
  23. * @type {string[]}
  24. */
  25. private readonly rc4Keys: string[];
  26. /**
  27. * @type {Map<string, string>}
  28. */
  29. private readonly stringLiteralCache: Map <string, string> = new Map();
  30. /**
  31. * @type {Map<string, string>}
  32. */
  33. private readonly stringLiteralHexadecimalIndexCache: Map <string, string> = new Map();
  34. /**
  35. * @type {IStorage<string>}
  36. */
  37. private readonly stringArrayStorage: IStorage<string>;
  38. /**
  39. * @param customNodeGroupStorage
  40. * @param stringArrayStorage
  41. * @param options
  42. */
  43. constructor (
  44. @inject(ServiceIdentifiers.TCustomNodeGroupStorage) customNodeGroupStorage: IStorage<ICustomNodeGroup>,
  45. @inject(ServiceIdentifiers.TStringArrayStorage) stringArrayStorage: IStorage<string>,
  46. @inject(ServiceIdentifiers.IOptions) options: IOptions
  47. ) {
  48. super(options);
  49. this.customNodeGroupStorage = customNodeGroupStorage;
  50. this.stringArrayStorage = stringArrayStorage;
  51. this.rc4Keys = RandomGeneratorUtils.getRandomGenerator()
  52. .n(() => RandomGeneratorUtils.getRandomGenerator().string({length: 4}), 50);
  53. }
  54. /**
  55. * @param nodeValue
  56. * @returns {string}
  57. */
  58. public replace (nodeValue: string): string {
  59. const usingStringArray: boolean = (
  60. this.options.stringArray &&
  61. nodeValue.length >= StringLiteralReplacer.minimumLengthForStringArray &&
  62. RandomGeneratorUtils.getRandomFloat(0, 1) <= this.options.stringArrayThreshold
  63. );
  64. const cacheKey: string = `${nodeValue}-${String(usingStringArray)}`;
  65. if (this.stringLiteralCache.has(cacheKey) && this.options.stringArrayEncoding !== StringArrayEncoding.rc4) {
  66. return <string>this.stringLiteralCache.get(cacheKey);
  67. }
  68. let result: string;
  69. if (usingStringArray) {
  70. result = this.replaceStringLiteralWithStringArrayCall(nodeValue);
  71. } else {
  72. result = `'${Utils.stringToUnicodeEscapeSequence(nodeValue, !this.options.unicodeEscapeSequence)}'`;
  73. }
  74. this.stringLiteralCache.set(cacheKey, result);
  75. return result;
  76. }
  77. /**
  78. * @param value
  79. * @return {string}
  80. */
  81. private getArrayHexadecimalIndex (value: string): string {
  82. if (this.stringLiteralHexadecimalIndexCache.has(value)) {
  83. return <string>this.stringLiteralHexadecimalIndexCache.get(value);
  84. }
  85. const stringArrayStorageLength: number = this.stringArrayStorage.getLength();
  86. const hexadecimalIndex: string = `${Utils.hexadecimalPrefix}${Utils.decToHex(stringArrayStorageLength)}`;
  87. this.stringArrayStorage.set(stringArrayStorageLength, value);
  88. this.stringLiteralHexadecimalIndexCache.set(value, hexadecimalIndex);
  89. return hexadecimalIndex;
  90. }
  91. /**
  92. * @param value
  93. * @returns {IEncodedValue}
  94. */
  95. private getEncodedValue (value: string): IEncodedValue {
  96. let encodedValue: string,
  97. key: string | undefined;
  98. switch (this.options.stringArrayEncoding) {
  99. case StringArrayEncoding.rc4:
  100. key = RandomGeneratorUtils.getRandomGenerator().pickone(this.rc4Keys);
  101. encodedValue = CryptUtils.btoa(CryptUtils.rc4(value, key));
  102. break;
  103. case StringArrayEncoding.base64:
  104. encodedValue = CryptUtils.btoa(value);
  105. break;
  106. default:
  107. encodedValue = value;
  108. }
  109. encodedValue = Utils.stringToUnicodeEscapeSequence(encodedValue, !this.options.unicodeEscapeSequence);
  110. return { encodedValue, key };
  111. }
  112. /**
  113. * @param value
  114. * @returns {string}
  115. */
  116. private replaceStringLiteralWithStringArrayCall (value: string): string {
  117. const { encodedValue, key }: IEncodedValue = this.getEncodedValue(value);
  118. const hexadecimalIndex: string = this.getArrayHexadecimalIndex(encodedValue);
  119. const rotatedStringArrayStorageId: string = Utils.stringRotate(this.stringArrayStorage.getStorageId(), 1);
  120. const stringArrayStorageCallsWrapperName: string = `_${Utils.hexadecimalPrefix}${rotatedStringArrayStorageId}`;
  121. if (key) {
  122. return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}', '${Utils.stringToUnicodeEscapeSequence(key, !this.options.unicodeEscapeSequence)}')`;
  123. }
  124. return `${stringArrayStorageCallsWrapperName}('${hexadecimalIndex}')`;
  125. }
  126. }