deepspider 0.1.0

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 (261) hide show
  1. package/.claude/agents/check.md +122 -0
  2. package/.claude/agents/debug.md +106 -0
  3. package/.claude/agents/dispatch.md +214 -0
  4. package/.claude/agents/implement.md +96 -0
  5. package/.claude/agents/plan.md +396 -0
  6. package/.claude/agents/research.md +120 -0
  7. package/.claude/commands/evolve/merge.md +80 -0
  8. package/.claude/commands/trellis/before-backend-dev.md +13 -0
  9. package/.claude/commands/trellis/before-frontend-dev.md +13 -0
  10. package/.claude/commands/trellis/break-loop.md +107 -0
  11. package/.claude/commands/trellis/check-backend.md +13 -0
  12. package/.claude/commands/trellis/check-cross-layer.md +153 -0
  13. package/.claude/commands/trellis/check-frontend.md +13 -0
  14. package/.claude/commands/trellis/create-command.md +154 -0
  15. package/.claude/commands/trellis/finish-work.md +129 -0
  16. package/.claude/commands/trellis/integrate-skill.md +219 -0
  17. package/.claude/commands/trellis/onboard.md +358 -0
  18. package/.claude/commands/trellis/parallel.md +193 -0
  19. package/.claude/commands/trellis/record-session.md +62 -0
  20. package/.claude/commands/trellis/start.md +280 -0
  21. package/.claude/commands/trellis/update-spec.md +213 -0
  22. package/.claude/hooks/inject-subagent-context.py +758 -0
  23. package/.claude/hooks/ralph-loop.py +374 -0
  24. package/.claude/hooks/session-start.py +126 -0
  25. package/.claude/settings.json +41 -0
  26. package/.claude/skills/deepagents-guide/SKILL.md +428 -0
  27. package/.cursor/commands/trellis-before-backend-dev.md +13 -0
  28. package/.cursor/commands/trellis-before-frontend-dev.md +13 -0
  29. package/.cursor/commands/trellis-break-loop.md +107 -0
  30. package/.cursor/commands/trellis-check-backend.md +13 -0
  31. package/.cursor/commands/trellis-check-cross-layer.md +153 -0
  32. package/.cursor/commands/trellis-check-frontend.md +13 -0
  33. package/.cursor/commands/trellis-create-command.md +154 -0
  34. package/.cursor/commands/trellis-finish-work.md +129 -0
  35. package/.cursor/commands/trellis-integrate-skill.md +219 -0
  36. package/.cursor/commands/trellis-onboard.md +358 -0
  37. package/.cursor/commands/trellis-record-session.md +62 -0
  38. package/.cursor/commands/trellis-start.md +156 -0
  39. package/.cursor/commands/trellis-update-spec.md +213 -0
  40. package/.env.example +11 -0
  41. package/.husky/pre-commit +1 -0
  42. package/.mcp.json +8 -0
  43. package/.trellis/.template-hashes.json +65 -0
  44. package/.trellis/.version +1 -0
  45. package/.trellis/scripts/add-session.sh +384 -0
  46. package/.trellis/scripts/common/developer.sh +129 -0
  47. package/.trellis/scripts/common/git-context.sh +263 -0
  48. package/.trellis/scripts/common/paths.sh +208 -0
  49. package/.trellis/scripts/common/phase.sh +150 -0
  50. package/.trellis/scripts/common/registry.sh +247 -0
  51. package/.trellis/scripts/common/task-queue.sh +142 -0
  52. package/.trellis/scripts/common/task-utils.sh +151 -0
  53. package/.trellis/scripts/common/worktree.sh +128 -0
  54. package/.trellis/scripts/create-bootstrap.sh +299 -0
  55. package/.trellis/scripts/get-context.sh +7 -0
  56. package/.trellis/scripts/get-developer.sh +15 -0
  57. package/.trellis/scripts/init-developer.sh +34 -0
  58. package/.trellis/scripts/multi-agent/cleanup.sh +396 -0
  59. package/.trellis/scripts/multi-agent/create-pr.sh +241 -0
  60. package/.trellis/scripts/multi-agent/plan.sh +207 -0
  61. package/.trellis/scripts/multi-agent/start.sh +310 -0
  62. package/.trellis/scripts/multi-agent/status.sh +828 -0
  63. package/.trellis/scripts/task.sh +1118 -0
  64. package/.trellis/spec/backend/deepagents-guide.md +337 -0
  65. package/.trellis/spec/backend/directory-structure.md +126 -0
  66. package/.trellis/spec/backend/examples/skills/deepagents-guide/README.md +11 -0
  67. package/.trellis/spec/backend/examples/skills/deepagents-guide/agent.js.template +20 -0
  68. package/.trellis/spec/backend/examples/skills/deepagents-guide/skills-config.js.template +13 -0
  69. package/.trellis/spec/backend/examples/skills/deepagents-guide/subagent.js.template +19 -0
  70. package/.trellis/spec/backend/hook-guidelines.md +178 -0
  71. package/.trellis/spec/backend/index.md +36 -0
  72. package/.trellis/spec/backend/quality-guidelines.md +201 -0
  73. package/.trellis/spec/backend/state-management.md +76 -0
  74. package/.trellis/spec/backend/tool-guidelines.md +144 -0
  75. package/.trellis/spec/backend/type-safety.md +71 -0
  76. package/.trellis/spec/guides/code-reuse-thinking-guide.md +92 -0
  77. package/.trellis/spec/guides/cross-layer-thinking-guide.md +94 -0
  78. package/.trellis/spec/guides/index.md +79 -0
  79. package/.trellis/tasks/archive/02-02-evolving-skills/prd.md +61 -0
  80. package/.trellis/tasks/archive/02-02-evolving-skills/task.json +29 -0
  81. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/prd.md +86 -0
  82. package/.trellis/tasks/archive/2026-02/00-bootstrap-guidelines/task.json +27 -0
  83. package/.trellis/tasks/archive/2026-02/02-02-skills-system/check.jsonl +3 -0
  84. package/.trellis/tasks/archive/2026-02/02-02-skills-system/debug.jsonl +2 -0
  85. package/.trellis/tasks/archive/2026-02/02-02-skills-system/implement.jsonl +5 -0
  86. package/.trellis/tasks/archive/2026-02/02-02-skills-system/prd.md +33 -0
  87. package/.trellis/tasks/archive/2026-02/02-02-skills-system/task.json +41 -0
  88. package/.trellis/workflow.md +407 -0
  89. package/.trellis/workspace/index.md +123 -0
  90. package/.trellis/workspace/pony/index.md +40 -0
  91. package/.trellis/workspace/pony/journal-1.md +7 -0
  92. package/.trellis/worktree.yaml +47 -0
  93. package/AGENTS.md +18 -0
  94. package/CLAUDE.md +292 -0
  95. package/README.md +134 -0
  96. package/agents/deepspider.md +142 -0
  97. package/docs/DEBUG.md +42 -0
  98. package/docs/GUIDE.md +334 -0
  99. package/docs/PROMPT.md +60 -0
  100. package/docs/USAGE.md +226 -0
  101. package/eslint.config.js +51 -0
  102. package/package.json +78 -0
  103. package/requirements-crypto.txt +14 -0
  104. package/src/agent/index.js +97 -0
  105. package/src/agent/logger.js +164 -0
  106. package/src/agent/middleware/filterTools.js +64 -0
  107. package/src/agent/middleware/report.js +79 -0
  108. package/src/agent/prompts/system.js +315 -0
  109. package/src/agent/run.js +575 -0
  110. package/src/agent/skills/anti-detect/SKILL.md +28 -0
  111. package/src/agent/skills/anti-detect/evolved.md +12 -0
  112. package/src/agent/skills/captcha/SKILL.md +37 -0
  113. package/src/agent/skills/captcha/evolved.md +12 -0
  114. package/src/agent/skills/config.js +30 -0
  115. package/src/agent/skills/crawler/SKILL.md +9 -0
  116. package/src/agent/skills/crawler/evolved.md +16 -0
  117. package/src/agent/skills/dynamic-analysis/SKILL.md +91 -0
  118. package/src/agent/skills/dynamic-analysis/evolved.md +12 -0
  119. package/src/agent/skills/env/SKILL.md +72 -0
  120. package/src/agent/skills/env/evolved.md +12 -0
  121. package/src/agent/skills/evolve.js +79 -0
  122. package/src/agent/skills/general/SKILL.md +12 -0
  123. package/src/agent/skills/general/evolved.md +12 -0
  124. package/src/agent/skills/js2python/SKILL.md +30 -0
  125. package/src/agent/skills/js2python/evolved.md +13 -0
  126. package/src/agent/skills/report/SKILL.md +21 -0
  127. package/src/agent/skills/report/evolved.md +12 -0
  128. package/src/agent/skills/sandbox/SKILL.md +22 -0
  129. package/src/agent/skills/sandbox/evolved.md +16 -0
  130. package/src/agent/skills/static-analysis/SKILL.md +93 -0
  131. package/src/agent/skills/static-analysis/evolved.md +12 -0
  132. package/src/agent/skills/xpath/SKILL.md +119 -0
  133. package/src/agent/subagents/anti-detect.js +45 -0
  134. package/src/agent/subagents/captcha.js +51 -0
  135. package/src/agent/subagents/crawler.js +138 -0
  136. package/src/agent/subagents/dynamic.js +64 -0
  137. package/src/agent/subagents/env-agent.js +82 -0
  138. package/src/agent/subagents/index.js +37 -0
  139. package/src/agent/subagents/js2python.js +72 -0
  140. package/src/agent/subagents/sandbox.js +55 -0
  141. package/src/agent/subagents/static.js +66 -0
  142. package/src/agent/tools/analysis.js +135 -0
  143. package/src/agent/tools/analyzer.js +85 -0
  144. package/src/agent/tools/anti-detect.js +89 -0
  145. package/src/agent/tools/antidebug.js +64 -0
  146. package/src/agent/tools/async.js +43 -0
  147. package/src/agent/tools/browser.js +324 -0
  148. package/src/agent/tools/captcha.js +223 -0
  149. package/src/agent/tools/capture.js +179 -0
  150. package/src/agent/tools/correlate.js +303 -0
  151. package/src/agent/tools/crawler.js +116 -0
  152. package/src/agent/tools/cryptohook.js +80 -0
  153. package/src/agent/tools/debug.js +246 -0
  154. package/src/agent/tools/deobfuscator.js +90 -0
  155. package/src/agent/tools/env.js +83 -0
  156. package/src/agent/tools/envdump.js +92 -0
  157. package/src/agent/tools/evolve.js +164 -0
  158. package/src/agent/tools/extract.js +114 -0
  159. package/src/agent/tools/extractor.js +54 -0
  160. package/src/agent/tools/file.js +224 -0
  161. package/src/agent/tools/hook.js +84 -0
  162. package/src/agent/tools/hookManager.js +178 -0
  163. package/src/agent/tools/index.js +137 -0
  164. package/src/agent/tools/nodejs.js +101 -0
  165. package/src/agent/tools/patch.js +46 -0
  166. package/src/agent/tools/preprocess.js +71 -0
  167. package/src/agent/tools/profile.js +122 -0
  168. package/src/agent/tools/python.js +627 -0
  169. package/src/agent/tools/report.js +124 -0
  170. package/src/agent/tools/runtime.js +132 -0
  171. package/src/agent/tools/sandbox.js +79 -0
  172. package/src/agent/tools/store.js +73 -0
  173. package/src/agent/tools/trace.js +74 -0
  174. package/src/agent/tools/tracing.js +201 -0
  175. package/src/agent/tools/utils.js +51 -0
  176. package/src/agent/tools/verify.js +184 -0
  177. package/src/agent/tools/webcrack.js +109 -0
  178. package/src/analyzer/ASTAnalyzer.js +387 -0
  179. package/src/analyzer/CallStackAnalyzer.js +379 -0
  180. package/src/analyzer/Deobfuscator.js +289 -0
  181. package/src/analyzer/EncryptionAnalyzer.js +99 -0
  182. package/src/analyzer/index.js +22 -0
  183. package/src/browser/EnvBridge.js +186 -0
  184. package/src/browser/cdp.js +168 -0
  185. package/src/browser/client.js +197 -0
  186. package/src/browser/collector.js +444 -0
  187. package/src/browser/collectors/RequestCryptoLinker.js +109 -0
  188. package/src/browser/collectors/ResponseSearcher.js +107 -0
  189. package/src/browser/collectors/ScriptCollector.js +158 -0
  190. package/src/browser/collectors/index.js +26 -0
  191. package/src/browser/defaultHooks.js +932 -0
  192. package/src/browser/hooks/crypto.js +55 -0
  193. package/src/browser/hooks/index.js +64 -0
  194. package/src/browser/hooks/native.js +9 -0
  195. package/src/browser/hooks/network.js +33 -0
  196. package/src/browser/index.js +42 -0
  197. package/src/browser/interceptors/NetworkInterceptor.js +116 -0
  198. package/src/browser/interceptors/ScriptInterceptor.js +76 -0
  199. package/src/browser/interceptors/index.js +6 -0
  200. package/src/browser/ui/analysisPanel.js +1782 -0
  201. package/src/browser/ui/confirmDialog.js +158 -0
  202. package/src/browser/ui/panel.html +152 -0
  203. package/src/browser/ui/selector.js +170 -0
  204. package/src/config/index.js +5 -0
  205. package/src/config/paths.js +71 -0
  206. package/src/config/patterns/crypto.js +36 -0
  207. package/src/config/profiles/chrome.json +71 -0
  208. package/src/config/profiles/firefox.json +44 -0
  209. package/src/config/profiles/safari.json +38 -0
  210. package/src/core/EnvMonitor.js +200 -0
  211. package/src/core/PatchGenerator.js +278 -0
  212. package/src/core/Sandbox.js +181 -0
  213. package/src/env/AntiAntiDebug.js +111 -0
  214. package/src/env/AsyncHook.js +68 -0
  215. package/src/env/BrowserAPIList.js +265 -0
  216. package/src/env/CookieHook.js +48 -0
  217. package/src/env/CryptoHook.js +205 -0
  218. package/src/env/EnvCodeGenerator.js +157 -0
  219. package/src/env/EnvDumper.js +356 -0
  220. package/src/env/EnvExtractor.js +220 -0
  221. package/src/env/HookBase.js +618 -0
  222. package/src/env/NetworkHook.js +159 -0
  223. package/src/env/modules/bom/history.js +29 -0
  224. package/src/env/modules/bom/location.js +26 -0
  225. package/src/env/modules/bom/navigator.js +70 -0
  226. package/src/env/modules/bom/screen.js +26 -0
  227. package/src/env/modules/bom/storage.js +23 -0
  228. package/src/env/modules/dom/document.js +110 -0
  229. package/src/env/modules/dom/event.js +51 -0
  230. package/src/env/modules/index.js +34 -0
  231. package/src/env/modules/webapi/fetch.js +46 -0
  232. package/src/env/modules/webapi/url.js +47 -0
  233. package/src/env/modules/webapi/xhr.js +48 -0
  234. package/src/index.js +27 -0
  235. package/src/mcp/server.js +89 -0
  236. package/src/store/DataStore.js +708 -0
  237. package/src/store/Store.js +158 -0
  238. package/src/store/Validator.js +24 -0
  239. package/test/analyze.test.js +90 -0
  240. package/test/envdump.test.js +74 -0
  241. package/test/flow.test.js +90 -0
  242. package/test/hooks.test.js +138 -0
  243. package/test/plugin.test.js +35 -0
  244. package/test/refactor-full.test.js +30 -0
  245. package/test/refactor.test.js +21 -0
  246. package/test/samples/obfuscated.js +61 -0
  247. package/test/samples/original.js +66 -0
  248. package/test/samples/v10_eval_chain.js +52 -0
  249. package/test/samples/v11_bytecode_vm.js +81 -0
  250. package/test/samples/v12_polymorphic.js +69 -0
  251. package/test/samples/v1_ob_basic.js +98 -0
  252. package/test/samples/v2_ob_advanced.js +99 -0
  253. package/test/samples/v3_jjencode.js +77 -0
  254. package/test/samples/v4_aaencode.js +73 -0
  255. package/test/samples/v5_control_flow.js +86 -0
  256. package/test/samples/v6_string_encryption.js +71 -0
  257. package/test/samples/v7_jsvmp.js +83 -0
  258. package/test/samples/v8_anti_debug.js +79 -0
  259. package/test/samples/v9_proxy_trap.js +49 -0
  260. package/test/samples.test.js +96 -0
  261. package/test/webcrack.test.js +55 -0
@@ -0,0 +1,444 @@
1
+ /**
2
+ * DeepSpider - 环境数据采集器
3
+ * 从真实浏览器动态采集任意环境属性
4
+ */
5
+
6
+ export class EnvCollector {
7
+ constructor(page) {
8
+ this.page = page;
9
+ this.cache = new Map();
10
+ }
11
+
12
+ /**
13
+ * 动态采集任意属性路径
14
+ * @param {string} path - 属性路径,如 'navigator.connection.effectiveType'
15
+ * @param {object} options - 采集选项
16
+ */
17
+ async collect(path, options = {}) {
18
+ const { depth = 1, includeProto = false, useCache = true } = options;
19
+
20
+ if (useCache && this.cache.has(path)) {
21
+ return this.cache.get(path);
22
+ }
23
+
24
+ const result = await this.page.evaluate(({ path, depth, includeProto }) => {
25
+ function getByPath(obj, path) {
26
+ return path.split('.').reduce((o, k) => o && o[k], obj);
27
+ }
28
+
29
+ function serialize(val, currentDepth, maxDepth) {
30
+ if (val === null) return { type: 'null', value: null };
31
+ if (val === undefined) return { type: 'undefined', value: undefined };
32
+
33
+ const type = typeof val;
34
+
35
+ if (type === 'function') {
36
+ return { type: 'function', name: val.name || 'anonymous' };
37
+ }
38
+
39
+ if (type !== 'object') {
40
+ return { type, value: val };
41
+ }
42
+
43
+ if (currentDepth >= maxDepth) {
44
+ return { type: 'object', value: '[Object]', truncated: true };
45
+ }
46
+
47
+ if (Array.isArray(val)) {
48
+ return {
49
+ type: 'array',
50
+ value: val.map(v => serialize(v, currentDepth + 1, maxDepth))
51
+ };
52
+ }
53
+
54
+ const result = { type: 'object', properties: {} };
55
+ const keys = Object.getOwnPropertyNames(val);
56
+
57
+ for (const key of keys.slice(0, 50)) {
58
+ try {
59
+ const desc = Object.getOwnPropertyDescriptor(val, key);
60
+ if (desc.get) {
61
+ result.properties[key] = {
62
+ ...serialize(val[key], currentDepth + 1, maxDepth),
63
+ hasGetter: true
64
+ };
65
+ } else {
66
+ result.properties[key] = serialize(desc.value, currentDepth + 1, maxDepth);
67
+ }
68
+ } catch (e) {
69
+ result.properties[key] = { type: 'error', message: e.message };
70
+ }
71
+ }
72
+
73
+ return result;
74
+ }
75
+
76
+ try {
77
+ const value = getByPath(window, path);
78
+ if (value === undefined) {
79
+ return { success: false, error: `${path} is undefined` };
80
+ }
81
+
82
+ const serialized = serialize(value, 0, depth);
83
+
84
+ // 采集属性描述符
85
+ const parts = path.split('.');
86
+ const propName = parts.pop();
87
+ const parentPath = parts.join('.');
88
+ const parent = parentPath ? getByPath(window, parentPath) : window;
89
+
90
+ let descriptor = null;
91
+ if (parent) {
92
+ const desc = Object.getOwnPropertyDescriptor(parent, propName);
93
+ if (desc) {
94
+ descriptor = {
95
+ configurable: desc.configurable,
96
+ enumerable: desc.enumerable,
97
+ writable: desc.writable,
98
+ hasGetter: !!desc.get,
99
+ hasSetter: !!desc.set
100
+ };
101
+ }
102
+ }
103
+
104
+ return {
105
+ success: true,
106
+ path,
107
+ data: serialized,
108
+ descriptor
109
+ };
110
+ } catch (e) {
111
+ return { success: false, error: e.message };
112
+ }
113
+ }, { path, depth, includeProto });
114
+
115
+ if (result.success && useCache) {
116
+ this.cache.set(path, result);
117
+ }
118
+
119
+ return result;
120
+ }
121
+
122
+ /**
123
+ * 批量采集多个属性路径
124
+ */
125
+ async collectBatch(paths, options = {}) {
126
+ const results = {};
127
+ const promises = paths.map(async (path) => {
128
+ results[path] = await this.collect(path, options);
129
+ });
130
+ await Promise.all(promises);
131
+ return results;
132
+ }
133
+
134
+ /**
135
+ * 根据缺失列表采集
136
+ */
137
+ async collectMissing(missingPaths, options = {}) {
138
+ const collected = {};
139
+ const failed = [];
140
+
141
+ for (const path of missingPaths) {
142
+ const result = await this.collect(path, { ...options, depth: 2 });
143
+ if (result.success) {
144
+ collected[path] = result;
145
+ } else {
146
+ failed.push({ path, error: result.error });
147
+ }
148
+ }
149
+
150
+ return { collected, failed };
151
+ }
152
+
153
+ /**
154
+ * 深度采集整个对象
155
+ */
156
+ async collectDeep(rootPath, options = {}) {
157
+ const { maxDepth = 3, maxProps = 100 } = options;
158
+
159
+ return await this.page.evaluate(({ rootPath, maxDepth, maxProps }) => {
160
+ function getByPath(obj, path) {
161
+ return path.split('.').reduce((o, k) => o && o[k], obj);
162
+ }
163
+
164
+ function collectRecursive(obj, path, depth, collected) {
165
+ if (depth > maxDepth || collected.size > maxProps) return;
166
+ if (!obj || typeof obj !== 'object') return;
167
+
168
+ const keys = Object.getOwnPropertyNames(obj);
169
+ for (const key of keys) {
170
+ if (collected.size > maxProps) break;
171
+
172
+ const fullPath = path ? `${path}.${key}` : key;
173
+ try {
174
+ const val = obj[key];
175
+ const type = typeof val;
176
+
177
+ collected.set(fullPath, {
178
+ type,
179
+ value: type === 'function' ? '[Function]' :
180
+ type === 'object' ? '[Object]' :
181
+ val
182
+ });
183
+
184
+ if (type === 'object' && val !== null) {
185
+ collectRecursive(val, fullPath, depth + 1, collected);
186
+ }
187
+ } catch (e) {
188
+ collected.set(fullPath, { type: 'error', error: e.message });
189
+ }
190
+ }
191
+ }
192
+
193
+ const root = getByPath(window, rootPath);
194
+ if (!root) {
195
+ return { success: false, error: `${rootPath} not found` };
196
+ }
197
+
198
+ const collected = new Map();
199
+ collectRecursive(root, rootPath, 0, collected);
200
+
201
+ return {
202
+ success: true,
203
+ rootPath,
204
+ properties: Object.fromEntries(collected)
205
+ };
206
+ }, { rootPath, maxDepth, maxProps });
207
+ }
208
+
209
+ // === 特殊环境采集 ===
210
+
211
+ /**
212
+ * 采集 Canvas 指纹
213
+ */
214
+ async collectCanvas() {
215
+ return await this.page.evaluate(() => {
216
+ try {
217
+ const canvas = document.createElement('canvas');
218
+ canvas.width = 200;
219
+ canvas.height = 50;
220
+ const ctx = canvas.getContext('2d');
221
+
222
+ ctx.textBaseline = 'top';
223
+ ctx.font = '14px Arial';
224
+ ctx.fillStyle = '#f60';
225
+ ctx.fillRect(0, 0, 200, 50);
226
+ ctx.fillStyle = '#069';
227
+ ctx.fillText('DeepSpider Canvas Test', 2, 15);
228
+ ctx.fillStyle = 'rgba(102, 204, 0, 0.7)';
229
+ ctx.fillText('DeepSpider Canvas Test', 4, 17);
230
+
231
+ return {
232
+ success: true,
233
+ dataURL: canvas.toDataURL(),
234
+ support: {
235
+ '2d': !!canvas.getContext('2d'),
236
+ 'webgl': !!canvas.getContext('webgl'),
237
+ 'webgl2': !!canvas.getContext('webgl2')
238
+ }
239
+ };
240
+ } catch (e) {
241
+ return { success: false, error: e.message };
242
+ }
243
+ });
244
+ }
245
+
246
+ /**
247
+ * 采集 WebGL 信息
248
+ */
249
+ async collectWebGL() {
250
+ return await this.page.evaluate(() => {
251
+ try {
252
+ const canvas = document.createElement('canvas');
253
+ const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
254
+
255
+ if (!gl) {
256
+ return { success: false, error: 'WebGL not supported' };
257
+ }
258
+
259
+ const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
260
+
261
+ return {
262
+ success: true,
263
+ vendor: gl.getParameter(gl.VENDOR),
264
+ renderer: gl.getParameter(gl.RENDERER),
265
+ version: gl.getParameter(gl.VERSION),
266
+ shadingLanguageVersion: gl.getParameter(gl.SHADING_LANGUAGE_VERSION),
267
+ unmaskedVendor: debugInfo ? gl.getParameter(debugInfo.UNMASKED_VENDOR_WEBGL) : null,
268
+ unmaskedRenderer: debugInfo ? gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL) : null,
269
+ maxTextureSize: gl.getParameter(gl.MAX_TEXTURE_SIZE),
270
+ maxViewportDims: gl.getParameter(gl.MAX_VIEWPORT_DIMS)
271
+ };
272
+ } catch (e) {
273
+ return { success: false, error: e.message };
274
+ }
275
+ });
276
+ }
277
+
278
+ /**
279
+ * 采集音频指纹
280
+ */
281
+ async collectAudioFingerprint() {
282
+ return await this.page.evaluate(() => {
283
+ return new Promise((resolve) => {
284
+ try {
285
+ const AudioContext = window.AudioContext || window.webkitAudioContext;
286
+ if (!AudioContext) {
287
+ resolve({ success: false, error: 'AudioContext not supported' });
288
+ return;
289
+ }
290
+
291
+ const context = new AudioContext();
292
+ const oscillator = context.createOscillator();
293
+ const analyser = context.createAnalyser();
294
+ const gain = context.createGain();
295
+ const processor = context.createScriptProcessor(4096, 1, 1);
296
+
297
+ gain.gain.value = 0;
298
+ oscillator.type = 'triangle';
299
+ oscillator.frequency.value = 10000;
300
+
301
+ oscillator.connect(analyser);
302
+ analyser.connect(processor);
303
+ processor.connect(gain);
304
+ gain.connect(context.destination);
305
+
306
+ let fingerprint = 0;
307
+ processor.onaudioprocess = (e) => {
308
+ const data = e.inputBuffer.getChannelData(0);
309
+ for (let i = 0; i < data.length; i++) {
310
+ fingerprint += Math.abs(data[i]);
311
+ }
312
+ processor.disconnect();
313
+ oscillator.disconnect();
314
+ context.close();
315
+
316
+ resolve({
317
+ success: true,
318
+ fingerprint: fingerprint.toString(),
319
+ sampleRate: context.sampleRate,
320
+ channelCount: context.destination.channelCount
321
+ });
322
+ };
323
+
324
+ oscillator.start(0);
325
+ setTimeout(() => oscillator.stop(), 100);
326
+ } catch (e) {
327
+ resolve({ success: false, error: e.message });
328
+ }
329
+ });
330
+ });
331
+ }
332
+
333
+ /**
334
+ * 采集字体列表
335
+ */
336
+ async collectFonts() {
337
+ return await this.page.evaluate(() => {
338
+ const testFonts = [
339
+ 'Arial', 'Arial Black', 'Comic Sans MS', 'Courier New', 'Georgia',
340
+ 'Impact', 'Times New Roman', 'Trebuchet MS', 'Verdana', 'Webdings',
341
+ 'Microsoft YaHei', 'SimHei', 'SimSun', 'KaiTi', 'FangSong'
342
+ ];
343
+
344
+ const baseFonts = ['monospace', 'sans-serif', 'serif'];
345
+ const testString = 'mmmmmmmmmmlli';
346
+ const testSize = '72px';
347
+
348
+ const span = document.createElement('span');
349
+ span.style.position = 'absolute';
350
+ span.style.left = '-9999px';
351
+ span.style.fontSize = testSize;
352
+ span.innerText = testString;
353
+ document.body.appendChild(span);
354
+
355
+ const baseWidths = {};
356
+ for (const base of baseFonts) {
357
+ span.style.fontFamily = base;
358
+ baseWidths[base] = span.offsetWidth;
359
+ }
360
+
361
+ const detected = [];
362
+ for (const font of testFonts) {
363
+ let found = false;
364
+ for (const base of baseFonts) {
365
+ span.style.fontFamily = `'${font}', ${base}`;
366
+ if (span.offsetWidth !== baseWidths[base]) {
367
+ found = true;
368
+ break;
369
+ }
370
+ }
371
+ if (found) detected.push(font);
372
+ }
373
+
374
+ document.body.removeChild(span);
375
+
376
+ return { success: true, fonts: detected, total: detected.length };
377
+ });
378
+ }
379
+
380
+ // === 快捷方法(兼容旧 API)===
381
+
382
+ async getCookies() {
383
+ const cookies = await this.page.context().cookies();
384
+ return { success: true, cookies };
385
+ }
386
+
387
+ async getStorage(type = 'local') {
388
+ return await this.page.evaluate((storageType) => {
389
+ const storage = storageType === 'local' ? localStorage : sessionStorage;
390
+ const data = {};
391
+ for (let i = 0; i < storage.length; i++) {
392
+ const key = storage.key(i);
393
+ data[key] = storage.getItem(key);
394
+ }
395
+ return { success: true, data };
396
+ }, type);
397
+ }
398
+
399
+ /**
400
+ * 采集完整环境快照
401
+ */
402
+ async collectFullSnapshot() {
403
+ const [
404
+ navigator,
405
+ screen,
406
+ location,
407
+ document,
408
+ canvas,
409
+ webgl,
410
+ fonts,
411
+ cookies,
412
+ localStorage
413
+ ] = await Promise.all([
414
+ this.collectDeep('navigator', { maxDepth: 2 }),
415
+ this.collectDeep('screen', { maxDepth: 1 }),
416
+ this.collect('location', { depth: 1 }),
417
+ this.collect('document', { depth: 1 }),
418
+ this.collectCanvas(),
419
+ this.collectWebGL(),
420
+ this.collectFonts(),
421
+ this.getCookies(),
422
+ this.getStorage('local')
423
+ ]);
424
+
425
+ return {
426
+ timestamp: Date.now(),
427
+ navigator,
428
+ screen,
429
+ location,
430
+ document,
431
+ canvas,
432
+ webgl,
433
+ fonts,
434
+ cookies,
435
+ localStorage
436
+ };
437
+ }
438
+
439
+ clearCache() {
440
+ this.cache.clear();
441
+ }
442
+ }
443
+
444
+ export default EnvCollector;
@@ -0,0 +1,109 @@
1
+ /**
2
+ * DeepSpider - 请求-加密关联器
3
+ * 建立请求和加密调用的映射关系
4
+ */
5
+
6
+ export class RequestCryptoLinker {
7
+ /**
8
+ * 生成关联脚本(注入页面)
9
+ */
10
+ generateLinkerScript() {
11
+ return `
12
+ (function() {
13
+ const deepspider = window.__deepspider__;
14
+ if (!deepspider || deepspider._cryptoLinker) return;
15
+ deepspider._cryptoLinker = true;
16
+
17
+ /**
18
+ * 分析请求的加密关联
19
+ * @param {string} url - 请求 URL
20
+ * @returns {Object} 关联分析结果
21
+ */
22
+ deepspider.analyzeRequestCrypto = function(url) {
23
+ const result = {
24
+ url,
25
+ cryptoCalls: [],
26
+ headerEncryption: [],
27
+ bodyEncryption: [],
28
+ timeline: []
29
+ };
30
+
31
+ // 获取该请求关联的加密调用
32
+ const xhrLogs = deepspider.logs?.xhr || [];
33
+ const fetchLogs = deepspider.logs?.fetch || [];
34
+ const cryptoLogs = deepspider.logs?.crypto || [];
35
+
36
+ // 找到目标请求
37
+ const allLogs = [...xhrLogs, ...fetchLogs];
38
+ const targetRequest = allLogs.find(l =>
39
+ l.url === url || l.url?.includes(url)
40
+ );
41
+
42
+ if (!targetRequest) {
43
+ return { error: '未找到该请求', url };
44
+ }
45
+
46
+ // 获取请求时间窗口内的加密调用
47
+ const requestTime = targetRequest.timestamp;
48
+ const windowStart = requestTime - 5000; // 请求前5秒
49
+ const windowEnd = requestTime + 1000; // 请求后1秒
50
+
51
+ cryptoLogs.forEach(log => {
52
+ if (log.timestamp >= windowStart && log.timestamp <= windowEnd) {
53
+ result.cryptoCalls.push({
54
+ algo: log.algo,
55
+ timestamp: log.timestamp,
56
+ timeDiff: log.timestamp - requestTime,
57
+ data: log.data || log.message,
58
+ key: log.key,
59
+ stack: deepspider.parseStack(log.stack)?.slice(0, 5)
60
+ });
61
+ }
62
+ });
63
+
64
+ // 分析 Header 中可能的加密字段
65
+ const headers = targetRequest.requestHeaders || {};
66
+ const suspiciousHeaders = ['sign', 'token', 'auth', 'key', 'encrypt', 'hash'];
67
+
68
+ Object.entries(headers).forEach(([name, value]) => {
69
+ const lowerName = name.toLowerCase();
70
+ if (suspiciousHeaders.some(s => lowerName.includes(s))) {
71
+ result.headerEncryption.push({ name, value });
72
+ }
73
+ });
74
+
75
+ // 分析 Body 中可能的加密字段
76
+ const body = targetRequest.requestBody || targetRequest.body;
77
+ if (body && typeof body === 'string') {
78
+ // 检测 Base64
79
+ if (/^[A-Za-z0-9+/=]{20,}$/.test(body)) {
80
+ result.bodyEncryption.push({ type: 'base64', value: body.slice(0, 100) });
81
+ }
82
+ // 检测 JSON 中的加密字段
83
+ try {
84
+ const json = JSON.parse(body);
85
+ Object.entries(json).forEach(([key, value]) => {
86
+ if (typeof value === 'string' && value.length > 20) {
87
+ if (/^[A-Za-z0-9+/=]+$/.test(value)) {
88
+ result.bodyEncryption.push({ field: key, type: 'possible-encrypted', value: value.slice(0, 50) });
89
+ }
90
+ }
91
+ });
92
+ } catch(e) {}
93
+ }
94
+
95
+ // 构建时间线
96
+ result.timeline = result.cryptoCalls
97
+ .map(c => ({ time: c.timeDiff, event: c.algo }))
98
+ .sort((a, b) => a.time - b.time);
99
+
100
+ return result;
101
+ };
102
+
103
+ console.log('[DeepSpider] Request-Crypto Linker 已启用');
104
+ })();
105
+ `;
106
+ }
107
+ }
108
+
109
+ export default RequestCryptoLinker;
@@ -0,0 +1,107 @@
1
+ /**
2
+ * DeepSpider - 响应搜索器
3
+ * 在所有记录的响应中搜索文本,定位数据来源
4
+ */
5
+
6
+ export class ResponseSearcher {
7
+ /**
8
+ * 生成搜索脚本(注入页面)
9
+ */
10
+ generateSearchScript() {
11
+ return `
12
+ (function() {
13
+ const deepspider = window.__deepspider__;
14
+ if (!deepspider || deepspider._responseSearcher) return;
15
+ deepspider._responseSearcher = true;
16
+
17
+ /**
18
+ * 在所有响应中搜索文本
19
+ * @param {string} text - 要搜索的文本
20
+ * @returns {Array} 匹配的请求列表
21
+ */
22
+ deepspider.searchInResponses = function(text) {
23
+ if (!text || text.length < 2) return [];
24
+
25
+ const results = [];
26
+ const searchText = text.trim();
27
+
28
+ // 搜索 XHR 日志
29
+ const xhrLogs = deepspider.logs?.xhr || [];
30
+ xhrLogs.forEach((log, index) => {
31
+ if (log.action === 'response' && log.response) {
32
+ if (log.response.includes(searchText)) {
33
+ results.push({
34
+ type: 'xhr',
35
+ index,
36
+ url: log.url,
37
+ status: log.status,
38
+ matchIn: 'response',
39
+ linkedCrypto: log.linkedCrypto || [],
40
+ timestamp: log.timestamp
41
+ });
42
+ }
43
+ }
44
+ });
45
+
46
+ // 搜索 Fetch 日志
47
+ const fetchLogs = deepspider.logs?.fetch || [];
48
+ fetchLogs.forEach((log, index) => {
49
+ if (log.action === 'response' && log.response) {
50
+ if (log.response.includes(searchText)) {
51
+ results.push({
52
+ type: 'fetch',
53
+ index,
54
+ url: log.url,
55
+ status: log.status,
56
+ matchIn: 'response',
57
+ linkedCrypto: log.linkedCrypto || [],
58
+ timestamp: log.timestamp
59
+ });
60
+ }
61
+ }
62
+ });
63
+
64
+ // 按时间排序,最近的在前
65
+ results.sort((a, b) => b.timestamp - a.timestamp);
66
+
67
+ return results;
68
+ };
69
+
70
+ /**
71
+ * 获取指定请求的完整信息
72
+ */
73
+ deepspider.getRequestDetail = function(type, index) {
74
+ const logs = deepspider.logs?.[type] || [];
75
+
76
+ // 找到对应的 send 和 response
77
+ const responseLogs = logs.filter(l => l.action === 'response');
78
+ const responseLog = responseLogs[index];
79
+
80
+ if (!responseLog) return null;
81
+
82
+ // 找到对应的 send 日志
83
+ const sendLog = logs.find(l =>
84
+ l.action === 'send' &&
85
+ l.url === responseLog.url &&
86
+ l.timestamp < responseLog.timestamp
87
+ );
88
+
89
+ return {
90
+ url: responseLog.url,
91
+ method: sendLog?.method || 'GET',
92
+ status: responseLog.status,
93
+ requestHeaders: sendLog?.requestHeaders || {},
94
+ requestBody: sendLog?.requestBody || sendLog?.body,
95
+ response: responseLog.response,
96
+ linkedCrypto: responseLog.linkedCrypto || [],
97
+ timestamp: responseLog.timestamp
98
+ };
99
+ };
100
+
101
+ console.log('[DeepSpider] Response Searcher 已启用');
102
+ })();
103
+ `;
104
+ }
105
+ }
106
+
107
+ export default ResponseSearcher;