@ai-jshook/mcp 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (271) hide show
  1. package/.env.example +38 -0
  2. package/CLAUDE.md +170 -0
  3. package/README.md +564 -0
  4. package/bun.lock +1484 -0
  5. package/dist/index.d.ts +3 -0
  6. package/dist/index.d.ts.map +1 -0
  7. package/dist/index.js +57 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/modules/analyzer/AISummarizer.d.ts +41 -0
  10. package/dist/modules/analyzer/AISummarizer.d.ts.map +1 -0
  11. package/dist/modules/analyzer/AISummarizer.js +186 -0
  12. package/dist/modules/analyzer/AISummarizer.js.map +1 -0
  13. package/dist/modules/analyzer/CodeAnalyzer.d.ts +28 -0
  14. package/dist/modules/analyzer/CodeAnalyzer.d.ts.map +1 -0
  15. package/dist/modules/analyzer/CodeAnalyzer.js +1287 -0
  16. package/dist/modules/analyzer/CodeAnalyzer.js.map +1 -0
  17. package/dist/modules/analyzer/IntelligentAnalyzer.d.ts +114 -0
  18. package/dist/modules/analyzer/IntelligentAnalyzer.d.ts.map +1 -0
  19. package/dist/modules/analyzer/IntelligentAnalyzer.js +1176 -0
  20. package/dist/modules/analyzer/IntelligentAnalyzer.js.map +1 -0
  21. package/dist/modules/browser/BrowserModeManager.d.ts +31 -0
  22. package/dist/modules/browser/BrowserModeManager.d.ts.map +1 -0
  23. package/dist/modules/browser/BrowserModeManager.js +241 -0
  24. package/dist/modules/browser/BrowserModeManager.js.map +1 -0
  25. package/dist/modules/captcha/AICaptchaDetector.d.ts +32 -0
  26. package/dist/modules/captcha/AICaptchaDetector.d.ts.map +1 -0
  27. package/dist/modules/captcha/AICaptchaDetector.js +387 -0
  28. package/dist/modules/captcha/AICaptchaDetector.js.map +1 -0
  29. package/dist/modules/captcha/CaptchaDetector.d.ts +28 -0
  30. package/dist/modules/captcha/CaptchaDetector.d.ts.map +1 -0
  31. package/dist/modules/captcha/CaptchaDetector.js +513 -0
  32. package/dist/modules/captcha/CaptchaDetector.js.map +1 -0
  33. package/dist/modules/collector/CodeCache.d.ts +37 -0
  34. package/dist/modules/collector/CodeCache.d.ts.map +1 -0
  35. package/dist/modules/collector/CodeCache.js +188 -0
  36. package/dist/modules/collector/CodeCache.js.map +1 -0
  37. package/dist/modules/collector/CodeCollector.d.ts +107 -0
  38. package/dist/modules/collector/CodeCollector.d.ts.map +1 -0
  39. package/dist/modules/collector/CodeCollector.js +796 -0
  40. package/dist/modules/collector/CodeCollector.js.map +1 -0
  41. package/dist/modules/collector/CodeCompressor.d.ts +65 -0
  42. package/dist/modules/collector/CodeCompressor.d.ts.map +1 -0
  43. package/dist/modules/collector/CodeCompressor.js +245 -0
  44. package/dist/modules/collector/CodeCompressor.js.map +1 -0
  45. package/dist/modules/collector/DOMInspector.d.ts +51 -0
  46. package/dist/modules/collector/DOMInspector.d.ts.map +1 -0
  47. package/dist/modules/collector/DOMInspector.js +437 -0
  48. package/dist/modules/collector/DOMInspector.js.map +1 -0
  49. package/dist/modules/collector/PageController.d.ts +79 -0
  50. package/dist/modules/collector/PageController.d.ts.map +1 -0
  51. package/dist/modules/collector/PageController.js +287 -0
  52. package/dist/modules/collector/PageController.js.map +1 -0
  53. package/dist/modules/collector/SmartCodeCollector.d.ts +38 -0
  54. package/dist/modules/collector/SmartCodeCollector.d.ts.map +1 -0
  55. package/dist/modules/collector/SmartCodeCollector.js +208 -0
  56. package/dist/modules/collector/SmartCodeCollector.js.map +1 -0
  57. package/dist/modules/collector/StreamingCollector.d.ts +46 -0
  58. package/dist/modules/collector/StreamingCollector.d.ts.map +1 -0
  59. package/dist/modules/collector/StreamingCollector.js +127 -0
  60. package/dist/modules/collector/StreamingCollector.js.map +1 -0
  61. package/dist/modules/crypto/CryptoDetector.d.ts +22 -0
  62. package/dist/modules/crypto/CryptoDetector.d.ts.map +1 -0
  63. package/dist/modules/crypto/CryptoDetector.js +168 -0
  64. package/dist/modules/crypto/CryptoDetector.js.map +1 -0
  65. package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts +31 -0
  66. package/dist/modules/crypto/CryptoDetectorEnhanced.d.ts.map +1 -0
  67. package/dist/modules/crypto/CryptoDetectorEnhanced.js +269 -0
  68. package/dist/modules/crypto/CryptoDetectorEnhanced.js.map +1 -0
  69. package/dist/modules/crypto/CryptoRules.d.ts +59 -0
  70. package/dist/modules/crypto/CryptoRules.d.ts.map +1 -0
  71. package/dist/modules/crypto/CryptoRules.js +234 -0
  72. package/dist/modules/crypto/CryptoRules.js.map +1 -0
  73. package/dist/modules/debugger/BlackboxManager.d.ts +14 -0
  74. package/dist/modules/debugger/BlackboxManager.d.ts.map +1 -0
  75. package/dist/modules/debugger/BlackboxManager.js +98 -0
  76. package/dist/modules/debugger/BlackboxManager.js.map +1 -0
  77. package/dist/modules/debugger/DebuggerManager.d.ts +138 -0
  78. package/dist/modules/debugger/DebuggerManager.d.ts.map +1 -0
  79. package/dist/modules/debugger/DebuggerManager.js +777 -0
  80. package/dist/modules/debugger/DebuggerManager.js.map +1 -0
  81. package/dist/modules/debugger/EventBreakpointManager.d.ts +30 -0
  82. package/dist/modules/debugger/EventBreakpointManager.d.ts.map +1 -0
  83. package/dist/modules/debugger/EventBreakpointManager.js +125 -0
  84. package/dist/modules/debugger/EventBreakpointManager.js.map +1 -0
  85. package/dist/modules/debugger/RuntimeInspector.d.ts +54 -0
  86. package/dist/modules/debugger/RuntimeInspector.d.ts.map +1 -0
  87. package/dist/modules/debugger/RuntimeInspector.js +277 -0
  88. package/dist/modules/debugger/RuntimeInspector.js.map +1 -0
  89. package/dist/modules/debugger/ScriptManager.d.ts +94 -0
  90. package/dist/modules/debugger/ScriptManager.d.ts.map +1 -0
  91. package/dist/modules/debugger/ScriptManager.js +433 -0
  92. package/dist/modules/debugger/ScriptManager.js.map +1 -0
  93. package/dist/modules/debugger/WatchExpressionManager.d.ts +52 -0
  94. package/dist/modules/debugger/WatchExpressionManager.d.ts.map +1 -0
  95. package/dist/modules/debugger/WatchExpressionManager.js +136 -0
  96. package/dist/modules/debugger/WatchExpressionManager.js.map +1 -0
  97. package/dist/modules/debugger/XHRBreakpointManager.d.ts +21 -0
  98. package/dist/modules/debugger/XHRBreakpointManager.d.ts.map +1 -0
  99. package/dist/modules/debugger/XHRBreakpointManager.js +81 -0
  100. package/dist/modules/debugger/XHRBreakpointManager.js.map +1 -0
  101. package/dist/modules/deobfuscator/ASTOptimizer.d.ts +12 -0
  102. package/dist/modules/deobfuscator/ASTOptimizer.d.ts.map +1 -0
  103. package/dist/modules/deobfuscator/ASTOptimizer.js +234 -0
  104. package/dist/modules/deobfuscator/ASTOptimizer.js.map +1 -0
  105. package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts +52 -0
  106. package/dist/modules/deobfuscator/AdvancedDeobfuscator.d.ts.map +1 -0
  107. package/dist/modules/deobfuscator/AdvancedDeobfuscator.js +985 -0
  108. package/dist/modules/deobfuscator/AdvancedDeobfuscator.js.map +1 -0
  109. package/dist/modules/deobfuscator/Deobfuscator.d.ts +23 -0
  110. package/dist/modules/deobfuscator/Deobfuscator.d.ts.map +1 -0
  111. package/dist/modules/deobfuscator/Deobfuscator.js +487 -0
  112. package/dist/modules/deobfuscator/Deobfuscator.js.map +1 -0
  113. package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts +19 -0
  114. package/dist/modules/deobfuscator/JSVMPDeobfuscator.d.ts.map +1 -0
  115. package/dist/modules/deobfuscator/JSVMPDeobfuscator.js +594 -0
  116. package/dist/modules/deobfuscator/JSVMPDeobfuscator.js.map +1 -0
  117. package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts +28 -0
  118. package/dist/modules/deobfuscator/JScramberDeobfuscator.d.ts.map +1 -0
  119. package/dist/modules/deobfuscator/JScramberDeobfuscator.js +239 -0
  120. package/dist/modules/deobfuscator/JScramberDeobfuscator.js.map +1 -0
  121. package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts +38 -0
  122. package/dist/modules/deobfuscator/PackerDeobfuscator.d.ts.map +1 -0
  123. package/dist/modules/deobfuscator/PackerDeobfuscator.js +191 -0
  124. package/dist/modules/deobfuscator/PackerDeobfuscator.js.map +1 -0
  125. package/dist/modules/detector/ObfuscationDetector.d.ts +35 -0
  126. package/dist/modules/detector/ObfuscationDetector.d.ts.map +1 -0
  127. package/dist/modules/detector/ObfuscationDetector.js +278 -0
  128. package/dist/modules/detector/ObfuscationDetector.js.map +1 -0
  129. package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts +32 -0
  130. package/dist/modules/emulator/AIEnvironmentAnalyzer.d.ts.map +1 -0
  131. package/dist/modules/emulator/AIEnvironmentAnalyzer.js +548 -0
  132. package/dist/modules/emulator/AIEnvironmentAnalyzer.js.map +1 -0
  133. package/dist/modules/emulator/BrowserAPIDatabase.d.ts +34 -0
  134. package/dist/modules/emulator/BrowserAPIDatabase.d.ts.map +1 -0
  135. package/dist/modules/emulator/BrowserAPIDatabase.js +326 -0
  136. package/dist/modules/emulator/BrowserAPIDatabase.js.map +1 -0
  137. package/dist/modules/emulator/BrowserEnvironmentRules.d.ts +47 -0
  138. package/dist/modules/emulator/BrowserEnvironmentRules.d.ts.map +1 -0
  139. package/dist/modules/emulator/BrowserEnvironmentRules.js +493 -0
  140. package/dist/modules/emulator/BrowserEnvironmentRules.js.map +1 -0
  141. package/dist/modules/emulator/EnvironmentEmulator.d.ts +27 -0
  142. package/dist/modules/emulator/EnvironmentEmulator.d.ts.map +1 -0
  143. package/dist/modules/emulator/EnvironmentEmulator.js +1113 -0
  144. package/dist/modules/emulator/EnvironmentEmulator.js.map +1 -0
  145. package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts +26 -0
  146. package/dist/modules/emulator/EnvironmentEmulatorEnhanced.d.ts.map +1 -0
  147. package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js +493 -0
  148. package/dist/modules/emulator/EnvironmentEmulatorEnhanced.js.map +1 -0
  149. package/dist/modules/emulator/templates/chrome-env.d.ts +260 -0
  150. package/dist/modules/emulator/templates/chrome-env.d.ts.map +1 -0
  151. package/dist/modules/emulator/templates/chrome-env.js +253 -0
  152. package/dist/modules/emulator/templates/chrome-env.js.map +1 -0
  153. package/dist/modules/hook/AIHookGenerator.d.ts +53 -0
  154. package/dist/modules/hook/AIHookGenerator.d.ts.map +1 -0
  155. package/dist/modules/hook/AIHookGenerator.js +353 -0
  156. package/dist/modules/hook/AIHookGenerator.js.map +1 -0
  157. package/dist/modules/hook/HookManager.d.ts +67 -0
  158. package/dist/modules/hook/HookManager.d.ts.map +1 -0
  159. package/dist/modules/hook/HookManager.js +1225 -0
  160. package/dist/modules/hook/HookManager.js.map +1 -0
  161. package/dist/modules/monitor/ConsoleMonitor.d.ts +140 -0
  162. package/dist/modules/monitor/ConsoleMonitor.d.ts.map +1 -0
  163. package/dist/modules/monitor/ConsoleMonitor.js +834 -0
  164. package/dist/modules/monitor/ConsoleMonitor.js.map +1 -0
  165. package/dist/modules/monitor/PerformanceMonitor.d.ts +65 -0
  166. package/dist/modules/monitor/PerformanceMonitor.d.ts.map +1 -0
  167. package/dist/modules/monitor/PerformanceMonitor.js +175 -0
  168. package/dist/modules/monitor/PerformanceMonitor.js.map +1 -0
  169. package/dist/modules/stealth/StealthScripts2025.d.ts +17 -0
  170. package/dist/modules/stealth/StealthScripts2025.d.ts.map +1 -0
  171. package/dist/modules/stealth/StealthScripts2025.js +274 -0
  172. package/dist/modules/stealth/StealthScripts2025.js.map +1 -0
  173. package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts +69 -0
  174. package/dist/modules/symbolic/JSVMPSymbolicExecutor.d.ts.map +1 -0
  175. package/dist/modules/symbolic/JSVMPSymbolicExecutor.js +232 -0
  176. package/dist/modules/symbolic/JSVMPSymbolicExecutor.js.map +1 -0
  177. package/dist/modules/symbolic/SymbolicExecutor.d.ts +69 -0
  178. package/dist/modules/symbolic/SymbolicExecutor.d.ts.map +1 -0
  179. package/dist/modules/symbolic/SymbolicExecutor.js +346 -0
  180. package/dist/modules/symbolic/SymbolicExecutor.js.map +1 -0
  181. package/dist/server/AIHookToolDefinitions.d.ts +3 -0
  182. package/dist/server/AIHookToolDefinitions.d.ts.map +1 -0
  183. package/dist/server/AIHookToolDefinitions.js +284 -0
  184. package/dist/server/AIHookToolDefinitions.js.map +1 -0
  185. package/dist/server/AIHookToolHandlers.d.ts +50 -0
  186. package/dist/server/AIHookToolHandlers.d.ts.map +1 -0
  187. package/dist/server/AIHookToolHandlers.js +311 -0
  188. package/dist/server/AIHookToolHandlers.js.map +1 -0
  189. package/dist/server/AdvancedToolDefinitions.d.ts +3 -0
  190. package/dist/server/AdvancedToolDefinitions.d.ts.map +1 -0
  191. package/dist/server/AdvancedToolDefinitions.js +218 -0
  192. package/dist/server/AdvancedToolDefinitions.js.map +1 -0
  193. package/dist/server/AdvancedToolHandlers.d.ts +85 -0
  194. package/dist/server/AdvancedToolHandlers.d.ts.map +1 -0
  195. package/dist/server/AdvancedToolHandlers.js +431 -0
  196. package/dist/server/AdvancedToolHandlers.js.map +1 -0
  197. package/dist/server/BrowserToolDefinitions.d.ts +3 -0
  198. package/dist/server/BrowserToolDefinitions.d.ts.map +1 -0
  199. package/dist/server/BrowserToolDefinitions.js +841 -0
  200. package/dist/server/BrowserToolDefinitions.js.map +1 -0
  201. package/dist/server/BrowserToolHandlers.d.ts +290 -0
  202. package/dist/server/BrowserToolHandlers.d.ts.map +1 -0
  203. package/dist/server/BrowserToolHandlers.js +784 -0
  204. package/dist/server/BrowserToolHandlers.js.map +1 -0
  205. package/dist/server/CacheToolDefinitions.d.ts +3 -0
  206. package/dist/server/CacheToolDefinitions.d.ts.map +1 -0
  207. package/dist/server/CacheToolDefinitions.js +166 -0
  208. package/dist/server/CacheToolDefinitions.js.map +1 -0
  209. package/dist/server/DebuggerToolDefinitions.d.ts +3 -0
  210. package/dist/server/DebuggerToolDefinitions.d.ts.map +1 -0
  211. package/dist/server/DebuggerToolDefinitions.js +600 -0
  212. package/dist/server/DebuggerToolDefinitions.js.map +1 -0
  213. package/dist/server/DebuggerToolHandlers.d.ts +230 -0
  214. package/dist/server/DebuggerToolHandlers.d.ts.map +1 -0
  215. package/dist/server/DebuggerToolHandlers.js +935 -0
  216. package/dist/server/DebuggerToolHandlers.js.map +1 -0
  217. package/dist/server/MCPServer.d.ts +55 -0
  218. package/dist/server/MCPServer.d.ts.map +1 -0
  219. package/dist/server/MCPServer.js +1344 -0
  220. package/dist/server/MCPServer.js.map +1 -0
  221. package/dist/server/TokenBudgetToolDefinitions.d.ts +3 -0
  222. package/dist/server/TokenBudgetToolDefinitions.d.ts.map +1 -0
  223. package/dist/server/TokenBudgetToolDefinitions.js +114 -0
  224. package/dist/server/TokenBudgetToolDefinitions.js.map +1 -0
  225. package/dist/services/LLMService.d.ts +41 -0
  226. package/dist/services/LLMService.d.ts.map +1 -0
  227. package/dist/services/LLMService.js +792 -0
  228. package/dist/services/LLMService.js.map +1 -0
  229. package/dist/types/index.d.ts +527 -0
  230. package/dist/types/index.d.ts.map +1 -0
  231. package/dist/types/index.js +2 -0
  232. package/dist/types/index.js.map +1 -0
  233. package/dist/utils/AdaptiveDataSerializer.d.ts +27 -0
  234. package/dist/utils/AdaptiveDataSerializer.d.ts.map +1 -0
  235. package/dist/utils/AdaptiveDataSerializer.js +215 -0
  236. package/dist/utils/AdaptiveDataSerializer.js.map +1 -0
  237. package/dist/utils/CacheAdapters.d.ts +30 -0
  238. package/dist/utils/CacheAdapters.d.ts.map +1 -0
  239. package/dist/utils/CacheAdapters.js +83 -0
  240. package/dist/utils/CacheAdapters.js.map +1 -0
  241. package/dist/utils/TokenBudgetManager.d.ts +52 -0
  242. package/dist/utils/TokenBudgetManager.d.ts.map +1 -0
  243. package/dist/utils/TokenBudgetManager.js +190 -0
  244. package/dist/utils/TokenBudgetManager.js.map +1 -0
  245. package/dist/utils/UnifiedCacheManager.d.ts +55 -0
  246. package/dist/utils/UnifiedCacheManager.d.ts.map +1 -0
  247. package/dist/utils/UnifiedCacheManager.js +207 -0
  248. package/dist/utils/UnifiedCacheManager.js.map +1 -0
  249. package/dist/utils/cache.d.ts +13 -0
  250. package/dist/utils/cache.d.ts.map +1 -0
  251. package/dist/utils/cache.js +92 -0
  252. package/dist/utils/cache.js.map +1 -0
  253. package/dist/utils/config.d.ts +7 -0
  254. package/dist/utils/config.d.ts.map +1 -0
  255. package/dist/utils/config.js +93 -0
  256. package/dist/utils/config.js.map +1 -0
  257. package/dist/utils/detailedDataManager.d.ts +60 -0
  258. package/dist/utils/detailedDataManager.d.ts.map +1 -0
  259. package/dist/utils/detailedDataManager.js +204 -0
  260. package/dist/utils/detailedDataManager.js.map +1 -0
  261. package/dist/utils/logger.d.ts +16 -0
  262. package/dist/utils/logger.d.ts.map +1 -0
  263. package/dist/utils/logger.js +47 -0
  264. package/dist/utils/logger.js.map +1 -0
  265. package/dist/utils/parallel.d.ts +40 -0
  266. package/dist/utils/parallel.d.ts.map +1 -0
  267. package/dist/utils/parallel.js +148 -0
  268. package/dist/utils/parallel.js.map +1 -0
  269. package/package.json +94 -0
  270. package/server.json +39 -0
  271. package/tsconfig.dev.json +14 -0
@@ -0,0 +1,1287 @@
1
+ import * as parser from '@babel/parser';
2
+ import traverse from '@babel/traverse';
3
+ import * as t from '@babel/types';
4
+ import { logger } from '../../utils/logger.js';
5
+ export class CodeAnalyzer {
6
+ llm;
7
+ constructor(llm) {
8
+ this.llm = llm;
9
+ }
10
+ async understand(options) {
11
+ logger.info('Starting code understanding...');
12
+ const startTime = Date.now();
13
+ try {
14
+ const { code, context, focus = 'all' } = options;
15
+ const structure = await this.analyzeStructure(code);
16
+ logger.debug('Code structure analyzed');
17
+ const aiAnalysis = await this.aiAnalyze(code, focus);
18
+ logger.debug('AI analysis completed');
19
+ const techStack = this.detectTechStack(code, aiAnalysis);
20
+ logger.debug('Tech stack detected');
21
+ const businessLogic = this.extractBusinessLogic(aiAnalysis, context);
22
+ logger.debug('Business logic extracted');
23
+ const dataFlow = await this.analyzeDataFlow(code);
24
+ logger.debug('Data flow analyzed');
25
+ const securityRisks = this.identifySecurityRisks(code, aiAnalysis);
26
+ logger.debug('Security risks identified');
27
+ const { patterns, antiPatterns } = this.detectCodePatterns(code);
28
+ logger.debug(`Detected ${patterns.length} patterns and ${antiPatterns.length} anti-patterns`);
29
+ const complexityMetrics = this.analyzeComplexityMetrics(code);
30
+ logger.debug('Complexity metrics calculated');
31
+ const qualityScore = this.calculateQualityScore(structure, securityRisks, aiAnalysis, complexityMetrics, antiPatterns);
32
+ const duration = Date.now() - startTime;
33
+ logger.success(`Code understanding completed in ${duration}ms`);
34
+ return {
35
+ structure,
36
+ techStack,
37
+ businessLogic,
38
+ dataFlow,
39
+ securityRisks,
40
+ qualityScore,
41
+ codePatterns: patterns,
42
+ antiPatterns,
43
+ complexityMetrics,
44
+ };
45
+ }
46
+ catch (error) {
47
+ logger.error('Code understanding failed', error);
48
+ throw error;
49
+ }
50
+ }
51
+ async analyzeStructure(code) {
52
+ const functions = [];
53
+ const classes = [];
54
+ try {
55
+ const ast = parser.parse(code, {
56
+ sourceType: 'module',
57
+ plugins: ['jsx', 'typescript'],
58
+ });
59
+ const self = this;
60
+ traverse(ast, {
61
+ FunctionDeclaration(path) {
62
+ const node = path.node;
63
+ functions.push({
64
+ name: node.id?.name || 'anonymous',
65
+ params: node.params.map((p) => (p.type === 'Identifier' ? p.name : 'unknown')),
66
+ location: {
67
+ file: 'current',
68
+ line: node.loc?.start.line || 0,
69
+ column: node.loc?.start.column,
70
+ },
71
+ complexity: self.calculateComplexity(path),
72
+ });
73
+ },
74
+ FunctionExpression(path) {
75
+ const node = path.node;
76
+ const parent = path.parent;
77
+ let name = 'anonymous';
78
+ if (parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
79
+ name = parent.id.name;
80
+ }
81
+ else if (parent.type === 'AssignmentExpression' && parent.left.type === 'Identifier') {
82
+ name = parent.left.name;
83
+ }
84
+ functions.push({
85
+ name,
86
+ params: node.params.map((p) => (p.type === 'Identifier' ? p.name : 'unknown')),
87
+ location: {
88
+ file: 'current',
89
+ line: node.loc?.start.line || 0,
90
+ column: node.loc?.start.column,
91
+ },
92
+ complexity: self.calculateComplexity(path),
93
+ });
94
+ },
95
+ ArrowFunctionExpression(path) {
96
+ const node = path.node;
97
+ const parent = path.parent;
98
+ let name = 'arrow';
99
+ if (parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
100
+ name = parent.id.name;
101
+ }
102
+ functions.push({
103
+ name,
104
+ params: node.params.map((p) => (p.type === 'Identifier' ? p.name : 'unknown')),
105
+ location: {
106
+ file: 'current',
107
+ line: node.loc?.start.line || 0,
108
+ column: node.loc?.start.column,
109
+ },
110
+ complexity: self.calculateComplexity(path),
111
+ });
112
+ },
113
+ ClassDeclaration(path) {
114
+ const node = path.node;
115
+ const methods = [];
116
+ const properties = [];
117
+ path.traverse({
118
+ ClassMethod(methodPath) {
119
+ const method = methodPath.node;
120
+ methods.push({
121
+ name: method.key.type === 'Identifier' ? method.key.name : 'unknown',
122
+ params: method.params.map((p) => (p.type === 'Identifier' ? p.name : 'unknown')),
123
+ location: {
124
+ file: 'current',
125
+ line: method.loc?.start.line || 0,
126
+ column: method.loc?.start.column,
127
+ },
128
+ complexity: 1,
129
+ });
130
+ },
131
+ ClassProperty(propertyPath) {
132
+ const property = propertyPath.node;
133
+ if (property.key.type === 'Identifier') {
134
+ properties.push({
135
+ name: property.key.name,
136
+ type: undefined,
137
+ value: undefined,
138
+ });
139
+ }
140
+ },
141
+ });
142
+ classes.push({
143
+ name: node.id?.name || 'anonymous',
144
+ methods,
145
+ properties,
146
+ location: {
147
+ file: 'current',
148
+ line: node.loc?.start.line || 0,
149
+ column: node.loc?.start.column,
150
+ },
151
+ });
152
+ },
153
+ });
154
+ }
155
+ catch (error) {
156
+ logger.warn('Failed to parse code structure', error);
157
+ }
158
+ const modules = this.analyzeModules(code);
159
+ const callGraph = this.buildCallGraph(functions, code);
160
+ return {
161
+ functions,
162
+ classes,
163
+ modules,
164
+ callGraph,
165
+ };
166
+ }
167
+ async aiAnalyze(code, focus) {
168
+ try {
169
+ const messages = this.llm.generateCodeAnalysisPrompt(code, focus);
170
+ const response = await this.llm.chat(messages, { temperature: 0.3, maxTokens: 2000 });
171
+ const jsonMatch = response.content.match(/\{[\s\S]*\}/);
172
+ if (jsonMatch) {
173
+ return JSON.parse(jsonMatch[0]);
174
+ }
175
+ return { rawAnalysis: response.content };
176
+ }
177
+ catch (error) {
178
+ logger.warn('AI analysis failed, using fallback', error);
179
+ return {};
180
+ }
181
+ }
182
+ detectTechStack(code, aiAnalysis) {
183
+ const techStack = {
184
+ other: [],
185
+ };
186
+ if (aiAnalysis.techStack && typeof aiAnalysis.techStack === 'object') {
187
+ const ts = aiAnalysis.techStack;
188
+ techStack.framework = ts.framework;
189
+ techStack.bundler = ts.bundler;
190
+ if (Array.isArray(ts.libraries)) {
191
+ techStack.other = ts.libraries;
192
+ }
193
+ }
194
+ if (code.includes('React.') || code.includes('useState') || code.includes('useEffect')) {
195
+ techStack.framework = 'React';
196
+ }
197
+ else if (code.includes('Vue.') || code.includes('createApp')) {
198
+ techStack.framework = 'Vue';
199
+ }
200
+ else if (code.includes('@angular/')) {
201
+ techStack.framework = 'Angular';
202
+ }
203
+ if (code.includes('__webpack_require__')) {
204
+ techStack.bundler = 'Webpack';
205
+ }
206
+ const cryptoLibs = [];
207
+ if (code.includes('CryptoJS'))
208
+ cryptoLibs.push('CryptoJS');
209
+ if (code.includes('JSEncrypt'))
210
+ cryptoLibs.push('JSEncrypt');
211
+ if (code.includes('crypto-js'))
212
+ cryptoLibs.push('crypto-js');
213
+ if (cryptoLibs.length > 0) {
214
+ techStack.cryptoLibrary = cryptoLibs;
215
+ }
216
+ return techStack;
217
+ }
218
+ extractBusinessLogic(aiAnalysis, context) {
219
+ const businessLogic = {
220
+ mainFeatures: [],
221
+ entities: [],
222
+ rules: [],
223
+ dataModel: {},
224
+ };
225
+ if (aiAnalysis.businessLogic && typeof aiAnalysis.businessLogic === 'object') {
226
+ const bl = aiAnalysis.businessLogic;
227
+ if (Array.isArray(bl.mainFeatures)) {
228
+ businessLogic.mainFeatures = bl.mainFeatures;
229
+ }
230
+ if (typeof bl.dataFlow === 'string') {
231
+ businessLogic.rules.push(bl.dataFlow);
232
+ }
233
+ }
234
+ if (context) {
235
+ businessLogic.dataModel = { ...businessLogic.dataModel, ...context };
236
+ }
237
+ return businessLogic;
238
+ }
239
+ analyzeModules(code) {
240
+ const modules = [];
241
+ try {
242
+ const ast = parser.parse(code, {
243
+ sourceType: 'module',
244
+ plugins: ['jsx', 'typescript'],
245
+ });
246
+ const imports = [];
247
+ const exports = [];
248
+ traverse(ast, {
249
+ ImportDeclaration(path) {
250
+ imports.push(path.node.source.value);
251
+ },
252
+ ExportNamedDeclaration(path) {
253
+ if (path.node.source) {
254
+ exports.push(path.node.source.value);
255
+ }
256
+ },
257
+ ExportDefaultDeclaration() {
258
+ exports.push('default');
259
+ },
260
+ });
261
+ if (imports.length > 0 || exports.length > 0) {
262
+ modules.push({
263
+ name: 'current',
264
+ imports,
265
+ exports,
266
+ });
267
+ }
268
+ }
269
+ catch (error) {
270
+ logger.warn('Module analysis failed', error);
271
+ }
272
+ return modules;
273
+ }
274
+ buildCallGraph(functions, code) {
275
+ const nodes = functions.map((fn) => ({
276
+ id: fn.name,
277
+ name: fn.name,
278
+ type: 'function',
279
+ }));
280
+ const edges = [];
281
+ try {
282
+ const ast = parser.parse(code, {
283
+ sourceType: 'module',
284
+ plugins: ['jsx', 'typescript'],
285
+ });
286
+ let currentFunction = '';
287
+ traverse(ast, {
288
+ FunctionDeclaration(path) {
289
+ currentFunction = path.node.id?.name || '';
290
+ },
291
+ FunctionExpression(path) {
292
+ const parent = path.parent;
293
+ if (parent.type === 'VariableDeclarator' && parent.id.type === 'Identifier') {
294
+ currentFunction = parent.id.name;
295
+ }
296
+ },
297
+ CallExpression(path) {
298
+ if (currentFunction) {
299
+ const callee = path.node.callee;
300
+ let calledFunction = '';
301
+ if (callee.type === 'Identifier') {
302
+ calledFunction = callee.name;
303
+ }
304
+ else if (callee.type === 'MemberExpression' && callee.property.type === 'Identifier') {
305
+ calledFunction = callee.property.name;
306
+ }
307
+ if (calledFunction && functions.some((f) => f.name === calledFunction)) {
308
+ edges.push({
309
+ from: currentFunction,
310
+ to: calledFunction,
311
+ });
312
+ }
313
+ }
314
+ },
315
+ });
316
+ }
317
+ catch (error) {
318
+ logger.warn('Call graph construction failed', error);
319
+ }
320
+ return { nodes, edges };
321
+ }
322
+ calculateComplexity(path) {
323
+ let complexity = 1;
324
+ const anyPath = path;
325
+ if (anyPath.traverse) {
326
+ anyPath.traverse({
327
+ IfStatement() {
328
+ complexity++;
329
+ },
330
+ SwitchCase() {
331
+ complexity++;
332
+ },
333
+ ForStatement() {
334
+ complexity++;
335
+ },
336
+ WhileStatement() {
337
+ complexity++;
338
+ },
339
+ DoWhileStatement() {
340
+ complexity++;
341
+ },
342
+ ConditionalExpression() {
343
+ complexity++;
344
+ },
345
+ LogicalExpression(logicalPath) {
346
+ if (logicalPath.node.operator === '&&' || logicalPath.node.operator === '||') {
347
+ complexity++;
348
+ }
349
+ },
350
+ CatchClause() {
351
+ complexity++;
352
+ },
353
+ });
354
+ }
355
+ return complexity;
356
+ }
357
+ async analyzeDataFlow(code) {
358
+ const graph = { nodes: [], edges: [] };
359
+ const sources = [];
360
+ const sinks = [];
361
+ const taintPaths = [];
362
+ const taintMap = new Map();
363
+ const sanitizers = new Set([
364
+ 'encodeURIComponent', 'encodeURI', 'escape', 'decodeURIComponent', 'decodeURI',
365
+ 'htmlentities', 'htmlspecialchars', 'escapeHtml', 'escapeHTML',
366
+ 'he.encode', 'he.escape',
367
+ 'validator.escape', 'validator.unescape', 'validator.stripLow',
368
+ 'validator.blacklist', 'validator.whitelist', 'validator.trim',
369
+ 'validator.isEmail', 'validator.isURL', 'validator.isInt',
370
+ 'DOMPurify.sanitize', 'DOMPurify.addHook',
371
+ 'crypto.encrypt', 'crypto.hash', 'crypto.createHash', 'crypto.createHmac',
372
+ 'CryptoJS.AES.encrypt', 'CryptoJS.SHA256', 'CryptoJS.MD5',
373
+ 'bcrypt.hash', 'bcrypt.compare',
374
+ 'btoa', 'atob', 'Buffer.from',
375
+ 'db.prepare', 'db.query', 'mysql.escape', 'pg.query',
376
+ 'xss', 'sanitizeHtml',
377
+ 'parseInt', 'parseFloat', 'Number', 'String',
378
+ 'JSON.stringify', 'JSON.parse',
379
+ 'String.prototype.replace', 'String.prototype.trim',
380
+ 'Array.prototype.filter', 'Array.prototype.map',
381
+ ]);
382
+ try {
383
+ const ast = parser.parse(code, {
384
+ sourceType: 'module',
385
+ plugins: ['jsx', 'typescript'],
386
+ });
387
+ const self = this;
388
+ traverse(ast, {
389
+ CallExpression(path) {
390
+ const callee = path.node.callee;
391
+ const line = path.node.loc?.start.line || 0;
392
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
393
+ const methodName = callee.property.name;
394
+ if (['fetch', 'ajax', 'get', 'post', 'request', 'axios'].includes(methodName)) {
395
+ const sourceId = `source-network-${line}`;
396
+ sources.push({ type: 'network', location: { file: 'current', line } });
397
+ graph.nodes.push({
398
+ id: sourceId,
399
+ name: `${methodName}()`,
400
+ type: 'source',
401
+ location: { file: 'current', line },
402
+ });
403
+ const parent = path.parent;
404
+ if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
405
+ taintMap.set(parent.id.name, { sourceType: 'network', sourceLine: line });
406
+ }
407
+ }
408
+ else if (['querySelector', 'getElementById', 'getElementsByClassName', 'getElementsByTagName'].includes(methodName)) {
409
+ const sourceId = `source-dom-${line}`;
410
+ sources.push({ type: 'user_input', location: { file: 'current', line } });
411
+ graph.nodes.push({
412
+ id: sourceId,
413
+ name: `${methodName}()`,
414
+ type: 'source',
415
+ location: { file: 'current', line },
416
+ });
417
+ }
418
+ }
419
+ if (t.isIdentifier(callee)) {
420
+ const funcName = callee.name;
421
+ if (['eval', 'Function', 'setTimeout', 'setInterval'].includes(funcName)) {
422
+ const sinkId = `sink-eval-${line}`;
423
+ sinks.push({ type: 'eval', location: { file: 'current', line } });
424
+ graph.nodes.push({
425
+ id: sinkId,
426
+ name: `${funcName}()`,
427
+ type: 'sink',
428
+ location: { file: 'current', line },
429
+ });
430
+ self.checkTaintedArguments(path.node.arguments, taintMap, taintPaths, funcName, line);
431
+ }
432
+ }
433
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
434
+ const methodName = callee.property.name;
435
+ if (['write', 'writeln'].includes(methodName) &&
436
+ t.isIdentifier(callee.object) && callee.object.name === 'document') {
437
+ const sinkId = `sink-document-write-${line}`;
438
+ sinks.push({ type: 'xss', location: { file: 'current', line } });
439
+ graph.nodes.push({
440
+ id: sinkId,
441
+ name: `document.${methodName}()`,
442
+ type: 'sink',
443
+ location: { file: 'current', line },
444
+ });
445
+ self.checkTaintedArguments(path.node.arguments, taintMap, taintPaths, methodName, line);
446
+ }
447
+ if (['query', 'execute', 'exec', 'run'].includes(methodName)) {
448
+ const sinkId = `sink-sql-${line}`;
449
+ sinks.push({ type: 'sql-injection', location: { file: 'current', line } });
450
+ graph.nodes.push({
451
+ id: sinkId,
452
+ name: `${methodName}() (SQL)`,
453
+ type: 'sink',
454
+ location: { file: 'current', line },
455
+ });
456
+ self.checkTaintedArguments(path.node.arguments, taintMap, taintPaths, methodName, line);
457
+ }
458
+ if (['exec', 'spawn', 'execSync', 'spawnSync'].includes(methodName)) {
459
+ const sinkId = `sink-command-${line}`;
460
+ sinks.push({ type: 'other', location: { file: 'current', line } });
461
+ graph.nodes.push({
462
+ id: sinkId,
463
+ name: `${methodName}() (Command)`,
464
+ type: 'sink',
465
+ location: { file: 'current', line },
466
+ });
467
+ self.checkTaintedArguments(path.node.arguments, taintMap, taintPaths, methodName, line);
468
+ }
469
+ if (['readFile', 'writeFile', 'readFileSync', 'writeFileSync', 'open'].includes(methodName)) {
470
+ const sinkId = `sink-file-${line}`;
471
+ sinks.push({ type: 'other', location: { file: 'current', line } });
472
+ graph.nodes.push({
473
+ id: sinkId,
474
+ name: `${methodName}() (File)`,
475
+ type: 'sink',
476
+ location: { file: 'current', line },
477
+ });
478
+ self.checkTaintedArguments(path.node.arguments, taintMap, taintPaths, methodName, line);
479
+ }
480
+ }
481
+ },
482
+ MemberExpression(path) {
483
+ const obj = path.node.object;
484
+ const prop = path.node.property;
485
+ const line = path.node.loc?.start.line || 0;
486
+ if (t.isIdentifier(obj) && obj.name === 'location' && t.isIdentifier(prop)) {
487
+ if (['href', 'search', 'hash', 'pathname'].includes(prop.name)) {
488
+ const sourceId = `source-url-${line}`;
489
+ sources.push({ type: 'user_input', location: { file: 'current', line } });
490
+ graph.nodes.push({
491
+ id: sourceId,
492
+ name: `location.${prop.name}`,
493
+ type: 'source',
494
+ location: { file: 'current', line },
495
+ });
496
+ const parent = path.parent;
497
+ if (t.isVariableDeclarator(parent) && t.isIdentifier(parent.id)) {
498
+ taintMap.set(parent.id.name, { sourceType: 'url', sourceLine: line });
499
+ }
500
+ }
501
+ }
502
+ if (t.isIdentifier(obj) && obj.name === 'document' && t.isIdentifier(prop) && prop.name === 'cookie') {
503
+ const sourceId = `source-cookie-${line}`;
504
+ sources.push({ type: 'storage', location: { file: 'current', line } });
505
+ graph.nodes.push({
506
+ id: sourceId,
507
+ name: 'document.cookie',
508
+ type: 'source',
509
+ location: { file: 'current', line },
510
+ });
511
+ }
512
+ if (t.isIdentifier(obj) && ['localStorage', 'sessionStorage'].includes(obj.name)) {
513
+ const sourceId = `source-storage-${line}`;
514
+ sources.push({ type: 'storage', location: { file: 'current', line } });
515
+ graph.nodes.push({
516
+ id: sourceId,
517
+ name: `${obj.name}.getItem()`,
518
+ type: 'source',
519
+ location: { file: 'current', line },
520
+ });
521
+ }
522
+ if (t.isIdentifier(obj) && obj.name === 'window' &&
523
+ t.isIdentifier(prop) && prop.name === 'name') {
524
+ const sourceId = `source-window-name-${line}`;
525
+ sources.push({ type: 'user_input', location: { file: 'current', line } });
526
+ graph.nodes.push({
527
+ id: sourceId,
528
+ name: 'window.name',
529
+ type: 'source',
530
+ location: { file: 'current', line },
531
+ });
532
+ }
533
+ if (t.isIdentifier(obj) && obj.name === 'event' &&
534
+ t.isIdentifier(prop) && prop.name === 'data') {
535
+ const sourceId = `source-postmessage-${line}`;
536
+ sources.push({ type: 'network', location: { file: 'current', line } });
537
+ graph.nodes.push({
538
+ id: sourceId,
539
+ name: 'event.data (postMessage)',
540
+ type: 'source',
541
+ location: { file: 'current', line },
542
+ });
543
+ }
544
+ if (t.isIdentifier(obj) && obj.name === 'message' &&
545
+ t.isIdentifier(prop) && prop.name === 'data') {
546
+ const sourceId = `source-websocket-${line}`;
547
+ sources.push({ type: 'network', location: { file: 'current', line } });
548
+ graph.nodes.push({
549
+ id: sourceId,
550
+ name: 'WebSocket message.data',
551
+ type: 'source',
552
+ location: { file: 'current', line },
553
+ });
554
+ }
555
+ },
556
+ AssignmentExpression(path) {
557
+ const left = path.node.left;
558
+ const right = path.node.right;
559
+ const line = path.node.loc?.start.line || 0;
560
+ if (t.isMemberExpression(left) && t.isIdentifier(left.property)) {
561
+ const propName = left.property.name;
562
+ if (['innerHTML', 'outerHTML'].includes(propName)) {
563
+ const sinkId = `sink-dom-${line}`;
564
+ sinks.push({ type: 'xss', location: { file: 'current', line } });
565
+ graph.nodes.push({
566
+ id: sinkId,
567
+ name: propName,
568
+ type: 'sink',
569
+ location: { file: 'current', line },
570
+ });
571
+ if (t.isIdentifier(right) && taintMap.has(right.name)) {
572
+ const taintInfo = taintMap.get(right.name);
573
+ taintPaths.push({
574
+ source: { type: taintInfo.sourceType, location: { file: 'current', line: taintInfo.sourceLine } },
575
+ sink: { type: 'xss', location: { file: 'current', line } },
576
+ path: [
577
+ { file: 'current', line: taintInfo.sourceLine },
578
+ { file: 'current', line },
579
+ ],
580
+ });
581
+ }
582
+ }
583
+ }
584
+ },
585
+ });
586
+ traverse(ast, {
587
+ VariableDeclarator(path) {
588
+ const id = path.node.id;
589
+ const init = path.node.init;
590
+ if (t.isIdentifier(id) && init) {
591
+ if (t.isCallExpression(init) && self.checkSanitizer(init, sanitizers)) {
592
+ const arg = init.arguments[0];
593
+ if (t.isIdentifier(arg) && taintMap.has(arg.name)) {
594
+ logger.debug(`Taint cleaned by sanitizer: ${arg.name} -> ${id.name}`);
595
+ return;
596
+ }
597
+ }
598
+ if (t.isIdentifier(init) && taintMap.has(init.name)) {
599
+ const taintInfo = taintMap.get(init.name);
600
+ taintMap.set(id.name, taintInfo);
601
+ }
602
+ else if (t.isBinaryExpression(init)) {
603
+ const leftTainted = t.isIdentifier(init.left) && taintMap.has(init.left.name);
604
+ const rightTainted = t.isIdentifier(init.right) && taintMap.has(init.right.name);
605
+ if (leftTainted || rightTainted) {
606
+ const taintInfo = leftTainted ? taintMap.get(init.left.name) : taintMap.get(init.right.name);
607
+ taintMap.set(id.name, taintInfo);
608
+ }
609
+ }
610
+ else if (t.isCallExpression(init)) {
611
+ const arg = init.arguments[0];
612
+ if (t.isIdentifier(arg) && taintMap.has(arg.name)) {
613
+ const taintInfo = taintMap.get(arg.name);
614
+ taintMap.set(id.name, taintInfo);
615
+ }
616
+ }
617
+ }
618
+ },
619
+ AssignmentExpression(path) {
620
+ const left = path.node.left;
621
+ const right = path.node.right;
622
+ if (t.isIdentifier(left) && t.isIdentifier(right) && taintMap.has(right.name)) {
623
+ const taintInfo = taintMap.get(right.name);
624
+ taintMap.set(left.name, taintInfo);
625
+ }
626
+ },
627
+ });
628
+ }
629
+ catch (error) {
630
+ logger.warn('Data flow analysis failed', error);
631
+ }
632
+ if (taintPaths.length > 0 && this.llm) {
633
+ try {
634
+ await this.enhanceTaintAnalysisWithLLM(code, sources, sinks, taintPaths);
635
+ }
636
+ catch (error) {
637
+ logger.warn('LLM-enhanced taint analysis failed', error);
638
+ }
639
+ }
640
+ return {
641
+ graph,
642
+ sources,
643
+ sinks,
644
+ taintPaths,
645
+ };
646
+ }
647
+ async enhanceTaintAnalysisWithLLM(code, sources, sinks, taintPaths) {
648
+ if (!this.llm || taintPaths.length === 0)
649
+ return;
650
+ try {
651
+ const sourcesList = sources.map(s => `${s.type} at line ${s.location.line}`);
652
+ const sinksList = sinks.map(s => `${s.type} at line ${s.location.line}`);
653
+ const messages = this.llm.generateTaintAnalysisPrompt(code.length > 4000 ? code.substring(0, 4000) : code, sourcesList, sinksList);
654
+ const response = await this.llm.chat(messages, {
655
+ temperature: 0.2,
656
+ maxTokens: 2000,
657
+ });
658
+ const jsonMatch = response.content.match(/\{[\s\S]*\}/);
659
+ if (jsonMatch) {
660
+ const llmResult = JSON.parse(jsonMatch[0]);
661
+ if (Array.isArray(llmResult.taintPaths)) {
662
+ logger.info(`LLM identified ${llmResult.taintPaths.length} additional taint paths`);
663
+ llmResult.taintPaths.forEach((path) => {
664
+ const exists = taintPaths.some(p => p.source.location.line === path.source?.location?.line &&
665
+ p.sink.location.line === path.sink?.location?.line);
666
+ if (!exists && path.source && path.sink) {
667
+ taintPaths.push({
668
+ source: path.source,
669
+ sink: path.sink,
670
+ path: path.path || [],
671
+ });
672
+ }
673
+ });
674
+ }
675
+ }
676
+ }
677
+ catch (error) {
678
+ logger.debug('LLM taint analysis enhancement failed', error);
679
+ }
680
+ }
681
+ checkTaintedArguments(args, taintMap, taintPaths, _funcName, line) {
682
+ args.forEach((arg) => {
683
+ if (t.isIdentifier(arg) && taintMap.has(arg.name)) {
684
+ const taintInfo = taintMap.get(arg.name);
685
+ taintPaths.push({
686
+ source: {
687
+ type: taintInfo.sourceType,
688
+ location: { file: 'current', line: taintInfo.sourceLine },
689
+ },
690
+ sink: {
691
+ type: 'eval',
692
+ location: { file: 'current', line },
693
+ },
694
+ path: [
695
+ { file: 'current', line: taintInfo.sourceLine },
696
+ { file: 'current', line },
697
+ ],
698
+ });
699
+ }
700
+ });
701
+ }
702
+ identifySecurityRisks(code, aiAnalysis) {
703
+ const risks = [];
704
+ if (Array.isArray(aiAnalysis.securityRisks)) {
705
+ aiAnalysis.securityRisks.forEach((risk) => {
706
+ if (typeof risk === 'object' && risk !== null) {
707
+ const r = risk;
708
+ risks.push({
709
+ type: r.type || 'other',
710
+ severity: r.severity || 'low',
711
+ location: { file: 'current', line: r.location?.line || 0 },
712
+ description: r.description || '',
713
+ recommendation: r.recommendation || '',
714
+ });
715
+ }
716
+ });
717
+ }
718
+ try {
719
+ const ast = parser.parse(code, {
720
+ sourceType: 'module',
721
+ plugins: ['jsx', 'typescript'],
722
+ });
723
+ traverse(ast, {
724
+ AssignmentExpression(path) {
725
+ const left = path.node.left;
726
+ const line = path.node.loc?.start.line || 0;
727
+ if (t.isMemberExpression(left) && t.isIdentifier(left.property)) {
728
+ const propName = left.property.name;
729
+ if (['innerHTML', 'outerHTML', 'insertAdjacentHTML'].includes(propName)) {
730
+ risks.push({
731
+ type: 'xss',
732
+ severity: 'high',
733
+ location: { file: 'current', line },
734
+ description: `Potential XSS vulnerability: Direct assignment to ${propName} without sanitization`,
735
+ recommendation: 'Use textContent for plain text, or DOMPurify.sanitize() for HTML content',
736
+ });
737
+ }
738
+ if (propName === 'write' && t.isIdentifier(left.object) && left.object.name === 'document') {
739
+ risks.push({
740
+ type: 'xss',
741
+ severity: 'high',
742
+ location: { file: 'current', line },
743
+ description: 'Dangerous use of document.write() which can lead to XSS',
744
+ recommendation: 'Use modern DOM manipulation methods instead',
745
+ });
746
+ }
747
+ }
748
+ },
749
+ CallExpression(path) {
750
+ const callee = path.node.callee;
751
+ const line = path.node.loc?.start.line || 0;
752
+ if (t.isIdentifier(callee)) {
753
+ if (callee.name === 'eval') {
754
+ risks.push({
755
+ type: 'other',
756
+ severity: 'critical',
757
+ location: { file: 'current', line },
758
+ description: 'Critical: Use of eval() allows arbitrary code execution',
759
+ recommendation: 'Refactor to avoid eval(). Use JSON.parse() for data, or proper function calls',
760
+ });
761
+ }
762
+ if (callee.name === 'Function') {
763
+ risks.push({
764
+ type: 'other',
765
+ severity: 'critical',
766
+ location: { file: 'current', line },
767
+ description: 'Critical: Function constructor allows code injection',
768
+ recommendation: 'Use regular function declarations or arrow functions',
769
+ });
770
+ }
771
+ if (['setTimeout', 'setInterval'].includes(callee.name)) {
772
+ const firstArg = path.node.arguments[0];
773
+ if (t.isStringLiteral(firstArg) || (t.isIdentifier(firstArg) && firstArg.name !== 'function')) {
774
+ risks.push({
775
+ type: 'other',
776
+ severity: 'medium',
777
+ location: { file: 'current', line },
778
+ description: `${callee.name}() with string argument can lead to code injection`,
779
+ recommendation: `Use ${callee.name}() with function reference instead of string`,
780
+ });
781
+ }
782
+ }
783
+ }
784
+ if (t.isMemberExpression(callee) && t.isIdentifier(callee.property)) {
785
+ const methodName = callee.property.name;
786
+ if (['query', 'execute', 'exec', 'run'].includes(methodName)) {
787
+ const firstArg = path.node.arguments[0];
788
+ if (t.isBinaryExpression(firstArg) || t.isTemplateLiteral(firstArg)) {
789
+ risks.push({
790
+ type: 'sql-injection',
791
+ severity: 'critical',
792
+ location: { file: 'current', line },
793
+ description: 'Potential SQL injection: Query built with string concatenation',
794
+ recommendation: 'Use parameterized queries or prepared statements',
795
+ });
796
+ }
797
+ }
798
+ }
799
+ },
800
+ MemberExpression(path) {
801
+ const obj = path.node.object;
802
+ const prop = path.node.property;
803
+ const line = path.node.loc?.start.line || 0;
804
+ if (t.isIdentifier(obj) && obj.name === 'Math' &&
805
+ t.isIdentifier(prop) && prop.name === 'random') {
806
+ const parent = path.parent;
807
+ if (t.isCallExpression(parent) || t.isBinaryExpression(parent)) {
808
+ risks.push({
809
+ type: 'other',
810
+ severity: 'medium',
811
+ location: { file: 'current', line },
812
+ description: 'Math.random() is not cryptographically secure',
813
+ recommendation: 'Use crypto.getRandomValues() or crypto.randomBytes() for security-sensitive operations',
814
+ });
815
+ }
816
+ }
817
+ },
818
+ VariableDeclarator(path) {
819
+ const id = path.node.id;
820
+ const init = path.node.init;
821
+ const line = path.node.loc?.start.line || 0;
822
+ if (t.isIdentifier(id) && t.isStringLiteral(init)) {
823
+ const varName = id.name.toLowerCase();
824
+ const value = init.value;
825
+ const sensitivePatterns = [
826
+ { pattern: /(password|passwd|pwd)/i, type: 'password' },
827
+ { pattern: /(api[_-]?key|apikey)/i, type: 'API key' },
828
+ { pattern: /(secret|token|auth)/i, type: 'secret' },
829
+ { pattern: /(private[_-]?key|privatekey)/i, type: 'private key' },
830
+ ];
831
+ for (const { pattern, type } of sensitivePatterns) {
832
+ if (pattern.test(varName) && value.length > 8) {
833
+ risks.push({
834
+ type: 'other',
835
+ severity: 'critical',
836
+ location: { file: 'current', line },
837
+ description: `Hardcoded ${type} detected in source code`,
838
+ recommendation: `Store ${type} in environment variables or secure configuration`,
839
+ });
840
+ break;
841
+ }
842
+ }
843
+ }
844
+ },
845
+ });
846
+ }
847
+ catch (error) {
848
+ logger.warn('Static security analysis failed', error);
849
+ }
850
+ const uniqueRisks = risks.filter((risk, index, self) => index === self.findIndex((r) => r.type === risk.type && r.location.line === risk.location.line));
851
+ return uniqueRisks;
852
+ }
853
+ calculateQualityScore(structure, securityRisks, aiAnalysis, complexityMetrics, antiPatterns) {
854
+ let score = 100;
855
+ let securityScore = 100;
856
+ securityRisks.forEach((risk) => {
857
+ if (risk.severity === 'critical')
858
+ securityScore -= 20;
859
+ else if (risk.severity === 'high')
860
+ securityScore -= 10;
861
+ else if (risk.severity === 'medium')
862
+ securityScore -= 5;
863
+ else
864
+ securityScore -= 2;
865
+ });
866
+ securityScore = Math.max(0, securityScore);
867
+ let complexityScore = 100;
868
+ if (complexityMetrics) {
869
+ if (complexityMetrics.cyclomaticComplexity > 20)
870
+ complexityScore -= 30;
871
+ else if (complexityMetrics.cyclomaticComplexity > 10)
872
+ complexityScore -= 15;
873
+ else if (complexityMetrics.cyclomaticComplexity > 5)
874
+ complexityScore -= 5;
875
+ if (complexityMetrics.cognitiveComplexity > 15)
876
+ complexityScore -= 20;
877
+ else if (complexityMetrics.cognitiveComplexity > 10)
878
+ complexityScore -= 10;
879
+ }
880
+ else {
881
+ const avgComplexity = structure.functions.reduce((sum, fn) => sum + fn.complexity, 0) / (structure.functions.length || 1);
882
+ if (avgComplexity > 10)
883
+ complexityScore -= 20;
884
+ else if (avgComplexity > 5)
885
+ complexityScore -= 10;
886
+ }
887
+ complexityScore = Math.max(0, complexityScore);
888
+ let maintainabilityScore = complexityMetrics?.maintainabilityIndex || 70;
889
+ let codeSmellScore = 100;
890
+ if (antiPatterns) {
891
+ antiPatterns.forEach((pattern) => {
892
+ if (pattern.severity === 'high')
893
+ codeSmellScore -= 10;
894
+ else if (pattern.severity === 'medium')
895
+ codeSmellScore -= 5;
896
+ else
897
+ codeSmellScore -= 2;
898
+ });
899
+ }
900
+ codeSmellScore = Math.max(0, codeSmellScore);
901
+ let aiScore = 70;
902
+ if (typeof aiAnalysis.qualityScore === 'number') {
903
+ aiScore = aiAnalysis.qualityScore;
904
+ }
905
+ score =
906
+ securityScore * 0.40 +
907
+ complexityScore * 0.25 +
908
+ maintainabilityScore * 0.20 +
909
+ codeSmellScore * 0.15;
910
+ if (typeof aiAnalysis.qualityScore === 'number') {
911
+ score = (score + aiScore) / 2;
912
+ }
913
+ return Math.round(Math.max(0, Math.min(100, score)));
914
+ }
915
+ checkSanitizer(node, sanitizers) {
916
+ const { callee } = node;
917
+ if (t.isIdentifier(callee)) {
918
+ return sanitizers.has(callee.name);
919
+ }
920
+ if (t.isMemberExpression(callee)) {
921
+ const fullName = this.getMemberExpressionName(callee);
922
+ return sanitizers.has(fullName);
923
+ }
924
+ return false;
925
+ }
926
+ getMemberExpressionName(node) {
927
+ const parts = [];
928
+ let current = node;
929
+ while (t.isMemberExpression(current)) {
930
+ if (t.isIdentifier(current.property)) {
931
+ parts.unshift(current.property.name);
932
+ }
933
+ current = current.object;
934
+ }
935
+ if (t.isIdentifier(current)) {
936
+ parts.unshift(current.name);
937
+ }
938
+ return parts.join('.');
939
+ }
940
+ detectCodePatterns(code) {
941
+ const patterns = [];
942
+ const antiPatterns = [];
943
+ try {
944
+ const ast = parser.parse(code, {
945
+ sourceType: 'module',
946
+ plugins: ['jsx', 'typescript'],
947
+ });
948
+ traverse(ast, {
949
+ VariableDeclarator(path) {
950
+ const init = path.node.init;
951
+ if (t.isCallExpression(init) &&
952
+ t.isFunctionExpression(init.callee) &&
953
+ init.callee.body.body.some(stmt => t.isReturnStatement(stmt) &&
954
+ t.isObjectExpression(stmt.argument))) {
955
+ patterns.push({
956
+ name: 'Singleton Pattern',
957
+ location: path.node.loc?.start.line || 0,
958
+ description: 'IIFE returning object (Singleton pattern)',
959
+ });
960
+ }
961
+ },
962
+ ClassDeclaration(path) {
963
+ const methods = path.node.body.body.filter(m => t.isClassMethod(m));
964
+ const methodNames = methods.map(m => t.isClassMethod(m) && t.isIdentifier(m.key) ? m.key.name : '');
965
+ if (methodNames.includes('subscribe') &&
966
+ methodNames.includes('unsubscribe') &&
967
+ methodNames.includes('notify')) {
968
+ patterns.push({
969
+ name: 'Observer Pattern',
970
+ location: path.node.loc?.start.line || 0,
971
+ description: 'Class with subscribe/unsubscribe/notify methods',
972
+ });
973
+ }
974
+ },
975
+ FunctionDeclaration(path) {
976
+ const loc = path.node.loc;
977
+ if (loc) {
978
+ const lines = loc.end.line - loc.start.line;
979
+ if (lines > 50) {
980
+ antiPatterns.push({
981
+ name: 'Long Function',
982
+ location: loc.start.line,
983
+ severity: 'medium',
984
+ recommendation: `Function is ${lines} lines long. Consider breaking it into smaller functions (max 50 lines)`,
985
+ });
986
+ }
987
+ }
988
+ },
989
+ IfStatement(path) {
990
+ let depth = 0;
991
+ let current = path.parentPath;
992
+ while (current) {
993
+ if (current.isIfStatement() ||
994
+ current.isForStatement() ||
995
+ current.isWhileStatement()) {
996
+ depth++;
997
+ }
998
+ current = current.parentPath;
999
+ }
1000
+ if (depth > 3) {
1001
+ antiPatterns.push({
1002
+ name: 'Deep Nesting',
1003
+ location: path.node.loc?.start.line || 0,
1004
+ severity: 'medium',
1005
+ recommendation: `Nesting depth is ${depth}. Consider extracting to separate functions or using early returns`,
1006
+ });
1007
+ }
1008
+ },
1009
+ NumericLiteral(path) {
1010
+ const value = path.node.value;
1011
+ const parent = path.parent;
1012
+ const commonNumbers = [0, 1, -1, 2, 10, 100, 1000];
1013
+ if (commonNumbers.includes(value))
1014
+ return;
1015
+ if (t.isMemberExpression(parent) && parent.property === path.node)
1016
+ return;
1017
+ if (t.isAssignmentPattern(parent))
1018
+ return;
1019
+ antiPatterns.push({
1020
+ name: 'Magic Number',
1021
+ location: path.node.loc?.start.line || 0,
1022
+ severity: 'low',
1023
+ recommendation: `Replace magic number ${value} with a named constant`,
1024
+ });
1025
+ },
1026
+ CatchClause(path) {
1027
+ const body = path.node.body.body;
1028
+ if (body.length === 0) {
1029
+ antiPatterns.push({
1030
+ name: 'Empty Catch Block',
1031
+ location: path.node.loc?.start.line || 0,
1032
+ severity: 'high',
1033
+ recommendation: 'Empty catch block swallows errors. Add proper error handling or logging',
1034
+ });
1035
+ }
1036
+ },
1037
+ VariableDeclaration(path) {
1038
+ if (path.node.kind === 'var') {
1039
+ antiPatterns.push({
1040
+ name: 'Use of var',
1041
+ location: path.node.loc?.start.line || 0,
1042
+ severity: 'low',
1043
+ recommendation: 'Use let or const instead of var for better scoping',
1044
+ });
1045
+ }
1046
+ },
1047
+ });
1048
+ const duplicates = this.detectDuplicateCode(ast);
1049
+ duplicates.forEach(dup => {
1050
+ antiPatterns.push({
1051
+ name: 'Duplicate Code',
1052
+ location: dup.location,
1053
+ severity: 'medium',
1054
+ recommendation: `Duplicate code found at lines ${dup.location} and ${dup.duplicateLocation}. Extract into a reusable function.`,
1055
+ });
1056
+ });
1057
+ }
1058
+ catch (error) {
1059
+ logger.warn('Code pattern detection failed', error);
1060
+ }
1061
+ return { patterns, antiPatterns };
1062
+ }
1063
+ analyzeComplexityMetrics(code) {
1064
+ let cyclomaticComplexity = 1;
1065
+ let cognitiveComplexity = 0;
1066
+ let operators = 0;
1067
+ let operands = 0;
1068
+ const uniqueOperators = new Set();
1069
+ const uniqueOperands = new Set();
1070
+ try {
1071
+ const ast = parser.parse(code, {
1072
+ sourceType: 'module',
1073
+ plugins: ['jsx', 'typescript'],
1074
+ });
1075
+ let nestingLevel = 0;
1076
+ traverse(ast, {
1077
+ IfStatement() { cyclomaticComplexity++; },
1078
+ SwitchCase() { cyclomaticComplexity++; },
1079
+ ForStatement() { cyclomaticComplexity++; },
1080
+ WhileStatement() { cyclomaticComplexity++; },
1081
+ DoWhileStatement() { cyclomaticComplexity++; },
1082
+ ConditionalExpression() { cyclomaticComplexity++; },
1083
+ LogicalExpression(path) {
1084
+ if (path.node.operator === '&&' || path.node.operator === '||') {
1085
+ cyclomaticComplexity++;
1086
+ }
1087
+ },
1088
+ CatchClause() { cyclomaticComplexity++; },
1089
+ 'IfStatement|ForStatement|WhileStatement|DoWhileStatement': {
1090
+ enter() {
1091
+ nestingLevel++;
1092
+ cognitiveComplexity += nestingLevel;
1093
+ },
1094
+ exit() {
1095
+ nestingLevel--;
1096
+ },
1097
+ },
1098
+ BinaryExpression(path) {
1099
+ operators++;
1100
+ uniqueOperators.add(path.node.operator);
1101
+ },
1102
+ UnaryExpression(path) {
1103
+ operators++;
1104
+ uniqueOperators.add(path.node.operator);
1105
+ },
1106
+ Identifier(path) {
1107
+ operands++;
1108
+ uniqueOperands.add(path.node.name);
1109
+ },
1110
+ NumericLiteral(path) {
1111
+ operands++;
1112
+ uniqueOperands.add(String(path.node.value));
1113
+ },
1114
+ StringLiteral(path) {
1115
+ operands++;
1116
+ uniqueOperands.add(path.node.value);
1117
+ },
1118
+ });
1119
+ }
1120
+ catch (error) {
1121
+ logger.warn('Complexity metrics calculation failed', error);
1122
+ }
1123
+ const n1 = uniqueOperators.size;
1124
+ const n2 = uniqueOperands.size;
1125
+ const N1 = operators;
1126
+ const N2 = operands;
1127
+ const vocabulary = n1 + n2;
1128
+ const length = N1 + N2;
1129
+ const difficulty = (n1 / 2) * (N2 / (n2 || 1));
1130
+ const effort = difficulty * length;
1131
+ const volume = length * Math.log2(vocabulary || 1);
1132
+ const loc = code.split('\n').length;
1133
+ const maintainabilityIndex = Math.max(0, 171 - 5.2 * Math.log(volume || 1) - 0.23 * cyclomaticComplexity - 16.2 * Math.log(loc));
1134
+ return {
1135
+ cyclomaticComplexity,
1136
+ cognitiveComplexity,
1137
+ maintainabilityIndex: Math.round(maintainabilityIndex),
1138
+ halsteadMetrics: {
1139
+ vocabulary,
1140
+ length,
1141
+ difficulty: Math.round(difficulty * 100) / 100,
1142
+ effort: Math.round(effort),
1143
+ },
1144
+ };
1145
+ }
1146
+ detectDuplicateCode(ast) {
1147
+ const duplicates = [];
1148
+ const codeBlocks = [];
1149
+ try {
1150
+ const self = this;
1151
+ traverse(ast, {
1152
+ FunctionDeclaration(path) {
1153
+ const hash = self.computeASTHash(path.node);
1154
+ const normalized = self.normalizeCode(path.node);
1155
+ codeBlocks.push({
1156
+ node: path.node,
1157
+ hash,
1158
+ location: path.node.loc?.start.line || 0,
1159
+ normalizedCode: normalized,
1160
+ });
1161
+ },
1162
+ FunctionExpression(path) {
1163
+ const hash = self.computeASTHash(path.node);
1164
+ const normalized = self.normalizeCode(path.node);
1165
+ codeBlocks.push({
1166
+ node: path.node,
1167
+ hash,
1168
+ location: path.node.loc?.start.line || 0,
1169
+ normalizedCode: normalized,
1170
+ });
1171
+ },
1172
+ ArrowFunctionExpression(path) {
1173
+ const hash = self.computeASTHash(path.node);
1174
+ const normalized = self.normalizeCode(path.node);
1175
+ codeBlocks.push({
1176
+ node: path.node,
1177
+ hash,
1178
+ location: path.node.loc?.start.line || 0,
1179
+ normalizedCode: normalized,
1180
+ });
1181
+ },
1182
+ ClassMethod(path) {
1183
+ const hash = self.computeASTHash(path.node);
1184
+ const normalized = self.normalizeCode(path.node);
1185
+ codeBlocks.push({
1186
+ node: path.node,
1187
+ hash,
1188
+ location: path.node.loc?.start.line || 0,
1189
+ normalizedCode: normalized,
1190
+ });
1191
+ },
1192
+ });
1193
+ for (let i = 0; i < codeBlocks.length; i++) {
1194
+ for (let j = i + 1; j < codeBlocks.length; j++) {
1195
+ const block1 = codeBlocks[i];
1196
+ const block2 = codeBlocks[j];
1197
+ if (block1.hash === block2.hash) {
1198
+ duplicates.push({
1199
+ location: block1.location,
1200
+ duplicateLocation: block2.location,
1201
+ similarity: 1.0,
1202
+ });
1203
+ continue;
1204
+ }
1205
+ const similarity = this.calculateCodeSimilarity(block1.normalizedCode, block2.normalizedCode);
1206
+ if (similarity >= 0.85) {
1207
+ duplicates.push({
1208
+ location: block1.location,
1209
+ duplicateLocation: block2.location,
1210
+ similarity,
1211
+ });
1212
+ }
1213
+ }
1214
+ }
1215
+ }
1216
+ catch (error) {
1217
+ logger.debug('Duplicate code detection failed', error);
1218
+ }
1219
+ return duplicates;
1220
+ }
1221
+ computeASTHash(node) {
1222
+ const normalized = JSON.stringify(node, (key, value) => {
1223
+ if (['loc', 'start', 'end', 'range'].includes(key)) {
1224
+ return undefined;
1225
+ }
1226
+ if (key === 'comments' || key === 'leadingComments' || key === 'trailingComments') {
1227
+ return undefined;
1228
+ }
1229
+ return value;
1230
+ });
1231
+ let hash = 0;
1232
+ for (let i = 0; i < normalized.length; i++) {
1233
+ const char = normalized.charCodeAt(i);
1234
+ hash = ((hash << 5) - hash) + char;
1235
+ hash = hash & hash;
1236
+ }
1237
+ return hash.toString(36);
1238
+ }
1239
+ normalizeCode(node) {
1240
+ let identifierCounter = 0;
1241
+ const identifierMap = new Map();
1242
+ const clonedNode = t.cloneNode(node, true, false);
1243
+ traverse(t.file(t.program([clonedNode])), {
1244
+ Identifier(path) {
1245
+ const name = path.node.name;
1246
+ const reserved = ['console', 'window', 'document', 'Math', 'JSON', 'Array', 'Object', 'String', 'Number'];
1247
+ if (reserved.includes(name))
1248
+ return;
1249
+ if (!identifierMap.has(name)) {
1250
+ identifierMap.set(name, `VAR_${identifierCounter++}`);
1251
+ }
1252
+ path.node.name = identifierMap.get(name);
1253
+ },
1254
+ StringLiteral(path) {
1255
+ path.node.value = 'STRING';
1256
+ },
1257
+ NumericLiteral(path) {
1258
+ path.node.value = 0;
1259
+ },
1260
+ });
1261
+ return JSON.stringify(clonedNode);
1262
+ }
1263
+ calculateCodeSimilarity(code1, code2) {
1264
+ const len1 = code1.length;
1265
+ const len2 = code2.length;
1266
+ if (Math.abs(len1 - len2) > Math.max(len1, len2) * 0.3) {
1267
+ return 0;
1268
+ }
1269
+ const matrix = Array.from({ length: len1 + 1 }, () => Array.from({ length: len2 + 1 }, () => 0));
1270
+ for (let i = 0; i <= len1; i++) {
1271
+ matrix[i][0] = i;
1272
+ }
1273
+ for (let j = 0; j <= len2; j++) {
1274
+ matrix[0][j] = j;
1275
+ }
1276
+ for (let i = 1; i <= len1; i++) {
1277
+ for (let j = 1; j <= len2; j++) {
1278
+ const cost = code1[i - 1] === code2[j - 1] ? 0 : 1;
1279
+ matrix[i][j] = Math.min(matrix[i - 1][j] + 1, matrix[i][j - 1] + 1, matrix[i - 1][j - 1] + cost);
1280
+ }
1281
+ }
1282
+ const distance = matrix[len1][len2];
1283
+ const maxLen = Math.max(len1, len2);
1284
+ return 1 - (distance / maxLen);
1285
+ }
1286
+ }
1287
+ //# sourceMappingURL=CodeAnalyzer.js.map