@xbrowser/cli 1.18.1 → 1.19.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.
- package/dist/{browser-W3Z4HPJN.js → browser-6LH6KJSW.js} +1 -1
- package/dist/{browser-SYE57VHU.js → browser-TUD2KA5G.js} +2 -2
- package/dist/{browser-UPKOT3V6.js → browser-Y5SIE6LY.js} +2 -2
- package/dist/{cdp-driver-KT6HL33W.js → cdp-driver-2EECZW4X.js} +1 -1
- package/dist/{cdp-driver-4UAV56SQ.js → cdp-driver-7HO5SRNN.js} +1 -1
- package/dist/{cdp-driver-NNBPNFF2.js → cdp-driver-E7G3VHIB.js} +41 -3
- package/dist/{chunk-EI2XZIDF.js → chunk-2BPZ25RE.js} +72 -7
- package/dist/{chunk-MMZCBUD3.js → chunk-C6PW3CPA.js} +72 -7
- package/dist/{chunk-DVKIL7H6.js → chunk-GKYSCHT4.js} +41 -3
- package/dist/{chunk-OU335JBI.js → chunk-KLXLNQI5.js} +112 -9
- package/dist/{chunk-LL37GYPP.js → chunk-LKJZYVIX.js} +41 -3
- package/dist/cli.js +724 -14
- package/dist/daemon-main.js +732 -17
- package/dist/index.js +725 -15
- package/dist/{session-replayer-J4GXUULG.js → session-replayer-RODZYHM3.js} +1 -1
- package/package.json +1 -1
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-2BPZ25RE.js";
|
|
24
|
+
import "./chunk-LKJZYVIX.js";
|
|
25
25
|
import "./chunk-3KZ34AUC.js";
|
|
26
26
|
import "./chunk-GDKLH7ZY.js";
|
|
27
27
|
import "./chunk-A6LPGFAL.js";
|
|
@@ -20,8 +20,8 @@ import {
|
|
|
20
20
|
saveSessionDiskMeta,
|
|
21
21
|
setActivePage,
|
|
22
22
|
touchSession
|
|
23
|
-
} from "./chunk-
|
|
24
|
-
import "./chunk-
|
|
23
|
+
} from "./chunk-C6PW3CPA.js";
|
|
24
|
+
import "./chunk-GKYSCHT4.js";
|
|
25
25
|
import "./chunk-3FWLW7FS.js";
|
|
26
26
|
import "./chunk-3KZ34AUC.js";
|
|
27
27
|
import "./chunk-GDKLH7ZY.js";
|
|
@@ -133,8 +133,17 @@ function buildStealthInitScript() {
|
|
|
133
133
|
" if(!e)return f.call(this,e);",
|
|
134
134
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
135
135
|
' if(p==="sourceCapabilities")return fc;',
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
// S124 修正(d67 攻防):恒 true 是"过度伪装"——站点自己的合成事件
|
|
137
|
+
// 也变 true → 直接判定浏览器被篡改(比检测自动化更严重的指纹暴露)。
|
|
138
|
+
// 正确策略:透传原始值(CDP Input 事件本来就是 true,JS 合成本来是 false)
|
|
139
|
+
// + paste 定向伪装(合成的 paste 在真实场景都是 trusted 的键盘操作产物)
|
|
140
|
+
' if(p==="isTrusted"){',
|
|
141
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
142
|
+
" if(orig===true)return true;",
|
|
143
|
+
' if(k.type==="paste")return true;',
|
|
144
|
+
" return orig;",
|
|
145
|
+
" }",
|
|
146
|
+
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])){',
|
|
138
147
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
139
148
|
" return k[p]+_f;",
|
|
140
149
|
" }",
|
|
@@ -144,6 +153,30 @@ function buildStealthInitScript() {
|
|
|
144
153
|
" return o.call(this,t,w,op);",
|
|
145
154
|
" };",
|
|
146
155
|
" EventTarget.prototype.addEventListener=_ael;",
|
|
156
|
+
// window.event isTrusted 伪装(S123):AEL 参数包装收不到 window.event
|
|
157
|
+
// (它是原始 Event 对象的隐式引用)。定义 getter 拦截读取。
|
|
158
|
+
" try{",
|
|
159
|
+
' var _weDesc=Object.getOwnPropertyDescriptor(Window.prototype,"event");',
|
|
160
|
+
" if(_weDesc&&_weDesc.get){",
|
|
161
|
+
" var _origWeGet=_weDesc.get;",
|
|
162
|
+
' Object.defineProperty(Window.prototype,"event",{',
|
|
163
|
+
" configurable:true,",
|
|
164
|
+
" get:function(){",
|
|
165
|
+
" var ev=_origWeGet.call(this);",
|
|
166
|
+
" if(!ev)return ev;",
|
|
167
|
+
" return new Proxy(ev,{get:function(k,p){",
|
|
168
|
+
' if(p==="isTrusted"){',
|
|
169
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
170
|
+
" if(orig===true)return true;",
|
|
171
|
+
' if(k.type==="paste")return true;',
|
|
172
|
+
" return orig;",
|
|
173
|
+
" }",
|
|
174
|
+
' var v=Reflect.get(k,p);return typeof v==="function"?v.bind(k):v;',
|
|
175
|
+
" }});",
|
|
176
|
+
" }",
|
|
177
|
+
" });",
|
|
178
|
+
" }",
|
|
179
|
+
" }catch(e){}",
|
|
147
180
|
// 2. Screen override (prototype-level, not instance-level)
|
|
148
181
|
" var _gw=function(){return 1728};",
|
|
149
182
|
" var _gh=function(){return 1117};",
|
|
@@ -401,7 +434,12 @@ function buildStealthInitScript() {
|
|
|
401
434
|
" };",
|
|
402
435
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
403
436
|
" var _ba=function(k,p){",
|
|
404
|
-
' if(p==="isTrusted")
|
|
437
|
+
' if(p==="isTrusted"){',
|
|
438
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
439
|
+
" if(orig===true)return true;",
|
|
440
|
+
' if(k.type==="paste")return true;',
|
|
441
|
+
" return orig;",
|
|
442
|
+
" }",
|
|
405
443
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
406
444
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
407
445
|
" }",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
launch
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-LKJZYVIX.js";
|
|
4
4
|
import {
|
|
5
5
|
errMsg
|
|
6
6
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -68,7 +68,9 @@ async function resolveCDPEndpoint(raw) {
|
|
|
68
68
|
}
|
|
69
69
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
70
70
|
try {
|
|
71
|
-
const
|
|
71
|
+
const u = new URL(raw);
|
|
72
|
+
u.pathname = (u.pathname.replace(/\/+$/, "") || "") + "/json/version";
|
|
73
|
+
const httpResp = await fetchNoProxy(u.toString());
|
|
72
74
|
const data = await httpResp.json();
|
|
73
75
|
if (!data.webSocketDebuggerUrl) {
|
|
74
76
|
throw new Error(`Could not discover CDP endpoint from ${raw}`);
|
|
@@ -206,9 +208,17 @@ function discoverChromiumPath() {
|
|
|
206
208
|
}
|
|
207
209
|
return void 0;
|
|
208
210
|
}
|
|
211
|
+
var _browserPool = /* @__PURE__ */ new Map();
|
|
209
212
|
async function createBrowser(options) {
|
|
210
213
|
if (options?.cdpEndpoint) {
|
|
211
214
|
const realEndpoint = await resolveCDPEndpoint(options.cdpEndpoint);
|
|
215
|
+
if (!options.intercept) {
|
|
216
|
+
const pooled = _browserPool.get(realEndpoint);
|
|
217
|
+
if (pooled && !pooled.disconnected) {
|
|
218
|
+
return pooled;
|
|
219
|
+
}
|
|
220
|
+
if (pooled) _browserPool.delete(realEndpoint);
|
|
221
|
+
}
|
|
212
222
|
if (options.intercept) {
|
|
213
223
|
const config = typeof options.intercept === "object" ? { ...options.intercept, cdpEndpoint: realEndpoint } : { cdpEndpoint: realEndpoint };
|
|
214
224
|
_sharedCdpProxy = new CDPInterceptorProxy(config);
|
|
@@ -221,6 +231,7 @@ async function createBrowser(options) {
|
|
|
221
231
|
await browser2.discoverContexts().catch((err) => {
|
|
222
232
|
console.error(`[browser] discoverContexts failed: ${errMsg(err)}`);
|
|
223
233
|
});
|
|
234
|
+
if (!options.intercept) _browserPool.set(realEndpoint, browser2);
|
|
224
235
|
return browser2;
|
|
225
236
|
}
|
|
226
237
|
const executablePath = options?.executablePath || process.env.XBROWSER_CHROMIUM_PATH || discoverChromiumPath();
|
|
@@ -607,11 +618,37 @@ async function createSession(name, url, options) {
|
|
|
607
618
|
}
|
|
608
619
|
context = contexts[0] || await b.newContext();
|
|
609
620
|
let targetPage = null;
|
|
610
|
-
|
|
621
|
+
const ownedUrls = /* @__PURE__ */ new Set();
|
|
622
|
+
for (const ssn of sessions.list()) {
|
|
623
|
+
try {
|
|
624
|
+
const u = ssn.page?.url();
|
|
625
|
+
if (u && u !== "about:blank") ownedUrls.add(u);
|
|
626
|
+
} catch {
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
if (url) {
|
|
630
|
+
for (const ctx of contexts) {
|
|
631
|
+
for (const p of ctx.pages()) {
|
|
632
|
+
try {
|
|
633
|
+
const pUrl = await resolvePageUrl(p);
|
|
634
|
+
if (ownedUrls.has(pUrl)) continue;
|
|
635
|
+
if (pUrl === url) {
|
|
636
|
+
targetPage = p;
|
|
637
|
+
break;
|
|
638
|
+
}
|
|
639
|
+
} catch {
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (targetPage) break;
|
|
643
|
+
}
|
|
644
|
+
}
|
|
645
|
+
if (!targetPage && targetHostname) {
|
|
611
646
|
for (const ctx of contexts) {
|
|
612
647
|
const pages = ctx.pages();
|
|
613
648
|
for (const p of pages) {
|
|
614
|
-
const
|
|
649
|
+
const pUrl0 = await resolvePageUrl(p);
|
|
650
|
+
if (ownedUrls.has(pUrl0)) continue;
|
|
651
|
+
const pUrl = pUrl0;
|
|
615
652
|
if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
|
|
616
653
|
targetPage = p;
|
|
617
654
|
break;
|
|
@@ -624,7 +661,9 @@ async function createSession(name, url, options) {
|
|
|
624
661
|
for (const ctx of contexts) {
|
|
625
662
|
const pages = ctx.pages();
|
|
626
663
|
for (const p of pages) {
|
|
627
|
-
const
|
|
664
|
+
const pUrl0 = await resolvePageUrl(p);
|
|
665
|
+
if (ownedUrls.has(pUrl0)) continue;
|
|
666
|
+
const pUrl = pUrl0;
|
|
628
667
|
if (isRealPageUrl(pUrl)) {
|
|
629
668
|
targetPage = p;
|
|
630
669
|
break;
|
|
@@ -635,13 +674,36 @@ async function createSession(name, url, options) {
|
|
|
635
674
|
}
|
|
636
675
|
if (!targetPage && options?.cdpEndpoint) {
|
|
637
676
|
const targets = await getCDPTargets(options.cdpEndpoint);
|
|
677
|
+
const ownedUrlsT = /* @__PURE__ */ new Set();
|
|
678
|
+
for (const ssn of sessions.list()) {
|
|
679
|
+
try {
|
|
680
|
+
const u = ssn.page?.url();
|
|
681
|
+
if (u && u !== "about:blank") ownedUrlsT.add(u);
|
|
682
|
+
} catch {
|
|
683
|
+
}
|
|
684
|
+
}
|
|
638
685
|
const matchTarget = targets.find(
|
|
639
|
-
(t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
|
|
686
|
+
(t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && !ownedUrlsT.has(t.url) && (url ? t.url === url || t.url.includes(new URL(url).hostname) : true)
|
|
640
687
|
);
|
|
641
688
|
if (matchTarget && matchTarget.url) {
|
|
689
|
+
const ownedUrls2 = /* @__PURE__ */ new Set();
|
|
690
|
+
for (const ssn of sessions.list()) {
|
|
691
|
+
try {
|
|
692
|
+
const u = ssn.page?.url();
|
|
693
|
+
if (u && u !== "about:blank") ownedUrls2.add(u);
|
|
694
|
+
} catch {
|
|
695
|
+
}
|
|
696
|
+
}
|
|
697
|
+
const isOwned = (p) => {
|
|
698
|
+
try {
|
|
699
|
+
return ownedUrls2.has(p.url());
|
|
700
|
+
} catch {
|
|
701
|
+
return false;
|
|
702
|
+
}
|
|
703
|
+
};
|
|
642
704
|
const existing2 = context.pages().find((p) => {
|
|
643
705
|
try {
|
|
644
|
-
return p.url() === matchTarget.url;
|
|
706
|
+
return p.url() === matchTarget.url && !isOwned(p);
|
|
645
707
|
} catch {
|
|
646
708
|
return false;
|
|
647
709
|
}
|
|
@@ -659,7 +721,10 @@ async function createSession(name, url, options) {
|
|
|
659
721
|
const pages = context.pages();
|
|
660
722
|
if (pages.length > 1) {
|
|
661
723
|
try {
|
|
724
|
+
const owned = /* @__PURE__ */ new Set();
|
|
725
|
+
for (const ssn of sessions.list()) owned.add(ssn.page);
|
|
662
726
|
for (const p of pages) {
|
|
727
|
+
if (owned.has(p)) continue;
|
|
663
728
|
const vis = await p.evaluate("document.visibilityState").catch(() => "hidden");
|
|
664
729
|
if (vis === "visible") {
|
|
665
730
|
targetPage = p;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createRuleEngine,
|
|
3
3
|
launch
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-GKYSCHT4.js";
|
|
5
5
|
import {
|
|
6
6
|
errMsg
|
|
7
7
|
} from "./chunk-GDKLH7ZY.js";
|
|
@@ -495,7 +495,9 @@ async function resolveCDPEndpoint(raw) {
|
|
|
495
495
|
}
|
|
496
496
|
if (raw.startsWith("http://") || raw.startsWith("https://")) {
|
|
497
497
|
try {
|
|
498
|
-
const
|
|
498
|
+
const u = new URL(raw);
|
|
499
|
+
u.pathname = (u.pathname.replace(/\/+$/, "") || "") + "/json/version";
|
|
500
|
+
const httpResp = await fetchNoProxy(u.toString());
|
|
499
501
|
const data = await httpResp.json();
|
|
500
502
|
if (!data.webSocketDebuggerUrl) {
|
|
501
503
|
throw new Error(`Could not discover CDP endpoint from ${raw}`);
|
|
@@ -633,9 +635,17 @@ function discoverChromiumPath() {
|
|
|
633
635
|
}
|
|
634
636
|
return void 0;
|
|
635
637
|
}
|
|
638
|
+
var _browserPool = /* @__PURE__ */ new Map();
|
|
636
639
|
async function createBrowser(options) {
|
|
637
640
|
if (options?.cdpEndpoint) {
|
|
638
641
|
const realEndpoint = await resolveCDPEndpoint(options.cdpEndpoint);
|
|
642
|
+
if (!options.intercept) {
|
|
643
|
+
const pooled = _browserPool.get(realEndpoint);
|
|
644
|
+
if (pooled && !pooled.disconnected) {
|
|
645
|
+
return pooled;
|
|
646
|
+
}
|
|
647
|
+
if (pooled) _browserPool.delete(realEndpoint);
|
|
648
|
+
}
|
|
639
649
|
if (options.intercept) {
|
|
640
650
|
const config = typeof options.intercept === "object" ? { ...options.intercept, cdpEndpoint: realEndpoint } : { cdpEndpoint: realEndpoint };
|
|
641
651
|
_sharedCdpProxy = new CDPInterceptorProxy(config);
|
|
@@ -648,6 +658,7 @@ async function createBrowser(options) {
|
|
|
648
658
|
await browser2.discoverContexts().catch((err) => {
|
|
649
659
|
console.error(`[browser] discoverContexts failed: ${errMsg(err)}`);
|
|
650
660
|
});
|
|
661
|
+
if (!options.intercept) _browserPool.set(realEndpoint, browser2);
|
|
651
662
|
return browser2;
|
|
652
663
|
}
|
|
653
664
|
const executablePath = options?.executablePath || process.env.XBROWSER_CHROMIUM_PATH || discoverChromiumPath();
|
|
@@ -1034,11 +1045,37 @@ async function createSession(name, url, options) {
|
|
|
1034
1045
|
}
|
|
1035
1046
|
context = contexts[0] || await b.newContext();
|
|
1036
1047
|
let targetPage = null;
|
|
1037
|
-
|
|
1048
|
+
const ownedUrls = /* @__PURE__ */ new Set();
|
|
1049
|
+
for (const ssn of sessions.list()) {
|
|
1050
|
+
try {
|
|
1051
|
+
const u = ssn.page?.url();
|
|
1052
|
+
if (u && u !== "about:blank") ownedUrls.add(u);
|
|
1053
|
+
} catch {
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
if (url) {
|
|
1057
|
+
for (const ctx of contexts) {
|
|
1058
|
+
for (const p of ctx.pages()) {
|
|
1059
|
+
try {
|
|
1060
|
+
const pUrl = await resolvePageUrl(p);
|
|
1061
|
+
if (ownedUrls.has(pUrl)) continue;
|
|
1062
|
+
if (pUrl === url) {
|
|
1063
|
+
targetPage = p;
|
|
1064
|
+
break;
|
|
1065
|
+
}
|
|
1066
|
+
} catch {
|
|
1067
|
+
}
|
|
1068
|
+
}
|
|
1069
|
+
if (targetPage) break;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
if (!targetPage && targetHostname) {
|
|
1038
1073
|
for (const ctx of contexts) {
|
|
1039
1074
|
const pages = ctx.pages();
|
|
1040
1075
|
for (const p of pages) {
|
|
1041
|
-
const
|
|
1076
|
+
const pUrl0 = await resolvePageUrl(p);
|
|
1077
|
+
if (ownedUrls.has(pUrl0)) continue;
|
|
1078
|
+
const pUrl = pUrl0;
|
|
1042
1079
|
if (isRealPageUrl(pUrl) && pUrl.includes(targetHostname)) {
|
|
1043
1080
|
targetPage = p;
|
|
1044
1081
|
break;
|
|
@@ -1051,7 +1088,9 @@ async function createSession(name, url, options) {
|
|
|
1051
1088
|
for (const ctx of contexts) {
|
|
1052
1089
|
const pages = ctx.pages();
|
|
1053
1090
|
for (const p of pages) {
|
|
1054
|
-
const
|
|
1091
|
+
const pUrl0 = await resolvePageUrl(p);
|
|
1092
|
+
if (ownedUrls.has(pUrl0)) continue;
|
|
1093
|
+
const pUrl = pUrl0;
|
|
1055
1094
|
if (isRealPageUrl(pUrl)) {
|
|
1056
1095
|
targetPage = p;
|
|
1057
1096
|
break;
|
|
@@ -1062,13 +1101,36 @@ async function createSession(name, url, options) {
|
|
|
1062
1101
|
}
|
|
1063
1102
|
if (!targetPage && options?.cdpEndpoint) {
|
|
1064
1103
|
const targets = await getCDPTargets(options.cdpEndpoint);
|
|
1104
|
+
const ownedUrlsT = /* @__PURE__ */ new Set();
|
|
1105
|
+
for (const ssn of sessions.list()) {
|
|
1106
|
+
try {
|
|
1107
|
+
const u = ssn.page?.url();
|
|
1108
|
+
if (u && u !== "about:blank") ownedUrlsT.add(u);
|
|
1109
|
+
} catch {
|
|
1110
|
+
}
|
|
1111
|
+
}
|
|
1065
1112
|
const matchTarget = targets.find(
|
|
1066
|
-
(t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && (url ? t.url.includes(new URL(url).hostname) : true)
|
|
1113
|
+
(t) => t.url && t.url !== "about:blank" && !t.url.startsWith("chrome://") && !t.url.startsWith("chrome-untrusted://") && !t.url.startsWith("chrome-error://") && !ownedUrlsT.has(t.url) && (url ? t.url === url || t.url.includes(new URL(url).hostname) : true)
|
|
1067
1114
|
);
|
|
1068
1115
|
if (matchTarget && matchTarget.url) {
|
|
1116
|
+
const ownedUrls2 = /* @__PURE__ */ new Set();
|
|
1117
|
+
for (const ssn of sessions.list()) {
|
|
1118
|
+
try {
|
|
1119
|
+
const u = ssn.page?.url();
|
|
1120
|
+
if (u && u !== "about:blank") ownedUrls2.add(u);
|
|
1121
|
+
} catch {
|
|
1122
|
+
}
|
|
1123
|
+
}
|
|
1124
|
+
const isOwned = (p) => {
|
|
1125
|
+
try {
|
|
1126
|
+
return ownedUrls2.has(p.url());
|
|
1127
|
+
} catch {
|
|
1128
|
+
return false;
|
|
1129
|
+
}
|
|
1130
|
+
};
|
|
1069
1131
|
const existing2 = context.pages().find((p) => {
|
|
1070
1132
|
try {
|
|
1071
|
-
return p.url() === matchTarget.url;
|
|
1133
|
+
return p.url() === matchTarget.url && !isOwned(p);
|
|
1072
1134
|
} catch {
|
|
1073
1135
|
return false;
|
|
1074
1136
|
}
|
|
@@ -1086,7 +1148,10 @@ async function createSession(name, url, options) {
|
|
|
1086
1148
|
const pages = context.pages();
|
|
1087
1149
|
if (pages.length > 1) {
|
|
1088
1150
|
try {
|
|
1151
|
+
const owned = /* @__PURE__ */ new Set();
|
|
1152
|
+
for (const ssn of sessions.list()) owned.add(ssn.page);
|
|
1089
1153
|
for (const p of pages) {
|
|
1154
|
+
if (owned.has(p)) continue;
|
|
1090
1155
|
const vis = await p.evaluate("document.visibilityState").catch(() => "hidden");
|
|
1091
1156
|
if (vis === "visible") {
|
|
1092
1157
|
targetPage = p;
|
|
@@ -144,8 +144,17 @@ function buildStealthInitScript() {
|
|
|
144
144
|
" if(!e)return f.call(this,e);",
|
|
145
145
|
" return f.call(this,new Proxy(e,{get:function(k,p){",
|
|
146
146
|
' if(p==="sourceCapabilities")return fc;',
|
|
147
|
-
|
|
148
|
-
|
|
147
|
+
// S124 修正(d67 攻防):恒 true 是"过度伪装"——站点自己的合成事件
|
|
148
|
+
// 也变 true → 直接判定浏览器被篡改(比检测自动化更严重的指纹暴露)。
|
|
149
|
+
// 正确策略:透传原始值(CDP Input 事件本来就是 true,JS 合成本来是 false)
|
|
150
|
+
// + paste 定向伪装(合成的 paste 在真实场景都是 trusted 的键盘操作产物)
|
|
151
|
+
' if(p==="isTrusted"){',
|
|
152
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
153
|
+
" if(orig===true)return true;",
|
|
154
|
+
' if(k.type==="paste")return true;',
|
|
155
|
+
" return orig;",
|
|
156
|
+
" }",
|
|
157
|
+
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])){',
|
|
149
158
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;",
|
|
150
159
|
" return k[p]+_f;",
|
|
151
160
|
" }",
|
|
@@ -155,6 +164,30 @@ function buildStealthInitScript() {
|
|
|
155
164
|
" return o.call(this,t,w,op);",
|
|
156
165
|
" };",
|
|
157
166
|
" EventTarget.prototype.addEventListener=_ael;",
|
|
167
|
+
// window.event isTrusted 伪装(S123):AEL 参数包装收不到 window.event
|
|
168
|
+
// (它是原始 Event 对象的隐式引用)。定义 getter 拦截读取。
|
|
169
|
+
" try{",
|
|
170
|
+
' var _weDesc=Object.getOwnPropertyDescriptor(Window.prototype,"event");',
|
|
171
|
+
" if(_weDesc&&_weDesc.get){",
|
|
172
|
+
" var _origWeGet=_weDesc.get;",
|
|
173
|
+
' Object.defineProperty(Window.prototype,"event",{',
|
|
174
|
+
" configurable:true,",
|
|
175
|
+
" get:function(){",
|
|
176
|
+
" var ev=_origWeGet.call(this);",
|
|
177
|
+
" if(!ev)return ev;",
|
|
178
|
+
" return new Proxy(ev,{get:function(k,p){",
|
|
179
|
+
' if(p==="isTrusted"){',
|
|
180
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
181
|
+
" if(orig===true)return true;",
|
|
182
|
+
' if(k.type==="paste")return true;',
|
|
183
|
+
" return orig;",
|
|
184
|
+
" }",
|
|
185
|
+
' var v=Reflect.get(k,p);return typeof v==="function"?v.bind(k):v;',
|
|
186
|
+
" }});",
|
|
187
|
+
" }",
|
|
188
|
+
" });",
|
|
189
|
+
" }",
|
|
190
|
+
" }catch(e){}",
|
|
158
191
|
// 2. Screen override (prototype-level, not instance-level)
|
|
159
192
|
" var _gw=function(){return 1728};",
|
|
160
193
|
" var _gh=function(){return 1117};",
|
|
@@ -412,7 +445,12 @@ function buildStealthInitScript() {
|
|
|
412
445
|
" };",
|
|
413
446
|
// 4. onclick prototype hijack (dual-stream consistency)
|
|
414
447
|
" var _ba=function(k,p){",
|
|
415
|
-
' if(p==="isTrusted")
|
|
448
|
+
' if(p==="isTrusted"){',
|
|
449
|
+
' var orig=Reflect.get(k,"isTrusted");',
|
|
450
|
+
" if(orig===true)return true;",
|
|
451
|
+
' if(k.type==="paste")return true;',
|
|
452
|
+
" return orig;",
|
|
453
|
+
" }",
|
|
416
454
|
' if((p==="clientX"||p==="clientY")&&k.type==="click"&&Number.isInteger(k[p])&&k.isTrusted===true){',
|
|
417
455
|
" var _f=((k.timeStamp||Date.now())%89)/89*0.7+0.15;return k[p]+_f;",
|
|
418
456
|
" }",
|