@mseep/anything-analyzer 3.6.50

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 (172) hide show
  1. package/.codeartsdoer/.codebaseignore +0 -0
  2. package/.codeartsdoer/AGENTS.md +12 -0
  3. package/.github/workflows/build.yml +146 -0
  4. package/README.en.md +264 -0
  5. package/README.md +276 -0
  6. package/RELEASE_NOTES.md +16 -0
  7. package/USAGE.md +490 -0
  8. package/color-preview-r3.html +414 -0
  9. package/color-preview.html +414 -0
  10. package/dev-app-update.yml +3 -0
  11. package/electron-builder.yml +36 -0
  12. package/electron.vite.config.ts +40 -0
  13. package/package.json +53 -0
  14. package/report-2026-04-13-copilot-claude-sonnet-4.6.md +955 -0
  15. package/resources/doloffer-logo.png +0 -0
  16. package/resources/entitlements.mac.plist +12 -0
  17. package/resources/icon.ico +0 -0
  18. package/resources/icon.png +0 -0
  19. package/src/main/ai/ai-analyzer.ts +517 -0
  20. package/src/main/ai/crypto-script-extractor.ts +206 -0
  21. package/src/main/ai/data-assembler.ts +205 -0
  22. package/src/main/ai/llm-router.ts +1120 -0
  23. package/src/main/ai/prompt-builder.ts +349 -0
  24. package/src/main/ai/scene-detector.ts +302 -0
  25. package/src/main/capture/capture-engine.ts +130 -0
  26. package/src/main/capture/interaction-recorder.ts +171 -0
  27. package/src/main/capture/js-injector.ts +57 -0
  28. package/src/main/capture/replay-engine.ts +256 -0
  29. package/src/main/capture/storage-collector.ts +76 -0
  30. package/src/main/cdp/cdp-manager.ts +233 -0
  31. package/src/main/db/database.ts +41 -0
  32. package/src/main/db/migrations.ts +235 -0
  33. package/src/main/db/repositories.ts +574 -0
  34. package/src/main/fingerprint/http-spoofing.ts +48 -0
  35. package/src/main/fingerprint/presets.ts +173 -0
  36. package/src/main/fingerprint/profile-generator.ts +115 -0
  37. package/src/main/fingerprint/profile-store.ts +52 -0
  38. package/src/main/index.ts +260 -0
  39. package/src/main/ipc.ts +856 -0
  40. package/src/main/logger.ts +42 -0
  41. package/src/main/mcp/mcp-config.ts +66 -0
  42. package/src/main/mcp/mcp-manager.ts +155 -0
  43. package/src/main/mcp/mcp-server.ts +1038 -0
  44. package/src/main/prompt-templates.ts +170 -0
  45. package/src/main/proxy/ca-manager.ts +204 -0
  46. package/src/main/proxy/cert-download-page.ts +171 -0
  47. package/src/main/proxy/cert-installer.ts +242 -0
  48. package/src/main/proxy/mitm-proxy-config.ts +37 -0
  49. package/src/main/proxy/mitm-proxy-server.ts +1085 -0
  50. package/src/main/proxy/system-proxy.ts +248 -0
  51. package/src/main/session/session-manager.ts +724 -0
  52. package/src/main/tab-manager.ts +582 -0
  53. package/src/main/updater.ts +111 -0
  54. package/src/main/window.ts +235 -0
  55. package/src/preload/hook-script.ts +270 -0
  56. package/src/preload/index.ts +211 -0
  57. package/src/preload/interaction-hook.ts +286 -0
  58. package/src/preload/stealth-script.ts +302 -0
  59. package/src/preload/target-preload.ts +15 -0
  60. package/src/renderer/App.tsx +656 -0
  61. package/src/renderer/components/AiLogDetail.tsx +173 -0
  62. package/src/renderer/components/AiLogList.tsx +101 -0
  63. package/src/renderer/components/AiLogView.module.css +364 -0
  64. package/src/renderer/components/AiLogView.tsx +86 -0
  65. package/src/renderer/components/AnalyzeBar.module.css +79 -0
  66. package/src/renderer/components/AnalyzeBar.tsx +104 -0
  67. package/src/renderer/components/BrowserPanel.module.css +67 -0
  68. package/src/renderer/components/BrowserPanel.tsx +90 -0
  69. package/src/renderer/components/ControlBar.module.css +47 -0
  70. package/src/renderer/components/ControlBar.tsx +205 -0
  71. package/src/renderer/components/HookLog.tsx +132 -0
  72. package/src/renderer/components/InteractionLog.tsx +183 -0
  73. package/src/renderer/components/MCPServerModal.tsx +427 -0
  74. package/src/renderer/components/PromptTemplateModal.tsx +254 -0
  75. package/src/renderer/components/ReportView.module.css +413 -0
  76. package/src/renderer/components/ReportView.tsx +429 -0
  77. package/src/renderer/components/RequestDetail.module.css +191 -0
  78. package/src/renderer/components/RequestDetail.tsx +202 -0
  79. package/src/renderer/components/RequestLog.module.css +69 -0
  80. package/src/renderer/components/RequestLog.tsx +208 -0
  81. package/src/renderer/components/SessionList.module.css +245 -0
  82. package/src/renderer/components/SessionList.tsx +247 -0
  83. package/src/renderer/components/SettingsModal.tsx +100 -0
  84. package/src/renderer/components/StatusBar.module.css +44 -0
  85. package/src/renderer/components/StatusBar.tsx +102 -0
  86. package/src/renderer/components/StorageView.module.css +41 -0
  87. package/src/renderer/components/StorageView.tsx +178 -0
  88. package/src/renderer/components/TabBar.module.css +88 -0
  89. package/src/renderer/components/TabBar.tsx +70 -0
  90. package/src/renderer/components/Titlebar.module.css +254 -0
  91. package/src/renderer/components/Titlebar.tsx +169 -0
  92. package/src/renderer/components/settings/FingerprintSection.tsx +198 -0
  93. package/src/renderer/components/settings/GeneralSection.tsx +164 -0
  94. package/src/renderer/components/settings/LLMSection.tsx +148 -0
  95. package/src/renderer/components/settings/MCPServerSection.tsx +136 -0
  96. package/src/renderer/components/settings/MitmProxySection.tsx +320 -0
  97. package/src/renderer/components/settings/ProxySection.tsx +110 -0
  98. package/src/renderer/css-modules.d.ts +4 -0
  99. package/src/renderer/hooks/useCapture.ts +383 -0
  100. package/src/renderer/hooks/useConfirm.tsx +91 -0
  101. package/src/renderer/hooks/useSession.ts +136 -0
  102. package/src/renderer/hooks/useTabs.ts +103 -0
  103. package/src/renderer/i18n/en.ts +167 -0
  104. package/src/renderer/i18n/index.ts +47 -0
  105. package/src/renderer/i18n/zh.ts +170 -0
  106. package/src/renderer/index.html +12 -0
  107. package/src/renderer/main.tsx +15 -0
  108. package/src/renderer/styles/global.css +144 -0
  109. package/src/renderer/styles/themes/ayu-dark.css +59 -0
  110. package/src/renderer/styles/themes/catppuccin.css +59 -0
  111. package/src/renderer/styles/themes/discord.css +59 -0
  112. package/src/renderer/styles/themes/dracula.css +59 -0
  113. package/src/renderer/styles/themes/github-dark.css +59 -0
  114. package/src/renderer/styles/themes/gruvbox.css +59 -0
  115. package/src/renderer/styles/themes/index.css +11 -0
  116. package/src/renderer/styles/themes/light.css +59 -0
  117. package/src/renderer/styles/themes/nord.css +59 -0
  118. package/src/renderer/styles/themes/one-dark.css +59 -0
  119. package/src/renderer/styles/themes/tokyo-night.css +59 -0
  120. package/src/renderer/styles/tokens.css +137 -0
  121. package/src/renderer/theme.ts +31 -0
  122. package/src/renderer/ui/Badge.module.css +38 -0
  123. package/src/renderer/ui/Badge.tsx +36 -0
  124. package/src/renderer/ui/Button.module.css +142 -0
  125. package/src/renderer/ui/Button.tsx +46 -0
  126. package/src/renderer/ui/Collapse.module.css +49 -0
  127. package/src/renderer/ui/Collapse.tsx +57 -0
  128. package/src/renderer/ui/CopyableBlock.module.css +56 -0
  129. package/src/renderer/ui/CopyableBlock.tsx +42 -0
  130. package/src/renderer/ui/Empty.module.css +19 -0
  131. package/src/renderer/ui/Empty.tsx +34 -0
  132. package/src/renderer/ui/Icons.tsx +346 -0
  133. package/src/renderer/ui/Input.module.css +103 -0
  134. package/src/renderer/ui/Input.tsx +94 -0
  135. package/src/renderer/ui/InputNumber.module.css +68 -0
  136. package/src/renderer/ui/InputNumber.tsx +104 -0
  137. package/src/renderer/ui/Modal.module.css +83 -0
  138. package/src/renderer/ui/Modal.tsx +67 -0
  139. package/src/renderer/ui/Popconfirm.module.css +73 -0
  140. package/src/renderer/ui/Popconfirm.tsx +74 -0
  141. package/src/renderer/ui/Progress.module.css +35 -0
  142. package/src/renderer/ui/Progress.tsx +30 -0
  143. package/src/renderer/ui/Select.module.css +91 -0
  144. package/src/renderer/ui/Select.tsx +100 -0
  145. package/src/renderer/ui/Spinner.module.css +44 -0
  146. package/src/renderer/ui/Spinner.tsx +27 -0
  147. package/src/renderer/ui/Switch.module.css +39 -0
  148. package/src/renderer/ui/Switch.tsx +43 -0
  149. package/src/renderer/ui/Tabs.module.css +76 -0
  150. package/src/renderer/ui/Tabs.tsx +53 -0
  151. package/src/renderer/ui/Tag.module.css +66 -0
  152. package/src/renderer/ui/Tag.tsx +47 -0
  153. package/src/renderer/ui/Timeline.module.css +42 -0
  154. package/src/renderer/ui/Timeline.tsx +29 -0
  155. package/src/renderer/ui/Toast.module.css +99 -0
  156. package/src/renderer/ui/Toast.tsx +90 -0
  157. package/src/renderer/ui/Tooltip.module.css +26 -0
  158. package/src/renderer/ui/Tooltip.tsx +23 -0
  159. package/src/renderer/ui/VirtualTable.module.css +230 -0
  160. package/src/renderer/ui/VirtualTable.tsx +416 -0
  161. package/src/renderer/ui/index.ts +55 -0
  162. package/src/shared/types.ts +695 -0
  163. package/tests/main/ai/crypto-script-extractor.test.ts +281 -0
  164. package/tests/main/ai/llm-router.test.ts +1537 -0
  165. package/tests/main/ai/prompt-builder.test.ts +178 -0
  166. package/tests/main/ai/scene-detector.test.ts +212 -0
  167. package/tests/main/db/migrations.test.ts +134 -0
  168. package/tests/main/release-workflow.test.ts +59 -0
  169. package/tsconfig.json +7 -0
  170. package/tsconfig.node.json +23 -0
  171. package/tsconfig.web.json +24 -0
  172. package/vitest.config.ts +13 -0
@@ -0,0 +1,281 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { CryptoScriptExtractor } from "../../../src/main/ai/crypto-script-extractor";
3
+
4
+ // ---- Helpers ----
5
+
6
+ function makeJsRequest(url: string, body: string): any {
7
+ return {
8
+ id: "req-" + url,
9
+ session_id: "test-session",
10
+ sequence: 1,
11
+ timestamp: Date.now(),
12
+ method: "GET",
13
+ url,
14
+ request_headers: "{}",
15
+ request_body: null,
16
+ status_code: 200,
17
+ response_headers: '{"content-type":"application/javascript"}',
18
+ response_body: body,
19
+ content_type: "application/javascript",
20
+ initiator: null,
21
+ duration_ms: 100,
22
+ is_streaming: false,
23
+ is_websocket: false,
24
+ };
25
+ }
26
+
27
+ function makeApiRequest(): any {
28
+ return {
29
+ id: "req-api",
30
+ session_id: "test-session",
31
+ sequence: 2,
32
+ timestamp: Date.now(),
33
+ method: "POST",
34
+ url: "https://example.com/api/login",
35
+ request_headers: "{}",
36
+ request_body: '{"user":"test"}',
37
+ status_code: 200,
38
+ response_headers: '{"content-type":"application/json"}',
39
+ response_body: '{"token":"abc"}',
40
+ content_type: "application/json",
41
+ initiator: null,
42
+ duration_ms: 50,
43
+ is_streaming: false,
44
+ is_websocket: false,
45
+ };
46
+ }
47
+
48
+ function makeMockRepos(
49
+ requests: any[] = [],
50
+ hooks: any[] = [],
51
+ ) {
52
+ const requestsRepo = { findBySession: (_id: string) => requests };
53
+ const jsHooksRepo = { findBySession: (_id: string) => hooks };
54
+ return { requestsRepo, jsHooksRepo };
55
+ }
56
+
57
+ /**
58
+ * Build a JS source with filler lines, inserting a given code snippet at a
59
+ * specific line index.
60
+ */
61
+ function buildJsBody(
62
+ totalLines: number,
63
+ inserts: { line: number; code: string }[],
64
+ ): string {
65
+ const lines: string[] = [];
66
+ for (let i = 0; i < totalLines; i++) {
67
+ const insert = inserts.find((ins) => ins.line === i);
68
+ lines.push(insert ? insert.code : `// filler line ${i}`);
69
+ }
70
+ return lines.join("\n");
71
+ }
72
+
73
+ // ---- Tests ----
74
+
75
+ describe("CryptoScriptExtractor", () => {
76
+ const SESSION = "test-session";
77
+
78
+ // 1. Returns empty array when no JS requests exist
79
+ describe("no JS requests", () => {
80
+ it("should return empty array when repo only has non-JS requests", () => {
81
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([makeApiRequest()]);
82
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
83
+ const result = extractor.extract(SESSION);
84
+ expect(result).toEqual([]);
85
+ });
86
+ });
87
+
88
+ // 2. Returns empty array when JS has no crypto patterns
89
+ describe("JS with no crypto patterns", () => {
90
+ it("should return empty array when JS body has no crypto-related code", () => {
91
+ const js = makeJsRequest(
92
+ "https://example.com/app.js",
93
+ 'console.log("hello")\nvar x = 1;\nfunction add(a, b) { return a + b; }',
94
+ );
95
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
96
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
97
+ const result = extractor.extract(SESSION);
98
+ expect(result).toEqual([]);
99
+ });
100
+ });
101
+
102
+ // 3. Detects Tier 1 patterns (CryptoJS)
103
+ describe("Tier 1 detection", () => {
104
+ it("should detect CryptoJS.AES.encrypt as tier 1", () => {
105
+ const body = buildJsBody(10, [
106
+ { line: 5, code: 'var encrypted = CryptoJS.AES.encrypt(data, key);' },
107
+ ]);
108
+ const js = makeJsRequest("https://example.com/crypto.js", body);
109
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
110
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
111
+
112
+ const result = extractor.extract(SESSION);
113
+
114
+ expect(result.length).toBeGreaterThanOrEqual(1);
115
+ expect(result[0].tier).toBe(1);
116
+ expect(result[0].matchedPatterns).toContain("CryptoJS");
117
+ expect(result[0].content).toContain("CryptoJS.AES.encrypt");
118
+ expect(result[0].scriptUrl).toBe("https://example.com/crypto.js");
119
+ });
120
+ });
121
+
122
+ // 4. Detects Tier 2 patterns (generic encrypt)
123
+ describe("Tier 2 detection", () => {
124
+ it("should detect function encrypt() as tier 2", () => {
125
+ const body = buildJsBody(10, [
126
+ { line: 4, code: "function encrypt(data) { return data ^ 0xff; }" },
127
+ ]);
128
+ const js = makeJsRequest("https://example.com/util.js", body);
129
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
130
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
131
+
132
+ const result = extractor.extract(SESSION);
133
+
134
+ expect(result.length).toBeGreaterThanOrEqual(1);
135
+ expect(result[0].tier).toBe(2);
136
+ expect(result[0].matchedPatterns).toContain("encrypt(");
137
+ expect(result[0].content).toContain("function encrypt(data)");
138
+ });
139
+ });
140
+
141
+ // 5. Detects Tier 3 patterns (btoa)
142
+ describe("Tier 3 detection", () => {
143
+ it("should detect btoa() as tier 3", () => {
144
+ const body = buildJsBody(10, [
145
+ { line: 3, code: "var encoded = btoa(JSON.stringify(data));" },
146
+ ]);
147
+ const js = makeJsRequest("https://example.com/helpers.js", body);
148
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
149
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
150
+
151
+ const result = extractor.extract(SESSION);
152
+
153
+ expect(result.length).toBeGreaterThanOrEqual(1);
154
+ expect(result[0].tier).toBe(3);
155
+ expect(result[0].matchedPatterns).toContain("btoa(");
156
+ expect(result[0].content).toContain("btoa(JSON.stringify(data))");
157
+ });
158
+ });
159
+
160
+ // 6. Merges overlapping ranges
161
+ describe("merging overlapping ranges", () => {
162
+ it("should merge two crypto patterns within 10 lines into one snippet", () => {
163
+ // CONTEXT_LINES = 30, so two patterns 10 lines apart will have heavily
164
+ // overlapping context windows and should merge into a single snippet.
165
+ const body = buildJsBody(200, [
166
+ { line: 100, code: 'var a = CryptoJS.AES.encrypt(data, key);' },
167
+ { line: 110, code: 'var b = CryptoJS.MD5(data);' },
168
+ ]);
169
+ const js = makeJsRequest("https://example.com/crypto-bundle.js", body);
170
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
171
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
172
+
173
+ const result = extractor.extract(SESSION);
174
+
175
+ // Both patterns should be merged into a single snippet
176
+ expect(result).toHaveLength(1);
177
+ expect(result[0].content).toContain("CryptoJS.AES.encrypt");
178
+ expect(result[0].content).toContain("CryptoJS.MD5");
179
+ expect(result[0].matchedPatterns).toContain("CryptoJS");
180
+ });
181
+ });
182
+
183
+ // 7. Respects budget limit
184
+ describe("budget limit", () => {
185
+ it("should keep total output chars within the specified budget", () => {
186
+ // Create a large JS file with many widely-spaced tier-1 patterns so they
187
+ // do NOT merge, each producing a snippet of ~30+ lines.
188
+ const inserts: { line: number; code: string }[] = [];
189
+ for (let i = 0; i < 20; i++) {
190
+ // Space patterns 200 lines apart so context windows (30 lines each side)
191
+ // never overlap.
192
+ inserts.push({
193
+ line: i * 200,
194
+ code: `var enc${i} = CryptoJS.AES.encrypt(payload${i}, key${i});`,
195
+ });
196
+ }
197
+ const body = buildJsBody(4000, inserts);
198
+ const js = makeJsRequest("https://example.com/big-crypto.js", body);
199
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js]);
200
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
201
+
202
+ const tinyBudget = 500;
203
+ const result = extractor.extract(SESSION, tinyBudget);
204
+
205
+ const totalChars = result.reduce((sum, s) => sum + s.content.length, 0);
206
+ // The budget allows up to budgetChars plus the "[TRUNCATED]" suffix added
207
+ // to the last snippet, so we give a small margin.
208
+ expect(totalChars).toBeLessThanOrEqual(tinyBudget + 20);
209
+ });
210
+ });
211
+
212
+ // 8. Prioritizes by tier
213
+ describe("tier prioritization", () => {
214
+ it("should return Tier 1 snippets before Tier 2 snippets", () => {
215
+ // Place tier-2 pattern in one file, tier-1 in another.
216
+ // They should be sorted tier 1 first regardless of insertion order.
217
+ const js1 = makeJsRequest(
218
+ "https://example.com/tier2.js",
219
+ buildJsBody(10, [{ line: 5, code: "function encrypt(data) { return data; }" }]),
220
+ );
221
+ const js2 = makeJsRequest(
222
+ "https://example.com/tier1.js",
223
+ buildJsBody(10, [{ line: 5, code: "CryptoJS.AES.encrypt(data, key);" }]),
224
+ );
225
+ // Insert tier-2 first so natural order would be wrong
226
+ const { requestsRepo, jsHooksRepo } = makeMockRepos([js1, js2]);
227
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
228
+
229
+ const result = extractor.extract(SESSION);
230
+
231
+ expect(result.length).toBeGreaterThanOrEqual(2);
232
+ expect(result[0].tier).toBe(1);
233
+ expect(result[1].tier).toBe(2);
234
+ });
235
+ });
236
+
237
+ // 9. Hook call stack correlation
238
+ describe("hook call stack correlation", () => {
239
+ it("should prioritize scripts referenced in hook call stacks", () => {
240
+ // Two JS files both with tier-1 patterns. One is referenced by a crypto
241
+ // hook's call stack and should appear first even though both are tier 1.
242
+ const jsCorrelated = makeJsRequest(
243
+ "https://example.com/js/api.js",
244
+ buildJsBody(10, [{ line: 5, code: "crypto.subtle.encrypt(algo, key, data);" }]),
245
+ );
246
+ const jsOther = makeJsRequest(
247
+ "https://example.com/js/other.js",
248
+ buildJsBody(10, [{ line: 5, code: "CryptoJS.AES.encrypt(data, key);" }]),
249
+ );
250
+
251
+ const hooks = [
252
+ {
253
+ id: 1,
254
+ session_id: SESSION,
255
+ timestamp: Date.now(),
256
+ hook_type: "crypto" as const,
257
+ function_name: "encrypt",
258
+ arguments: "{}",
259
+ result: null,
260
+ call_stack:
261
+ "at encrypt (https://example.com/js/api.js:142:15)\n" +
262
+ "at processData (https://example.com/js/api.js:200:5)",
263
+ related_request_id: null,
264
+ },
265
+ ];
266
+
267
+ const { requestsRepo, jsHooksRepo } = makeMockRepos(
268
+ [jsOther, jsCorrelated],
269
+ hooks,
270
+ );
271
+ const extractor = new CryptoScriptExtractor(requestsRepo as any, jsHooksRepo as any);
272
+
273
+ const result = extractor.extract(SESSION);
274
+
275
+ expect(result.length).toBeGreaterThanOrEqual(2);
276
+ // The correlated script should come first because hookCorrelation is higher
277
+ expect(result[0].scriptUrl).toBe("https://example.com/js/api.js");
278
+ expect(result[1].scriptUrl).toBe("https://example.com/js/other.js");
279
+ });
280
+ });
281
+ });