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.
- package/.env.example +3 -0
- package/README.md +13 -13
- package/package.json +6 -6
- package/src/agent/core/PanelBridge.js +29 -77
- package/src/agent/core/StreamHandler.js +139 -14
- package/src/agent/index.js +51 -12
- package/src/agent/logger.js +184 -9
- package/src/agent/middleware/report.js +42 -16
- package/src/agent/middleware/subagent.js +233 -0
- package/src/agent/middleware/toolGuard.js +77 -0
- package/src/agent/middleware/validationWorkflow.js +171 -0
- package/src/agent/prompts/system.js +181 -59
- package/src/agent/run.js +41 -6
- package/src/agent/skills/crawler/SKILL.md +64 -3
- package/src/agent/skills/crawler/evolved.md +9 -1
- package/src/agent/skills/dynamic-analysis/SKILL.md +74 -7
- package/src/agent/skills/env/SKILL.md +75 -0
- package/src/agent/skills/evolve.js +0 -3
- package/src/agent/skills/sandbox/SKILL.md +35 -0
- package/src/agent/skills/static-analysis/SKILL.md +98 -2
- package/src/agent/subagents/anti-detect.js +10 -20
- package/src/agent/subagents/captcha.js +7 -19
- package/src/agent/subagents/crawler.js +25 -37
- package/src/agent/subagents/factory.js +109 -9
- package/src/agent/subagents/index.js +4 -13
- package/src/agent/subagents/js2python.js +7 -19
- package/src/agent/subagents/reverse.js +180 -0
- package/src/agent/tools/analysis.js +84 -1
- package/src/agent/tools/anti-detect.js +5 -2
- package/src/agent/tools/browser.js +160 -0
- package/src/agent/tools/captcha.js +1 -1
- package/src/agent/tools/capture.js +24 -3
- package/src/agent/tools/correlate.js +129 -15
- package/src/agent/tools/crawler.js +2 -1
- package/src/agent/tools/crawlerGenerator.js +90 -0
- package/src/agent/tools/debug.js +43 -6
- package/src/agent/tools/evolve.js +6 -3
- package/src/agent/tools/extractor.js +5 -1
- package/src/agent/tools/file.js +16 -7
- package/src/agent/tools/generateHook.js +66 -0
- package/src/agent/tools/hookManager.js +19 -9
- package/src/agent/tools/index.js +33 -20
- package/src/agent/tools/nodejs.js +41 -6
- package/src/agent/tools/python.js +4 -4
- package/src/agent/tools/report.js +2 -2
- package/src/agent/tools/runtime.js +1 -1
- package/src/agent/tools/sandbox.js +21 -1
- package/src/agent/tools/scratchpad.js +70 -0
- package/src/agent/tools/tracing.js +26 -0
- package/src/agent/tools/verifyAlgorithm.js +117 -0
- package/src/analyzer/EncryptionAnalyzer.js +2 -2
- package/src/browser/EnvBridge.js +27 -13
- package/src/browser/client.js +124 -18
- package/src/browser/collector.js +101 -22
- package/src/browser/defaultHooks.js +3 -1
- package/src/browser/hooks/index.js +5 -0
- package/src/browser/interceptors/AntiDebugInterceptor.js +132 -0
- package/src/browser/interceptors/NetworkInterceptor.js +77 -13
- package/src/browser/interceptors/ScriptInterceptor.js +34 -9
- package/src/browser/interceptors/index.js +1 -0
- package/src/browser/ui/analysisPanel.js +469 -464
- package/src/cli/commands/config.js +11 -3
- package/src/config/paths.js +9 -1
- package/src/config/settings.js +7 -1
- package/src/core/PatchGenerator.js +26 -6
- package/src/core/Sandbox.js +140 -3
- package/src/env/EnvCodeGenerator.js +60 -88
- package/src/env/modules/bom/history.js +6 -0
- package/src/env/modules/bom/location.js +6 -0
- package/src/env/modules/bom/navigator.js +13 -0
- package/src/env/modules/bom/screen.js +6 -0
- package/src/env/modules/bom/storage.js +7 -0
- package/src/env/modules/dom/document.js +14 -0
- package/src/env/modules/dom/event.js +4 -0
- package/src/env/modules/index.js +27 -10
- package/src/env/modules/webapi/fetch.js +4 -0
- package/src/env/modules/webapi/url.js +4 -0
- package/src/env/modules/webapi/xhr.js +8 -0
- package/src/store/DataStore.js +130 -47
- package/src/store/Store.js +2 -1
- package/src/agent/subagents/dynamic.js +0 -64
- package/src/agent/subagents/env-agent.js +0 -82
- package/src/agent/subagents/sandbox.js +0 -55
- 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
|
-
|
|
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);
|
package/src/config/paths.js
CHANGED
|
@@ -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 =>
|
|
51
|
+
Object.values(PATHS).forEach(dir => {
|
|
52
|
+
if (!ON_DEMAND_DIRS.has(dir)) ensureDir(dir);
|
|
53
|
+
});
|
|
46
54
|
}
|
|
47
55
|
|
|
48
56
|
/**
|
package/src/config/settings.js
CHANGED
|
@@ -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
|
-
|
|
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
|
-
|
|
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,
|
|
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 [
|
|
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, []);
|
package/src/core/Sandbox.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
87
|
+
const val = t[p];
|
|
88
|
+
if (val === undefined && !(p in t)) {
|
|
83
89
|
__recordMissing__(fullPath);
|
|
84
90
|
}
|
|
85
|
-
|
|
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
|
-
*
|
|
4
|
-
* 根据 Hook 收集的数据生成可在 Node.js 运行的浏览器环境代码
|
|
3
|
+
* 基于预置模块 + Hook 日志数据生成可在 Node.js 运行的浏览器环境代码
|
|
5
4
|
*/
|
|
6
5
|
|
|
7
|
-
import {
|
|
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
|
|
30
|
-
|
|
31
|
-
if (!this.envData[
|
|
32
|
-
this.envData[
|
|
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[
|
|
39
|
+
this.envData[rootObj].props[fullPath] = log.value;
|
|
36
40
|
} else if (log.type === 'call') {
|
|
37
|
-
this.envData[
|
|
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.
|
|
53
|
-
lines.push(this.
|
|
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
|
-
|
|
75
|
-
return
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
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
|
-
|
|
115
|
-
const
|
|
116
|
-
|
|
89
|
+
generateOverrides() {
|
|
90
|
+
const lines = ['\n// Hook 数据覆盖(真实环境值)'];
|
|
91
|
+
let hasOverrides = false;
|
|
117
92
|
|
|
118
|
-
|
|
119
|
-
//
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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;
|