@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.
- package/.codeartsdoer/.codebaseignore +0 -0
- package/.codeartsdoer/AGENTS.md +12 -0
- package/.github/workflows/build.yml +146 -0
- package/README.en.md +264 -0
- package/README.md +276 -0
- package/RELEASE_NOTES.md +16 -0
- package/USAGE.md +490 -0
- package/color-preview-r3.html +414 -0
- package/color-preview.html +414 -0
- package/dev-app-update.yml +3 -0
- package/electron-builder.yml +36 -0
- package/electron.vite.config.ts +40 -0
- package/package.json +53 -0
- package/report-2026-04-13-copilot-claude-sonnet-4.6.md +955 -0
- package/resources/doloffer-logo.png +0 -0
- package/resources/entitlements.mac.plist +12 -0
- package/resources/icon.ico +0 -0
- package/resources/icon.png +0 -0
- package/src/main/ai/ai-analyzer.ts +517 -0
- package/src/main/ai/crypto-script-extractor.ts +206 -0
- package/src/main/ai/data-assembler.ts +205 -0
- package/src/main/ai/llm-router.ts +1120 -0
- package/src/main/ai/prompt-builder.ts +349 -0
- package/src/main/ai/scene-detector.ts +302 -0
- package/src/main/capture/capture-engine.ts +130 -0
- package/src/main/capture/interaction-recorder.ts +171 -0
- package/src/main/capture/js-injector.ts +57 -0
- package/src/main/capture/replay-engine.ts +256 -0
- package/src/main/capture/storage-collector.ts +76 -0
- package/src/main/cdp/cdp-manager.ts +233 -0
- package/src/main/db/database.ts +41 -0
- package/src/main/db/migrations.ts +235 -0
- package/src/main/db/repositories.ts +574 -0
- package/src/main/fingerprint/http-spoofing.ts +48 -0
- package/src/main/fingerprint/presets.ts +173 -0
- package/src/main/fingerprint/profile-generator.ts +115 -0
- package/src/main/fingerprint/profile-store.ts +52 -0
- package/src/main/index.ts +260 -0
- package/src/main/ipc.ts +856 -0
- package/src/main/logger.ts +42 -0
- package/src/main/mcp/mcp-config.ts +66 -0
- package/src/main/mcp/mcp-manager.ts +155 -0
- package/src/main/mcp/mcp-server.ts +1038 -0
- package/src/main/prompt-templates.ts +170 -0
- package/src/main/proxy/ca-manager.ts +204 -0
- package/src/main/proxy/cert-download-page.ts +171 -0
- package/src/main/proxy/cert-installer.ts +242 -0
- package/src/main/proxy/mitm-proxy-config.ts +37 -0
- package/src/main/proxy/mitm-proxy-server.ts +1085 -0
- package/src/main/proxy/system-proxy.ts +248 -0
- package/src/main/session/session-manager.ts +724 -0
- package/src/main/tab-manager.ts +582 -0
- package/src/main/updater.ts +111 -0
- package/src/main/window.ts +235 -0
- package/src/preload/hook-script.ts +270 -0
- package/src/preload/index.ts +211 -0
- package/src/preload/interaction-hook.ts +286 -0
- package/src/preload/stealth-script.ts +302 -0
- package/src/preload/target-preload.ts +15 -0
- package/src/renderer/App.tsx +656 -0
- package/src/renderer/components/AiLogDetail.tsx +173 -0
- package/src/renderer/components/AiLogList.tsx +101 -0
- package/src/renderer/components/AiLogView.module.css +364 -0
- package/src/renderer/components/AiLogView.tsx +86 -0
- package/src/renderer/components/AnalyzeBar.module.css +79 -0
- package/src/renderer/components/AnalyzeBar.tsx +104 -0
- package/src/renderer/components/BrowserPanel.module.css +67 -0
- package/src/renderer/components/BrowserPanel.tsx +90 -0
- package/src/renderer/components/ControlBar.module.css +47 -0
- package/src/renderer/components/ControlBar.tsx +205 -0
- package/src/renderer/components/HookLog.tsx +132 -0
- package/src/renderer/components/InteractionLog.tsx +183 -0
- package/src/renderer/components/MCPServerModal.tsx +427 -0
- package/src/renderer/components/PromptTemplateModal.tsx +254 -0
- package/src/renderer/components/ReportView.module.css +413 -0
- package/src/renderer/components/ReportView.tsx +429 -0
- package/src/renderer/components/RequestDetail.module.css +191 -0
- package/src/renderer/components/RequestDetail.tsx +202 -0
- package/src/renderer/components/RequestLog.module.css +69 -0
- package/src/renderer/components/RequestLog.tsx +208 -0
- package/src/renderer/components/SessionList.module.css +245 -0
- package/src/renderer/components/SessionList.tsx +247 -0
- package/src/renderer/components/SettingsModal.tsx +100 -0
- package/src/renderer/components/StatusBar.module.css +44 -0
- package/src/renderer/components/StatusBar.tsx +102 -0
- package/src/renderer/components/StorageView.module.css +41 -0
- package/src/renderer/components/StorageView.tsx +178 -0
- package/src/renderer/components/TabBar.module.css +88 -0
- package/src/renderer/components/TabBar.tsx +70 -0
- package/src/renderer/components/Titlebar.module.css +254 -0
- package/src/renderer/components/Titlebar.tsx +169 -0
- package/src/renderer/components/settings/FingerprintSection.tsx +198 -0
- package/src/renderer/components/settings/GeneralSection.tsx +164 -0
- package/src/renderer/components/settings/LLMSection.tsx +148 -0
- package/src/renderer/components/settings/MCPServerSection.tsx +136 -0
- package/src/renderer/components/settings/MitmProxySection.tsx +320 -0
- package/src/renderer/components/settings/ProxySection.tsx +110 -0
- package/src/renderer/css-modules.d.ts +4 -0
- package/src/renderer/hooks/useCapture.ts +383 -0
- package/src/renderer/hooks/useConfirm.tsx +91 -0
- package/src/renderer/hooks/useSession.ts +136 -0
- package/src/renderer/hooks/useTabs.ts +103 -0
- package/src/renderer/i18n/en.ts +167 -0
- package/src/renderer/i18n/index.ts +47 -0
- package/src/renderer/i18n/zh.ts +170 -0
- package/src/renderer/index.html +12 -0
- package/src/renderer/main.tsx +15 -0
- package/src/renderer/styles/global.css +144 -0
- package/src/renderer/styles/themes/ayu-dark.css +59 -0
- package/src/renderer/styles/themes/catppuccin.css +59 -0
- package/src/renderer/styles/themes/discord.css +59 -0
- package/src/renderer/styles/themes/dracula.css +59 -0
- package/src/renderer/styles/themes/github-dark.css +59 -0
- package/src/renderer/styles/themes/gruvbox.css +59 -0
- package/src/renderer/styles/themes/index.css +11 -0
- package/src/renderer/styles/themes/light.css +59 -0
- package/src/renderer/styles/themes/nord.css +59 -0
- package/src/renderer/styles/themes/one-dark.css +59 -0
- package/src/renderer/styles/themes/tokyo-night.css +59 -0
- package/src/renderer/styles/tokens.css +137 -0
- package/src/renderer/theme.ts +31 -0
- package/src/renderer/ui/Badge.module.css +38 -0
- package/src/renderer/ui/Badge.tsx +36 -0
- package/src/renderer/ui/Button.module.css +142 -0
- package/src/renderer/ui/Button.tsx +46 -0
- package/src/renderer/ui/Collapse.module.css +49 -0
- package/src/renderer/ui/Collapse.tsx +57 -0
- package/src/renderer/ui/CopyableBlock.module.css +56 -0
- package/src/renderer/ui/CopyableBlock.tsx +42 -0
- package/src/renderer/ui/Empty.module.css +19 -0
- package/src/renderer/ui/Empty.tsx +34 -0
- package/src/renderer/ui/Icons.tsx +346 -0
- package/src/renderer/ui/Input.module.css +103 -0
- package/src/renderer/ui/Input.tsx +94 -0
- package/src/renderer/ui/InputNumber.module.css +68 -0
- package/src/renderer/ui/InputNumber.tsx +104 -0
- package/src/renderer/ui/Modal.module.css +83 -0
- package/src/renderer/ui/Modal.tsx +67 -0
- package/src/renderer/ui/Popconfirm.module.css +73 -0
- package/src/renderer/ui/Popconfirm.tsx +74 -0
- package/src/renderer/ui/Progress.module.css +35 -0
- package/src/renderer/ui/Progress.tsx +30 -0
- package/src/renderer/ui/Select.module.css +91 -0
- package/src/renderer/ui/Select.tsx +100 -0
- package/src/renderer/ui/Spinner.module.css +44 -0
- package/src/renderer/ui/Spinner.tsx +27 -0
- package/src/renderer/ui/Switch.module.css +39 -0
- package/src/renderer/ui/Switch.tsx +43 -0
- package/src/renderer/ui/Tabs.module.css +76 -0
- package/src/renderer/ui/Tabs.tsx +53 -0
- package/src/renderer/ui/Tag.module.css +66 -0
- package/src/renderer/ui/Tag.tsx +47 -0
- package/src/renderer/ui/Timeline.module.css +42 -0
- package/src/renderer/ui/Timeline.tsx +29 -0
- package/src/renderer/ui/Toast.module.css +99 -0
- package/src/renderer/ui/Toast.tsx +90 -0
- package/src/renderer/ui/Tooltip.module.css +26 -0
- package/src/renderer/ui/Tooltip.tsx +23 -0
- package/src/renderer/ui/VirtualTable.module.css +230 -0
- package/src/renderer/ui/VirtualTable.tsx +416 -0
- package/src/renderer/ui/index.ts +55 -0
- package/src/shared/types.ts +695 -0
- package/tests/main/ai/crypto-script-extractor.test.ts +281 -0
- package/tests/main/ai/llm-router.test.ts +1537 -0
- package/tests/main/ai/prompt-builder.test.ts +178 -0
- package/tests/main/ai/scene-detector.test.ts +212 -0
- package/tests/main/db/migrations.test.ts +134 -0
- package/tests/main/release-workflow.test.ts +59 -0
- package/tsconfig.json +7 -0
- package/tsconfig.node.json +23 -0
- package/tsconfig.web.json +24 -0
- package/vitest.config.ts +13 -0
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stealth Script — Injected into page context BEFORE hook-script.
|
|
3
|
+
* Overrides browser APIs to match the fingerprint profile.
|
|
4
|
+
*
|
|
5
|
+
* This file is designed to be stringified and injected via executeJavaScript().
|
|
6
|
+
* The __FINGERPRINT_PROFILE__ placeholder is replaced at injection time.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export function buildStealthScript(profileJson: string): string {
|
|
10
|
+
// Defensive escaping: ensure JSON can be safely embedded in a JS literal
|
|
11
|
+
const safeJson = profileJson
|
|
12
|
+
.replace(/\u2028/g, '\\u2028')
|
|
13
|
+
.replace(/\u2029/g, '\\u2029')
|
|
14
|
+
.replace(/<\/script/gi, '<\\/script');
|
|
15
|
+
return `(function() {
|
|
16
|
+
'use strict';
|
|
17
|
+
if (window.__stealth_applied__) return;
|
|
18
|
+
window.__stealth_applied__ = true;
|
|
19
|
+
|
|
20
|
+
const profile = ${safeJson};
|
|
21
|
+
|
|
22
|
+
// === Utility: make overridden functions look native ===
|
|
23
|
+
function makeNative(fn, name) {
|
|
24
|
+
const nativeToString = function() { return 'function ' + name + '() { [native code] }'; };
|
|
25
|
+
Object.defineProperty(nativeToString, 'name', { value: 'toString' });
|
|
26
|
+
fn.toString = nativeToString;
|
|
27
|
+
return fn;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function overrideGetter(obj, prop, value) {
|
|
31
|
+
try {
|
|
32
|
+
const desc = Object.getOwnPropertyDescriptor(obj, prop) ||
|
|
33
|
+
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(obj), prop);
|
|
34
|
+
if (desc && desc.get) {
|
|
35
|
+
const newGet = makeNative(function() { return value; }, 'get ' + prop);
|
|
36
|
+
Object.defineProperty(obj, prop, { get: newGet, configurable: true });
|
|
37
|
+
} else {
|
|
38
|
+
Object.defineProperty(obj, prop, { value: value, writable: false, configurable: true });
|
|
39
|
+
}
|
|
40
|
+
} catch(e) {}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Seeded PRNG (mulberry32) — top-level so all noise sections can use it
|
|
44
|
+
function mulberry32(seed) {
|
|
45
|
+
return function() {
|
|
46
|
+
seed |= 0; seed = seed + 0x6D2B79F5 | 0;
|
|
47
|
+
var t = Math.imul(seed ^ seed >>> 15, 1 | seed);
|
|
48
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
49
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// ========== Layer 2: Navigator / Screen Overrides ==========
|
|
54
|
+
|
|
55
|
+
// navigator.webdriver
|
|
56
|
+
try {
|
|
57
|
+
Object.defineProperty(navigator, 'webdriver', {
|
|
58
|
+
get: makeNative(function() { return false; }, 'get webdriver'),
|
|
59
|
+
configurable: true,
|
|
60
|
+
});
|
|
61
|
+
} catch(e) {}
|
|
62
|
+
|
|
63
|
+
// navigator.platform
|
|
64
|
+
overrideGetter(navigator, 'platform', profile.platform);
|
|
65
|
+
|
|
66
|
+
// navigator.languages
|
|
67
|
+
overrideGetter(navigator, 'languages', Object.freeze([...profile.languages]));
|
|
68
|
+
overrideGetter(navigator, 'language', profile.languages[0]);
|
|
69
|
+
|
|
70
|
+
// navigator.hardwareConcurrency
|
|
71
|
+
overrideGetter(navigator, 'hardwareConcurrency', profile.hardwareConcurrency);
|
|
72
|
+
|
|
73
|
+
// navigator.deviceMemory
|
|
74
|
+
overrideGetter(navigator, 'deviceMemory', profile.deviceMemory);
|
|
75
|
+
|
|
76
|
+
// navigator.plugins — fake PluginArray
|
|
77
|
+
try {
|
|
78
|
+
const fakePlugins = [
|
|
79
|
+
{ name: 'Chrome PDF Plugin', filename: 'internal-pdf-viewer', description: 'Portable Document Format' },
|
|
80
|
+
{ name: 'Chrome PDF Viewer', filename: 'mhjfbmdgcfjbbpaeojofohoefgiehjai', description: '' },
|
|
81
|
+
{ name: 'Native Client', filename: 'internal-nacl-plugin', description: '' },
|
|
82
|
+
];
|
|
83
|
+
const pluginArr = Object.create(PluginArray.prototype);
|
|
84
|
+
fakePlugins.forEach((p, i) => {
|
|
85
|
+
const plugin = Object.create(Plugin.prototype);
|
|
86
|
+
Object.defineProperty(plugin, 'name', { value: p.name });
|
|
87
|
+
Object.defineProperty(plugin, 'filename', { value: p.filename });
|
|
88
|
+
Object.defineProperty(plugin, 'description', { value: p.description });
|
|
89
|
+
Object.defineProperty(plugin, 'length', { value: 0 });
|
|
90
|
+
pluginArr[i] = plugin;
|
|
91
|
+
});
|
|
92
|
+
Object.defineProperty(pluginArr, 'length', { value: fakePlugins.length });
|
|
93
|
+
pluginArr.item = makeNative(function(i) { return pluginArr[i] || null; }, 'item');
|
|
94
|
+
pluginArr.namedItem = makeNative(function(name) {
|
|
95
|
+
for (let i = 0; i < pluginArr.length; i++) { if (pluginArr[i].name === name) return pluginArr[i]; }
|
|
96
|
+
return null;
|
|
97
|
+
}, 'namedItem');
|
|
98
|
+
pluginArr.refresh = makeNative(function() {}, 'refresh');
|
|
99
|
+
overrideGetter(navigator, 'plugins', pluginArr);
|
|
100
|
+
} catch(e) {}
|
|
101
|
+
|
|
102
|
+
// screen properties
|
|
103
|
+
overrideGetter(screen, 'width', profile.screenWidth);
|
|
104
|
+
overrideGetter(screen, 'height', profile.screenHeight);
|
|
105
|
+
overrideGetter(screen, 'availWidth', profile.screenWidth);
|
|
106
|
+
overrideGetter(screen, 'availHeight', profile.screenHeight - 40); // taskbar offset
|
|
107
|
+
overrideGetter(screen, 'colorDepth', profile.colorDepth);
|
|
108
|
+
overrideGetter(screen, 'pixelDepth', profile.colorDepth);
|
|
109
|
+
|
|
110
|
+
// window.devicePixelRatio
|
|
111
|
+
overrideGetter(window, 'devicePixelRatio', profile.devicePixelRatio);
|
|
112
|
+
|
|
113
|
+
// window.chrome — polyfill for non-headless detection
|
|
114
|
+
try {
|
|
115
|
+
if (!window.chrome) {
|
|
116
|
+
window.chrome = {};
|
|
117
|
+
}
|
|
118
|
+
if (!window.chrome.runtime) {
|
|
119
|
+
window.chrome.runtime = {
|
|
120
|
+
connect: makeNative(function() {}, 'connect'),
|
|
121
|
+
sendMessage: makeNative(function() {}, 'sendMessage'),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
} catch(e) {}
|
|
125
|
+
|
|
126
|
+
// Timezone spoofing
|
|
127
|
+
try {
|
|
128
|
+
const origDTF = Intl.DateTimeFormat;
|
|
129
|
+
const newDTF = makeNative(function(...args) {
|
|
130
|
+
const instance = new origDTF(...args);
|
|
131
|
+
const origResolved = instance.resolvedOptions.bind(instance);
|
|
132
|
+
instance.resolvedOptions = makeNative(function() {
|
|
133
|
+
const opts = origResolved();
|
|
134
|
+
opts.timeZone = profile.timezone;
|
|
135
|
+
return opts;
|
|
136
|
+
}, 'resolvedOptions');
|
|
137
|
+
return instance;
|
|
138
|
+
}, 'DateTimeFormat');
|
|
139
|
+
newDTF.prototype = origDTF.prototype;
|
|
140
|
+
newDTF.supportedLocalesOf = origDTF.supportedLocalesOf;
|
|
141
|
+
Intl.DateTimeFormat = newDTF;
|
|
142
|
+
} catch(e) {}
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
Date.prototype.getTimezoneOffset = makeNative(function() {
|
|
146
|
+
return profile.timezoneOffset;
|
|
147
|
+
}, 'getTimezoneOffset');
|
|
148
|
+
} catch(e) {}
|
|
149
|
+
|
|
150
|
+
// Permissions.query
|
|
151
|
+
try {
|
|
152
|
+
const origQuery = Permissions.prototype.query;
|
|
153
|
+
Permissions.prototype.query = makeNative(function(desc) {
|
|
154
|
+
if (desc && desc.name === 'notifications') {
|
|
155
|
+
return Promise.resolve({ state: 'prompt', onchange: null });
|
|
156
|
+
}
|
|
157
|
+
return origQuery.call(this, desc);
|
|
158
|
+
}, 'query');
|
|
159
|
+
} catch(e) {}
|
|
160
|
+
|
|
161
|
+
// ========== Layer 3: Canvas / WebGL / Audio / WebRTC Noise ==========
|
|
162
|
+
|
|
163
|
+
// --- Canvas fingerprint noise ---
|
|
164
|
+
try {
|
|
165
|
+
const canvasRng = mulberry32(profile.canvasNoise);
|
|
166
|
+
|
|
167
|
+
const origToDataURL = HTMLCanvasElement.prototype.toDataURL;
|
|
168
|
+
HTMLCanvasElement.prototype.toDataURL = makeNative(function(...args) {
|
|
169
|
+
try {
|
|
170
|
+
const ctx = this.getContext('2d');
|
|
171
|
+
if (ctx) {
|
|
172
|
+
const imageData = ctx.getImageData(0, 0, this.width, this.height);
|
|
173
|
+
const data = imageData.data;
|
|
174
|
+
// Apply subtle pixel noise
|
|
175
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
176
|
+
data[i] = data[i] + Math.floor((canvasRng() - 0.5) * 2); // R
|
|
177
|
+
data[i+1] = data[i+1] + Math.floor((canvasRng() - 0.5) * 2); // G
|
|
178
|
+
}
|
|
179
|
+
ctx.putImageData(imageData, 0, 0);
|
|
180
|
+
}
|
|
181
|
+
} catch(e) {}
|
|
182
|
+
return origToDataURL.apply(this, args);
|
|
183
|
+
}, 'toDataURL');
|
|
184
|
+
|
|
185
|
+
const origToBlob = HTMLCanvasElement.prototype.toBlob;
|
|
186
|
+
HTMLCanvasElement.prototype.toBlob = makeNative(function(...args) {
|
|
187
|
+
try {
|
|
188
|
+
const ctx = this.getContext('2d');
|
|
189
|
+
if (ctx) {
|
|
190
|
+
const imageData = ctx.getImageData(0, 0, this.width, this.height);
|
|
191
|
+
const data = imageData.data;
|
|
192
|
+
for (let i = 0; i < data.length; i += 4) {
|
|
193
|
+
data[i] = data[i] + Math.floor((canvasRng() - 0.5) * 2);
|
|
194
|
+
}
|
|
195
|
+
ctx.putImageData(imageData, 0, 0);
|
|
196
|
+
}
|
|
197
|
+
} catch(e) {}
|
|
198
|
+
return origToBlob.apply(this, args);
|
|
199
|
+
}, 'toBlob');
|
|
200
|
+
} catch(e) {}
|
|
201
|
+
|
|
202
|
+
// --- WebGL fingerprint ---
|
|
203
|
+
try {
|
|
204
|
+
const origGetParam = WebGLRenderingContext.prototype.getParameter;
|
|
205
|
+
WebGLRenderingContext.prototype.getParameter = makeNative(function(pname) {
|
|
206
|
+
const UNMASKED_VENDOR = 0x9245;
|
|
207
|
+
const UNMASKED_RENDERER = 0x9246;
|
|
208
|
+
if (pname === UNMASKED_VENDOR) return profile.webglVendor;
|
|
209
|
+
if (pname === UNMASKED_RENDERER) return profile.webglRenderer;
|
|
210
|
+
return origGetParam.call(this, pname);
|
|
211
|
+
}, 'getParameter');
|
|
212
|
+
|
|
213
|
+
if (typeof WebGL2RenderingContext !== 'undefined') {
|
|
214
|
+
const origGetParam2 = WebGL2RenderingContext.prototype.getParameter;
|
|
215
|
+
WebGL2RenderingContext.prototype.getParameter = makeNative(function(pname) {
|
|
216
|
+
const UNMASKED_VENDOR = 0x9245;
|
|
217
|
+
const UNMASKED_RENDERER = 0x9246;
|
|
218
|
+
if (pname === UNMASKED_VENDOR) return profile.webglVendor;
|
|
219
|
+
if (pname === UNMASKED_RENDERER) return profile.webglRenderer;
|
|
220
|
+
return origGetParam2.call(this, pname);
|
|
221
|
+
}, 'getParameter');
|
|
222
|
+
}
|
|
223
|
+
} catch(e) {}
|
|
224
|
+
|
|
225
|
+
// --- AudioContext fingerprint noise ---
|
|
226
|
+
try {
|
|
227
|
+
const audioRng = mulberry32(profile.audioNoise);
|
|
228
|
+
const origCreateOscillator = AudioContext.prototype.createOscillator;
|
|
229
|
+
AudioContext.prototype.createOscillator = makeNative(function() {
|
|
230
|
+
const osc = origCreateOscillator.call(this);
|
|
231
|
+
const origConnect = osc.connect.bind(osc);
|
|
232
|
+
osc.connect = makeNative(function(dest, ...args) {
|
|
233
|
+
if (dest instanceof AnalyserNode) {
|
|
234
|
+
// Inject a gain node with tiny noise
|
|
235
|
+
const gainNode = osc.context.createGain();
|
|
236
|
+
gainNode.gain.value = 1 + (audioRng() - 0.5) * 0.0001;
|
|
237
|
+
origConnect(gainNode);
|
|
238
|
+
gainNode.connect(dest);
|
|
239
|
+
return dest;
|
|
240
|
+
}
|
|
241
|
+
return origConnect(dest, ...args);
|
|
242
|
+
}, 'connect');
|
|
243
|
+
return osc;
|
|
244
|
+
}, 'createOscillator');
|
|
245
|
+
} catch(e) {}
|
|
246
|
+
|
|
247
|
+
// --- WebRTC protection ---
|
|
248
|
+
try {
|
|
249
|
+
if (profile.webrtcPolicy === 'block') {
|
|
250
|
+
window.RTCPeerConnection = makeNative(function() {
|
|
251
|
+
throw new DOMException('WebRTC is disabled', 'NotAllowedError');
|
|
252
|
+
}, 'RTCPeerConnection');
|
|
253
|
+
window.webkitRTCPeerConnection = window.RTCPeerConnection;
|
|
254
|
+
if (window.RTCSessionDescription) {
|
|
255
|
+
window.RTCSessionDescription = makeNative(function() {
|
|
256
|
+
throw new DOMException('WebRTC is disabled', 'NotAllowedError');
|
|
257
|
+
}, 'RTCSessionDescription');
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch(e) {}
|
|
261
|
+
|
|
262
|
+
// ========== iframe sync ==========
|
|
263
|
+
try {
|
|
264
|
+
const observer = new MutationObserver(function(mutations) {
|
|
265
|
+
mutations.forEach(function(mutation) {
|
|
266
|
+
mutation.addedNodes.forEach(function(node) {
|
|
267
|
+
if (node.tagName === 'IFRAME') {
|
|
268
|
+
injectIntoIframe(node);
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
});
|
|
272
|
+
});
|
|
273
|
+
observer.observe(document.documentElement, { childList: true, subtree: true });
|
|
274
|
+
|
|
275
|
+
function injectIntoIframe(iframe) {
|
|
276
|
+
const tryInject = function(attempt) {
|
|
277
|
+
try {
|
|
278
|
+
const win = iframe.contentWindow;
|
|
279
|
+
if (!win || win.__stealth_applied__) return;
|
|
280
|
+
// Re-apply critical overrides in iframe context
|
|
281
|
+
Object.defineProperty(win.navigator, 'webdriver', {
|
|
282
|
+
get: function() { return false; },
|
|
283
|
+
configurable: true,
|
|
284
|
+
});
|
|
285
|
+
win.__stealth_applied__ = true;
|
|
286
|
+
} catch(e) {
|
|
287
|
+
// Cross-origin iframe — cannot access, skip silently
|
|
288
|
+
if (attempt < 3) {
|
|
289
|
+
requestAnimationFrame(function() { tryInject(attempt + 1); });
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
};
|
|
293
|
+
iframe.addEventListener('load', function() { tryInject(0); });
|
|
294
|
+
tryInject(0);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
// Process existing iframes
|
|
298
|
+
document.querySelectorAll('iframe').forEach(injectIntoIframe);
|
|
299
|
+
} catch(e) {}
|
|
300
|
+
|
|
301
|
+
})();`;
|
|
302
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal preload for target browser tabs.
|
|
3
|
+
* Forwards hook/interaction messages from page context to main process via IPC.
|
|
4
|
+
* Does NOT expose any electronAPI to the page — keeps the target tab sandboxed.
|
|
5
|
+
*/
|
|
6
|
+
import { ipcRenderer } from "electron";
|
|
7
|
+
|
|
8
|
+
window.addEventListener("message", (event) => {
|
|
9
|
+
if (event.data?.type === "ar-hook") {
|
|
10
|
+
ipcRenderer.send("capture:hook-data", event.data);
|
|
11
|
+
}
|
|
12
|
+
if (event.data?.type === "ar-interaction") {
|
|
13
|
+
ipcRenderer.send("capture:hook-data", event.data);
|
|
14
|
+
}
|
|
15
|
+
});
|