deepspider 0.3.0 → 0.3.2

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 (84) hide show
  1. package/.env.example +3 -0
  2. package/README.md +13 -13
  3. package/package.json +6 -6
  4. package/src/agent/core/PanelBridge.js +29 -77
  5. package/src/agent/core/StreamHandler.js +139 -14
  6. package/src/agent/index.js +51 -12
  7. package/src/agent/logger.js +184 -9
  8. package/src/agent/middleware/report.js +42 -16
  9. package/src/agent/middleware/subagent.js +233 -0
  10. package/src/agent/middleware/toolGuard.js +77 -0
  11. package/src/agent/middleware/validationWorkflow.js +171 -0
  12. package/src/agent/prompts/system.js +181 -59
  13. package/src/agent/run.js +41 -6
  14. package/src/agent/skills/crawler/SKILL.md +64 -3
  15. package/src/agent/skills/crawler/evolved.md +9 -1
  16. package/src/agent/skills/dynamic-analysis/SKILL.md +74 -7
  17. package/src/agent/skills/env/SKILL.md +75 -0
  18. package/src/agent/skills/evolve.js +0 -3
  19. package/src/agent/skills/sandbox/SKILL.md +35 -0
  20. package/src/agent/skills/static-analysis/SKILL.md +98 -2
  21. package/src/agent/subagents/anti-detect.js +10 -20
  22. package/src/agent/subagents/captcha.js +7 -19
  23. package/src/agent/subagents/crawler.js +25 -37
  24. package/src/agent/subagents/factory.js +109 -9
  25. package/src/agent/subagents/index.js +4 -13
  26. package/src/agent/subagents/js2python.js +7 -19
  27. package/src/agent/subagents/reverse.js +180 -0
  28. package/src/agent/tools/analysis.js +84 -1
  29. package/src/agent/tools/anti-detect.js +5 -2
  30. package/src/agent/tools/browser.js +160 -0
  31. package/src/agent/tools/captcha.js +1 -1
  32. package/src/agent/tools/capture.js +24 -3
  33. package/src/agent/tools/correlate.js +129 -15
  34. package/src/agent/tools/crawler.js +2 -1
  35. package/src/agent/tools/crawlerGenerator.js +90 -0
  36. package/src/agent/tools/debug.js +43 -6
  37. package/src/agent/tools/evolve.js +6 -3
  38. package/src/agent/tools/extractor.js +5 -1
  39. package/src/agent/tools/file.js +16 -7
  40. package/src/agent/tools/generateHook.js +66 -0
  41. package/src/agent/tools/hookManager.js +19 -9
  42. package/src/agent/tools/index.js +33 -20
  43. package/src/agent/tools/nodejs.js +41 -6
  44. package/src/agent/tools/python.js +4 -4
  45. package/src/agent/tools/report.js +2 -2
  46. package/src/agent/tools/runtime.js +1 -1
  47. package/src/agent/tools/sandbox.js +21 -1
  48. package/src/agent/tools/scratchpad.js +70 -0
  49. package/src/agent/tools/tracing.js +26 -0
  50. package/src/agent/tools/verifyAlgorithm.js +117 -0
  51. package/src/analyzer/EncryptionAnalyzer.js +2 -2
  52. package/src/browser/EnvBridge.js +27 -13
  53. package/src/browser/client.js +124 -18
  54. package/src/browser/collector.js +101 -22
  55. package/src/browser/defaultHooks.js +3 -1
  56. package/src/browser/hooks/index.js +5 -0
  57. package/src/browser/interceptors/AntiDebugInterceptor.js +132 -0
  58. package/src/browser/interceptors/NetworkInterceptor.js +77 -13
  59. package/src/browser/interceptors/ScriptInterceptor.js +34 -9
  60. package/src/browser/interceptors/index.js +1 -0
  61. package/src/browser/ui/analysisPanel.js +469 -464
  62. package/src/cli/commands/config.js +11 -3
  63. package/src/config/paths.js +9 -1
  64. package/src/config/settings.js +7 -1
  65. package/src/core/PatchGenerator.js +26 -6
  66. package/src/core/Sandbox.js +140 -3
  67. package/src/env/EnvCodeGenerator.js +60 -88
  68. package/src/env/modules/bom/history.js +6 -0
  69. package/src/env/modules/bom/location.js +6 -0
  70. package/src/env/modules/bom/navigator.js +13 -0
  71. package/src/env/modules/bom/screen.js +6 -0
  72. package/src/env/modules/bom/storage.js +7 -0
  73. package/src/env/modules/dom/document.js +14 -0
  74. package/src/env/modules/dom/event.js +4 -0
  75. package/src/env/modules/index.js +27 -10
  76. package/src/env/modules/webapi/fetch.js +4 -0
  77. package/src/env/modules/webapi/url.js +4 -0
  78. package/src/env/modules/webapi/xhr.js +8 -0
  79. package/src/store/DataStore.js +130 -47
  80. package/src/store/Store.js +2 -1
  81. package/src/agent/subagents/dynamic.js +0 -64
  82. package/src/agent/subagents/env-agent.js +0 -82
  83. package/src/agent/subagents/sandbox.js +0 -55
  84. package/src/agent/subagents/static.js +0 -66
@@ -36,7 +36,7 @@ function list() {
36
36
  const effective = getEffectiveConfig();
37
37
  console.log('配置项:');
38
38
  for (const [key, { value, source }] of Object.entries(effective)) {
39
- const display = key === 'apiKey' && value ? maskKey(value) : value || '(未设置)';
39
+ const display = key === 'apiKey' && value ? maskKey(value) : formatValue(value);
40
40
  console.log(` ${key} = ${display} [${source}]`);
41
41
  }
42
42
  }
@@ -52,7 +52,7 @@ function get(key) {
52
52
  }
53
53
  const effective = getEffectiveConfig();
54
54
  const { value, source } = effective[key];
55
- const display = key === 'apiKey' && value ? maskKey(value) : value || '(未设置)';
55
+ const display = key === 'apiKey' && value ? maskKey(value) : formatValue(value);
56
56
  console.log(`${key} = ${display} [${source}]`);
57
57
  }
58
58
 
@@ -66,7 +66,10 @@ function set(key, value) {
66
66
  process.exit(1);
67
67
  }
68
68
  const config = loadConfig();
69
- config[key] = value;
69
+ // 布尔类型配置项:CLI 字符串转布尔值存储
70
+ config[key] = typeof DEFAULTS[key] === 'boolean'
71
+ ? (value === 'true' || value === '1')
72
+ : value;
70
73
  saveConfig(config);
71
74
 
72
75
  const envVar = ENV_MAP[key];
@@ -88,6 +91,11 @@ function path() {
88
91
  console.log(CONFIG_FILE);
89
92
  }
90
93
 
94
+ function formatValue(value) {
95
+ if (value === undefined || value === null || value === '') return '(未设置)';
96
+ return String(value);
97
+ }
98
+
91
99
  function maskKey(key) {
92
100
  if (key.length <= 8) return '****';
93
101
  return key.slice(0, 4) + '****' + key.slice(-4);
@@ -27,6 +27,9 @@ export const PATHS = {
27
27
 
28
28
  // 配置(预留)
29
29
  CONFIG_DIR: join(DEEPSPIDER_HOME, 'config'),
30
+
31
+ // 浏览器持久化数据(按需创建,不加入 initDirectories)
32
+ BROWSER_DATA_DIR: join(DEEPSPIDER_HOME, 'browser-data'),
30
33
  };
31
34
 
32
35
  /**
@@ -41,8 +44,13 @@ export function ensureDir(dir) {
41
44
  /**
42
45
  * 初始化所有目录
43
46
  */
47
+ // 按需创建的目录,不随 initDirectories 自动创建
48
+ const ON_DEMAND_DIRS = new Set([PATHS.BROWSER_DATA_DIR]);
49
+
44
50
  export function initDirectories() {
45
- Object.values(PATHS).forEach(dir => ensureDir(dir));
51
+ Object.values(PATHS).forEach(dir => {
52
+ if (!ON_DEMAND_DIRS.has(dir)) ensureDir(dir);
53
+ });
46
54
  }
47
55
 
48
56
  /**
@@ -16,12 +16,14 @@ export const DEFAULTS = {
16
16
  apiKey: '',
17
17
  baseUrl: 'https://api.openai.com/v1',
18
18
  model: 'gpt-4o',
19
+ persistBrowserData: false,
19
20
  };
20
21
 
21
22
  export const ENV_MAP = {
22
23
  apiKey: 'DEEPSPIDER_API_KEY',
23
24
  baseUrl: 'DEEPSPIDER_BASE_URL',
24
25
  model: 'DEEPSPIDER_MODEL',
26
+ persistBrowserData: 'DEEPSPIDER_PERSIST_BROWSER',
25
27
  };
26
28
 
27
29
  /**
@@ -57,7 +59,11 @@ export function getEffectiveConfig() {
57
59
  const envVal = process.env[envVar];
58
60
 
59
61
  if (envVal !== undefined && envVal !== '') {
60
- result[key] = { value: envVal, source: 'env' };
62
+ // 布尔类型配置项:环境变量字符串转布尔
63
+ const value = typeof DEFAULTS[key] === 'boolean'
64
+ ? (envVal === 'true' || envVal === '1')
65
+ : envVal;
66
+ result[key] = { value, source: 'env' };
61
67
  } else if (fileConfig[key] !== undefined && fileConfig[key] !== '') {
62
68
  result[key] = { value: fileConfig[key], source: 'file' };
63
69
  } else {
@@ -4,6 +4,7 @@
4
4
  */
5
5
 
6
6
  import { Store } from '../store/Store.js';
7
+ import { coveredAPIs } from '../env/modules/index.js';
7
8
 
8
9
  // 属性类型推断规则
9
10
  const TYPE_RULES = {
@@ -46,9 +47,26 @@ export class PatchGenerator {
46
47
  }
47
48
 
48
49
  async generate(property, context = {}) {
49
- // 检查缓存
50
+ // 检查缓存(skipCoveredCheck 时忽略 skipped 缓存)
50
51
  if (this.generated.has(property)) {
51
- return { ...this.generated.get(property), cached: true };
52
+ const cached = this.generated.get(property);
53
+ if (!(context.skipCoveredCheck && cached.skipped)) {
54
+ return { ...cached, cached: true };
55
+ }
56
+ }
57
+
58
+ // 0. 预置模块已覆盖,跳过(模块已加载到沙箱,无需重复补丁)
59
+ // 当模块未加载时(skipCoveredCheck=true),不跳过
60
+ if (!context.skipCoveredCheck && coveredAPIs.has(property)) {
61
+ const result = {
62
+ source: 'module-covered',
63
+ code: '',
64
+ property,
65
+ confidence: 1.0,
66
+ skipped: true,
67
+ };
68
+ this.generated.set(property, result);
69
+ return result;
52
70
  }
53
71
 
54
72
  // 1. 知识库精确匹配
@@ -169,7 +187,7 @@ export class PatchGenerator {
169
187
  return defaults[type] || 'undefined';
170
188
  }
171
189
 
172
- _generateSmartTemplate(property, context = {}) {
190
+ _generateSmartTemplate(property, _context = {}) {
173
191
  const parts = property.split('.');
174
192
  const propName = parts[parts.length - 1];
175
193
  const type = this._inferType(property);
@@ -194,12 +212,12 @@ export class PatchGenerator {
194
212
  }
195
213
 
196
214
  // 批量生成补丁
197
- async generateBatch(properties) {
215
+ async generateBatch(properties, context = {}) {
198
216
  const results = [];
199
217
  const conflicts = this._detectConflicts(properties);
200
218
 
201
219
  for (const prop of properties) {
202
- const result = await this.generate(prop);
220
+ const result = await this.generate(prop, context);
203
221
  result.hasConflict = conflicts.has(prop);
204
222
  results.push(result);
205
223
  }
@@ -232,7 +250,7 @@ export class PatchGenerator {
232
250
  }
233
251
 
234
252
  // 检测同一对象的重复定义
235
- for (const [root, props] of roots) {
253
+ for (const [_root, props] of roots) {
236
254
  if (props.length > 1) {
237
255
  const seen = new Set();
238
256
  for (const p of props) {
@@ -252,6 +270,8 @@ export class PatchGenerator {
252
270
  const grouped = new Map();
253
271
 
254
272
  for (const patch of patches) {
273
+ // 跳过已被模块覆盖或无代码的补丁
274
+ if (patch.skipped || !patch.code) continue;
255
275
  const root = patch.property.split('.')[0];
256
276
  if (!grouped.has(root)) {
257
277
  grouped.set(root, []);
@@ -5,6 +5,8 @@
5
5
 
6
6
  import ivm from 'isolated-vm';
7
7
  import { EnvMonitor } from './EnvMonitor.js';
8
+ import { PatchGenerator } from './PatchGenerator.js';
9
+ import { modules, loadOrder } from '../env/modules/index.js';
8
10
 
9
11
  export class Sandbox {
10
12
  constructor() {
@@ -12,6 +14,7 @@ export class Sandbox {
12
14
  this.context = null;
13
15
  this.missingEnv = [];
14
16
  this.monitor = new EnvMonitor();
17
+ this.patchGenerator = new PatchGenerator();
15
18
  this.envLoaded = false;
16
19
  }
17
20
 
@@ -74,17 +77,26 @@ export class Sandbox {
74
77
  global.window = window;
75
78
  global.self = window;
76
79
 
80
+ const __proxyCache__ = new WeakMap();
77
81
  global.__createProxy__ = function(obj, path) {
78
- return new Proxy(obj, {
82
+ if (__proxyCache__.has(obj)) return __proxyCache__.get(obj);
83
+ const proxy = new Proxy(obj, {
79
84
  get(t, p) {
80
85
  if (typeof p === 'symbol') return t[p];
81
86
  const fullPath = path ? path + '.' + p : String(p);
82
- if (t[p] === undefined && !(p in t)) {
87
+ const val = t[p];
88
+ if (val === undefined && !(p in t)) {
83
89
  __recordMissing__(fullPath);
84
90
  }
85
- return t[p];
91
+ // 递归代理子对象
92
+ if (val !== null && typeof val === 'object' && !__proxyCache__.has(val)) {
93
+ return __createProxy__(val, fullPath);
94
+ }
95
+ return val;
86
96
  }
87
97
  });
98
+ __proxyCache__.set(obj, proxy);
99
+ return proxy;
88
100
  };
89
101
  `;
90
102
 
@@ -138,6 +150,131 @@ export class Sandbox {
138
150
  }
139
151
  }
140
152
 
153
+ /**
154
+ * 自动补环境闭环执行
155
+ * 加载预置模块 → Proxy 监控 → 迭代补丁 → 返回结果
156
+ */
157
+ async executeWithAutoFix(code, options = {}) {
158
+ const {
159
+ timeout = 5000,
160
+ maxIterations = 10,
161
+ loadModules = true,
162
+ } = options;
163
+
164
+ // 1. 加载预置环境模块
165
+ if (loadModules && !this.envLoaded) {
166
+ const envCode = loadOrder.map(n => modules[n]).filter(Boolean).join('\n\n');
167
+ await this.loadEnv(envCode);
168
+ }
169
+
170
+ // 2. 用 __createProxy__ 包裹全局对象,启用 missing 监控
171
+ await this._wrapGlobalsWithProxy();
172
+
173
+ const appliedPatches = [];
174
+ let lastMissingKey = '';
175
+
176
+ for (let i = 0; i < maxIterations; i++) {
177
+ const result = await this.execute(code, { timeout });
178
+
179
+ // 成功且无缺失,直接返回
180
+ if (result.success && result.missingEnv.length === 0) {
181
+ return { ...result, iterations: i + 1, appliedPatches };
182
+ }
183
+
184
+ // 没有新的缺失可补,退出
185
+ const currentKey = result.missingEnv.sort().join('|');
186
+ if (result.missingEnv.length === 0 || currentKey === lastMissingKey) {
187
+ return {
188
+ ...result,
189
+ iterations: i + 1,
190
+ appliedPatches,
191
+ remainingMissing: result.missingEnv,
192
+ stalled: currentKey === lastMissingKey,
193
+ };
194
+ }
195
+ lastMissingKey = currentKey;
196
+
197
+ // 非环境类错误(timeout / runtime-error),不继续补
198
+ if (!result.success) {
199
+ const envErrors = ['undefined-reference', 'not-a-function', 'null-access'];
200
+ if (!envErrors.includes(result.errorType)) {
201
+ return {
202
+ ...result,
203
+ iterations: i + 1,
204
+ appliedPatches,
205
+ remainingMissing: result.missingEnv,
206
+ };
207
+ }
208
+ }
209
+
210
+ // 批量生成补丁并注入(过滤掉 skipped 和低置信度的)
211
+ // 未加载模块时,不跳过 coveredAPIs 中的属性
212
+ const genContext = this.envLoaded ? {} : { skipCoveredCheck: true };
213
+ const batch = await this.patchGenerator.generateBatch(result.missingEnv, genContext);
214
+ const effective = batch.patches.filter(p => p.confidence >= 0.5 && !p.skipped);
215
+ const patchCode = this.patchGenerator.mergePatchCode(effective);
216
+
217
+ if (!patchCode.trim()) {
218
+ return {
219
+ ...result,
220
+ iterations: i + 1,
221
+ appliedPatches,
222
+ remainingMissing: result.missingEnv,
223
+ };
224
+ }
225
+
226
+ const injectResult = await this.inject(patchCode);
227
+ if (!injectResult.success) {
228
+ return {
229
+ ...result,
230
+ iterations: i + 1,
231
+ appliedPatches,
232
+ remainingMissing: result.missingEnv,
233
+ patchError: injectResult.error,
234
+ };
235
+ }
236
+
237
+ appliedPatches.push(...effective);
238
+
239
+ // 补丁注入后重新包裹全局对象,恢复 Proxy 监控
240
+ await this._wrapGlobalsWithProxy();
241
+ }
242
+
243
+ // 达到最大迭代次数
244
+ const finalResult = await this.execute(code, { timeout });
245
+ return {
246
+ ...finalResult,
247
+ iterations: maxIterations,
248
+ appliedPatches,
249
+ remainingMissing: finalResult.missingEnv,
250
+ maxIterationsReached: true,
251
+ };
252
+ }
253
+
254
+ /**
255
+ * 将全局环境对象用 __createProxy__ 包裹,启用缺失属性监控
256
+ */
257
+ async _wrapGlobalsWithProxy() {
258
+ const wrapCode = `
259
+ (function() {
260
+ var targets = ['navigator', 'document', 'screen', 'location', 'history'];
261
+ for (var i = 0; i < targets.length; i++) {
262
+ var name = targets[i];
263
+ // 全局对象不存在时先创建空壳,确保 Proxy 可监控
264
+ if (typeof global[name] === 'undefined') {
265
+ global[name] = {};
266
+ }
267
+ if (typeof global[name] === 'object' && global[name] !== null) {
268
+ try {
269
+ global[name] = __createProxy__(global[name], name);
270
+ } catch(e) {}
271
+ }
272
+ }
273
+ })();
274
+ `;
275
+ await this.context.eval(wrapCode);
276
+ }
277
+
141
278
  // 错误分类(帮助判断是环境缺失还是代码错误)
142
279
  _classifyError(e) {
143
280
  const msg = e.message || '';
@@ -1,10 +1,9 @@
1
1
  /**
2
2
  * DeepSpider - 环境代码生成器
3
- * 借鉴 v_jstools make_env_1_v3.js
4
- * 根据 Hook 收集的数据生成可在 Node.js 运行的浏览器环境代码
3
+ * 基于预置模块 + Hook 日志数据生成可在 Node.js 运行的浏览器环境代码
5
4
  */
6
5
 
7
- import { GETSET_LIST, FUNC_LIST, HTML_TAG_MAP } from './BrowserAPIList.js';
6
+ import { modules, loadOrder } from './modules/index.js';
8
7
 
9
8
  export class EnvCodeGenerator {
10
9
  constructor() {
@@ -20,21 +19,26 @@ export class EnvCodeGenerator {
20
19
  }
21
20
 
22
21
  /**
23
- * 解析环境日志
22
+ * 解析环境日志(支持任意深度路径)
24
23
  */
25
24
  parseEnvLogs(logs) {
26
25
  for (const log of logs) {
27
26
  if (log._type === 'env' && log.path) {
28
27
  const parts = log.path.split('.');
29
- const className = parts[0];
30
- const propName = parts[1];
31
- if (!this.envData[className]) {
32
- this.envData[className] = { props: {}, methods: {} };
28
+ const rootObj = parts[0];
29
+
30
+ if (!this.envData[rootObj]) {
31
+ this.envData[rootObj] = { props: {}, methods: {} };
33
32
  }
33
+
34
+ // 完整路径作为 key,保留深层访问信息
35
+ const fullPath = parts.slice(1).join('.');
36
+ if (!fullPath) continue;
37
+
34
38
  if (log.type === 'get' || log.type === 'set') {
35
- this.envData[className].props[propName] = log.value;
39
+ this.envData[rootObj].props[fullPath] = log.value;
36
40
  } else if (log.type === 'call') {
37
- this.envData[className].methods[propName] = log.result;
41
+ this.envData[rootObj].methods[fullPath] = log.result;
38
42
  }
39
43
  }
40
44
  }
@@ -49,8 +53,8 @@ export class EnvCodeGenerator {
49
53
  lines.push('// 生成时间: ' + new Date().toISOString());
50
54
  lines.push('');
51
55
  lines.push(this.generateSafeWrapper());
52
- lines.push(this.generateBaseClasses());
53
- lines.push(this.generateGlobalObjects());
56
+ lines.push(this.generateModules());
57
+ lines.push(this.generateOverrides());
54
58
  return lines.join('\n');
55
59
  }
56
60
 
@@ -69,88 +73,56 @@ function v_saf(fn) {
69
73
  }
70
74
 
71
75
  /**
72
- * 生成基础类
76
+ * 加载预置模块代码
73
77
  */
74
- generateBaseClasses() {
75
- return `
76
- // EventTarget 基类
77
- class EventTarget {
78
- constructor() { this._listeners = {}; }
79
- addEventListener(type, fn) {
80
- if (!this._listeners[type]) this._listeners[type] = [];
81
- this._listeners[type].push(fn);
82
- }
83
- removeEventListener(type, fn) {
84
- if (!this._listeners[type]) return;
85
- this._listeners[type] = this._listeners[type].filter(f => f !== fn);
86
- }
87
- dispatchEvent(event) {
88
- const fns = this._listeners[event.type] || [];
89
- fns.forEach(fn => fn.call(this, event));
90
- return true;
91
- }
92
- }
93
-
94
- // Node 基类
95
- class Node extends EventTarget {
96
- constructor() {
97
- super();
98
- this.childNodes = [];
99
- this.parentNode = null;
100
- }
101
- appendChild(node) { node.parentNode = this; this.childNodes.push(node); return node; }
102
- removeChild(node) {
103
- const idx = this.childNodes.indexOf(node);
104
- if (idx > -1) this.childNodes.splice(idx, 1);
105
- node.parentNode = null;
106
- return node;
107
- }
108
- }`;
78
+ generateModules() {
79
+ return loadOrder
80
+ .map(name => modules[name])
81
+ .filter(Boolean)
82
+ .join('\n\n');
109
83
  }
110
84
 
111
85
  /**
112
- * 生成全局对象
86
+ * 根据 Hook 日志数据生成覆盖补丁
87
+ * 用真实采集值覆盖预置模块的默认值
113
88
  */
114
- generateGlobalObjects() {
115
- const nav = this.envData.navigator?.props || {};
116
- const scr = this.envData.screen?.props || {};
89
+ generateOverrides() {
90
+ const lines = ['\n// Hook 数据覆盖(真实环境值)'];
91
+ let hasOverrides = false;
117
92
 
118
- return `
119
- // Navigator
120
- var navigator = {
121
- userAgent: ${JSON.stringify(nav.userAgent || 'Mozilla/5.0')},
122
- platform: ${JSON.stringify(nav.platform || 'Win32')},
123
- language: ${JSON.stringify(nav.language || 'zh-CN')},
124
- cookieEnabled: true,
125
- onLine: true,
126
- webdriver: false,
127
- };
128
-
129
- // Screen
130
- var screen = {
131
- width: ${scr.width || 1920},
132
- height: ${scr.height || 1080},
133
- colorDepth: 24,
134
- };
135
-
136
- // Location
137
- var location = { href: '', protocol: 'https:', host: '', pathname: '/' };
138
-
139
- // Document
140
- var document = {
141
- cookie: '',
142
- createElement: function(tag) { return {}; },
143
- getElementById: function(id) { return null; },
144
- querySelector: function(sel) { return null; },
145
- };
146
-
147
- // Window
148
- var window = this;
149
- window.navigator = navigator;
150
- window.screen = screen;
151
- window.location = location;
152
- window.document = document;
153
- `;
93
+ for (const [rootObj, data] of Object.entries(this.envData)) {
94
+ // 属性覆盖
95
+ for (const [path, value] of Object.entries(data.props)) {
96
+ if (value === undefined || value === '[Object]') continue;
97
+ const fullPath = `${rootObj}.${path}`;
98
+ const parts = fullPath.split('.');
99
+
100
+ // 深层路径需要确保中间对象存在
101
+ if (parts.length > 2) {
102
+ for (let i = 2; i < parts.length; i++) {
103
+ const parentPath = parts.slice(0, i).join('.');
104
+ lines.push(`try { if (typeof ${parentPath} === 'undefined') ${parentPath} = {}; } catch(e) {}`);
105
+ }
106
+ }
107
+
108
+ lines.push(`try { ${fullPath} = ${this._serializeValue(value)}; } catch(e) {}`);
109
+ hasOverrides = true;
110
+ }
111
+ }
112
+
113
+ return hasOverrides ? lines.join('\n') : '';
114
+ }
115
+
116
+ _serializeValue(value) {
117
+ if (value === null) return 'null';
118
+ if (value === undefined) return 'undefined';
119
+ if (typeof value === 'string') return JSON.stringify(value);
120
+ if (typeof value === 'number' || typeof value === 'boolean') return String(value);
121
+ try {
122
+ return JSON.stringify(value);
123
+ } catch {
124
+ return 'undefined';
125
+ }
154
126
  }
155
127
  }
156
128
 
@@ -26,4 +26,10 @@ export const historyCode = `
26
26
  })();
27
27
  `;
28
28
 
29
+ export const historyCovers = [
30
+ 'history.length', 'history.state', 'history.scrollRestoration',
31
+ 'history.back', 'history.forward', 'history.go',
32
+ 'history.pushState', 'history.replaceState',
33
+ ];
34
+
29
35
  export default historyCode;
@@ -23,4 +23,10 @@ export const locationCode = `
23
23
  })();
24
24
  `;
25
25
 
26
+ export const locationCovers = [
27
+ 'location.href', 'location.protocol', 'location.host', 'location.hostname',
28
+ 'location.port', 'location.pathname', 'location.search', 'location.hash',
29
+ 'location.origin', 'location.assign', 'location.replace', 'location.reload',
30
+ ];
31
+
26
32
  export default locationCode;
@@ -67,4 +67,17 @@ export const navigatorCode = `
67
67
  })();
68
68
  `;
69
69
 
70
+ export const navigatorCovers = [
71
+ 'navigator.userAgent', 'navigator.appCodeName', 'navigator.appName',
72
+ 'navigator.appVersion', 'navigator.platform', 'navigator.product',
73
+ 'navigator.vendor', 'navigator.language', 'navigator.languages',
74
+ 'navigator.onLine', 'navigator.cookieEnabled', 'navigator.hardwareConcurrency',
75
+ 'navigator.maxTouchPoints', 'navigator.deviceMemory', 'navigator.webdriver',
76
+ 'navigator.doNotTrack', 'navigator.plugins', 'navigator.mimeTypes',
77
+ 'navigator.connection', 'navigator.geolocation', 'navigator.permissions',
78
+ 'navigator.clipboard', 'navigator.userAgentData',
79
+ 'navigator.javaEnabled', 'navigator.vibrate', 'navigator.sendBeacon',
80
+ 'navigator.getBattery',
81
+ ];
82
+
70
83
  export default navigatorCode;
@@ -23,4 +23,10 @@ export const screenCode = `
23
23
  })();
24
24
  `;
25
25
 
26
+ export const screenCovers = [
27
+ 'screen.width', 'screen.height', 'screen.availWidth', 'screen.availHeight',
28
+ 'screen.colorDepth', 'screen.pixelDepth', 'screen.availLeft', 'screen.availTop',
29
+ 'screen.orientation',
30
+ ];
31
+
26
32
  export default screenCode;
@@ -20,4 +20,11 @@ export const storageCode = `
20
20
  })();
21
21
  `;
22
22
 
23
+ export const storageCovers = [
24
+ 'localStorage.length', 'localStorage.key', 'localStorage.getItem',
25
+ 'localStorage.setItem', 'localStorage.removeItem', 'localStorage.clear',
26
+ 'sessionStorage.length', 'sessionStorage.key', 'sessionStorage.getItem',
27
+ 'sessionStorage.setItem', 'sessionStorage.removeItem', 'sessionStorage.clear',
28
+ ];
29
+
23
30
  export default storageCode;
@@ -107,4 +107,18 @@ export const documentCode = `
107
107
  })();
108
108
  `;
109
109
 
110
+ export const documentCovers = [
111
+ 'document.nodeType', 'document.nodeName', 'document.documentElement',
112
+ 'document.head', 'document.body', 'document.title', 'document.cookie',
113
+ 'document.domain', 'document.URL', 'document.referrer', 'document.readyState',
114
+ 'document.hidden', 'document.visibilityState',
115
+ 'document.createElement', 'document.createTextNode',
116
+ 'document.createDocumentFragment', 'document.createComment',
117
+ 'document.getElementById', 'document.getElementsByTagName',
118
+ 'document.getElementsByClassName', 'document.getElementsByName',
119
+ 'document.querySelector', 'document.querySelectorAll',
120
+ 'document.addEventListener', 'document.removeEventListener',
121
+ 'document.dispatchEvent', 'document.write', 'document.writeln',
122
+ ];
123
+
110
124
  export default documentCode;
@@ -48,4 +48,8 @@ export const eventCode = `
48
48
  })();
49
49
  `;
50
50
 
51
+ export const eventCovers = [
52
+ 'Event', 'CustomEvent', 'MouseEvent', 'KeyboardEvent',
53
+ ];
54
+
51
55
  export default eventCode;