DeadCodeInjectionTransformer.ts 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211
  1. import { injectable, inject } from 'inversify';
  2. import { ServiceIdentifiers } from '../../container/ServiceIdentifiers';
  3. import * as estraverse from 'estraverse';
  4. import * as ESTree from 'estree';
  5. import { IOptions } from '../../interfaces/options/IOptions';
  6. import { IRandomGenerator } from '../../interfaces/utils/IRandomGenerator';
  7. import { IVisitor } from '../../interfaces/IVisitor';
  8. import { AbstractNodeTransformer } from '../AbstractNodeTransformer';
  9. import { Node } from '../../node/Node';
  10. import { Nodes } from '../../node/Nodes';
  11. import { NodeUtils } from '../../node/NodeUtils';
  12. @injectable()
  13. export class DeadCodeInjectionTransformer extends AbstractNodeTransformer {
  14. /**
  15. * @type {number}
  16. */
  17. private static readonly maxNestedBlockStatementsCount: number = 4;
  18. /**
  19. * @type {number}
  20. */
  21. private static readonly minCollectedBlockStatementsCount: number = 5;
  22. /**
  23. * @type {ESTree.BlockStatement[]}
  24. */
  25. private readonly collectedBlockStatements: ESTree.BlockStatement[] = [];
  26. /**
  27. * @param {IRandomGenerator} randomGenerator
  28. * @param {IOptions} options
  29. */
  30. constructor (
  31. @inject(ServiceIdentifiers.IRandomGenerator) randomGenerator: IRandomGenerator,
  32. @inject(ServiceIdentifiers.IOptions) options: IOptions
  33. ) {
  34. super(randomGenerator, options);
  35. }
  36. /**
  37. * @return {IVisitor}
  38. */
  39. public getVisitor (): IVisitor {
  40. return {
  41. leave: (node: ESTree.Node, parentNode: ESTree.Node) => {
  42. if (Node.isProgramNode(node)) {
  43. return this.transformNode(node, parentNode);
  44. }
  45. }
  46. };
  47. }
  48. /**
  49. * @param {Program} programNode
  50. * @param {Node} parentNode
  51. * @returns {Node}
  52. */
  53. public transformNode (programNode: ESTree.Program, parentNode: ESTree.Node): ESTree.Node {
  54. this.transformProgramNode(programNode);
  55. return programNode;
  56. }
  57. /**
  58. * @param {BlockStatement} blockStatementNode
  59. * @param {BlockStatement[]} collectedBlockStatements
  60. */
  61. private collectBlockStatementNodes (
  62. blockStatementNode: ESTree.BlockStatement,
  63. collectedBlockStatements: ESTree.BlockStatement[]
  64. ): void {
  65. const clonedBlockStatementNode: ESTree.BlockStatement = NodeUtils.clone(blockStatementNode);
  66. let nestedBlockStatementsCount: number = 0,
  67. isValidBlockStatementNode: boolean = true;
  68. estraverse.replace(clonedBlockStatementNode, {
  69. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  70. /**
  71. * First step: count nested block statements in current block statement
  72. */
  73. if (Node.isBlockStatementNode(node)) {
  74. nestedBlockStatementsCount++;
  75. }
  76. /**
  77. * If nested block statements count bigger then specified amount or current block statement
  78. * contains prohibited nodes - we will stop traversing and leave method
  79. */
  80. if (
  81. nestedBlockStatementsCount > DeadCodeInjectionTransformer.maxNestedBlockStatementsCount ||
  82. Node.isBreakStatementNode(node) ||
  83. Node.isContinueStatementNode(node)
  84. ) {
  85. isValidBlockStatementNode = false;
  86. return estraverse.VisitorOption.Break;
  87. }
  88. /**
  89. * Second step: rename all identifiers (except identifiers in member expressions)
  90. * in current block statement
  91. */
  92. if (Node.isIdentifierNode(node) && !Node.isMemberExpressionNode(parentNode)) {
  93. node.name = this.randomGenerator.getRandomVariableName(6);
  94. }
  95. return node;
  96. }
  97. });
  98. if (!isValidBlockStatementNode) {
  99. return;
  100. }
  101. collectedBlockStatements.push(clonedBlockStatementNode);
  102. }
  103. /**
  104. * @param {BlockStatement} blockStatementNode
  105. * @param {BlockStatement} randomBlockStatementNode
  106. * @returns {BlockStatement}
  107. */
  108. private replaceBlockStatementNode (
  109. blockStatementNode: ESTree.BlockStatement,
  110. randomBlockStatementNode: ESTree.BlockStatement
  111. ): ESTree.BlockStatement {
  112. const random1: boolean = this.randomGenerator.getMathRandom() > 0.5;
  113. const random2: boolean = this.randomGenerator.getMathRandom() > 0.5;
  114. const operator: ESTree.BinaryOperator = random1 ? '===' : '!==';
  115. const leftString: string = this.randomGenerator.getRandomString(3);
  116. const rightString: string = random2 ? leftString : this.randomGenerator.getRandomString(3);
  117. let consequent: ESTree.BlockStatement,
  118. alternate: ESTree.BlockStatement;
  119. if ((random1 && random2) || (!random1 && !random2)) {
  120. consequent = blockStatementNode;
  121. alternate = randomBlockStatementNode;
  122. } else {
  123. consequent = randomBlockStatementNode;
  124. alternate = blockStatementNode;
  125. }
  126. let newBlockStatementNode: ESTree.BlockStatement = Nodes.getBlockStatementNode([
  127. Nodes.getIfStatementNode(
  128. Nodes.getBinaryExpressionNode(
  129. operator,
  130. Nodes.getLiteralNode(leftString),
  131. Nodes.getLiteralNode(rightString)
  132. ),
  133. consequent,
  134. alternate
  135. )
  136. ]);
  137. newBlockStatementNode = NodeUtils.parentize(newBlockStatementNode);
  138. return newBlockStatementNode;
  139. }
  140. /**
  141. * @param {Program} programNode
  142. */
  143. private transformProgramNode (programNode: ESTree.Program): void {
  144. estraverse.traverse(programNode, {
  145. enter: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  146. if (!Node.isBlockStatementNode(node)) {
  147. return;
  148. }
  149. this.collectBlockStatementNodes(node, this.collectedBlockStatements);
  150. }
  151. });
  152. if (this.collectedBlockStatements.length < DeadCodeInjectionTransformer.minCollectedBlockStatementsCount) {
  153. return;
  154. }
  155. estraverse.replace(programNode, {
  156. leave: (node: ESTree.Node, parentNode: ESTree.Node): any => {
  157. if (!this.collectedBlockStatements.length) {
  158. return estraverse.VisitorOption.Break;
  159. }
  160. if (
  161. !Node.isBlockStatementNode(node) ||
  162. this.randomGenerator.getMathRandom() > this.options.deadCodeInjectionThreshold
  163. ) {
  164. return node;
  165. }
  166. const minInteger: number = 0;
  167. const maxInteger: number = this.collectedBlockStatements.length - 1;
  168. const randomIndex: number = this.randomGenerator.getRandomInteger(minInteger, maxInteger);
  169. const randomBlockStatementNode: ESTree.BlockStatement = this.collectedBlockStatements.splice(randomIndex, 1)[0];
  170. if (randomBlockStatementNode === node) {
  171. return node;
  172. }
  173. return this.replaceBlockStatementNode(node, randomBlockStatementNode);
  174. }
  175. });
  176. }
  177. }