@xbrowser/cli 1.19.0 → 1.21.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.
@@ -20,8 +20,8 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-C6PW3CPA.js";
24
- import "./chunk-GKYSCHT4.js";
23
+ } from "./chunk-AVSTNODR.js";
24
+ import "./chunk-CLYZPSDR.js";
25
25
  import "./chunk-3FWLW7FS.js";
26
26
  import "./chunk-3KZ34AUC.js";
27
27
  import "./chunk-GDKLH7ZY.js";
@@ -20,7 +20,7 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-KLXLNQI5.js";
23
+ } from "./chunk-ESJYANQO.js";
24
24
  import "./chunk-3KZ34AUC.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
26
  import "./chunk-KFQGP6VL.js";
@@ -20,12 +20,12 @@ import {
20
20
  saveSessionDiskMeta,
21
21
  setActivePage,
22
22
  touchSession
23
- } from "./chunk-2BPZ25RE.js";
24
- import "./chunk-LKJZYVIX.js";
23
+ } from "./chunk-Z5RTK4GW.js";
24
+ import "./chunk-HWWG2ISJ.js";
25
25
  import "./chunk-3KZ34AUC.js";
26
26
  import "./chunk-GDKLH7ZY.js";
27
- import "./chunk-A6LPGFAL.js";
28
- import "./chunk-H2A5JUK5.js";
27
+ import "./chunk-WYETQ5C7.js";
28
+ import "./chunk-75DNUI6M.js";
29
29
  import "./chunk-KFQGP6VL.js";
30
30
  export {
31
31
  closeAllSessions,
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-LKJZYVIX.js";
17
+ } from "./chunk-HWWG2ISJ.js";
18
18
  import {
19
19
  connectToCDP,
20
20
  findChrome,
@@ -23,7 +23,7 @@ import {
23
23
  launchChrome
24
24
  } from "./chunk-3KZ34AUC.js";
25
25
  import "./chunk-GDKLH7ZY.js";
26
- import "./chunk-H2A5JUK5.js";
26
+ import "./chunk-75DNUI6M.js";
27
27
  import "./chunk-KFQGP6VL.js";
28
28
  export {
29
29
  CDPConnection,
@@ -21,6 +21,25 @@ import { EventEmitter } from "events";
21
21
 
22
22
  // src/cdp-driver/stealth.ts
23
23
  var DEFAULT_STEALTH_CONFIG = {
24
+ // S191: macOS arm64 + Chrome 151 真实采样(来源:登录桥只读通道)
25
+ uaChProfile: {
26
+ brands: [
27
+ { brand: "Not=A?Brand", version: "99" },
28
+ { brand: "Google Chrome", version: "151" },
29
+ { brand: "Chromium", version: "151" }
30
+ ],
31
+ platform: "macOS",
32
+ platformVersion: "26.2.0",
33
+ architecture: "arm",
34
+ bitness: "64",
35
+ model: "",
36
+ uaFullVersion: "151.0.7922.175",
37
+ fullVersionList: [
38
+ { brand: "Not=A?Brand", version: "99.0.0.0" },
39
+ { brand: "Google Chrome", version: "151.0.7922.175" },
40
+ { brand: "Chromium", version: "151.0.7922.175" }
41
+ ]
42
+ },
24
43
  bezierCurvature: [0.35, 0.6],
25
44
  noiseAmplitude: 5.5,
26
45
  overshootRange: [6, 14],
@@ -119,10 +138,13 @@ Object.assign(KEY_MAP, {
119
138
  "@": { key: "@", code: "Digit2", vk: 50, shift: true },
120
139
  "_": { key: "_", code: "Minus", vk: 189, shift: true }
121
140
  });
122
- function buildStealthInitScript() {
141
+ function buildStealthInitScript(config = DEFAULT_STEALTH_CONFIG) {
142
+ const prof = config.uaChProfile;
143
+ const profJson = prof ? JSON.stringify(prof) : "null";
123
144
  return [
124
145
  "(function(){",
125
146
  ' window.__xbStealthVer="57";',
147
+ " window.__xbUaChProf=" + profJson + ";",
126
148
  // 1. AEL event proxy
127
149
  " var o=EventTarget.prototype.addEventListener;",
128
150
  " var fc=new InputDeviceCapabilities({firesTouchEvents:false});",
@@ -185,9 +207,65 @@ function buildStealthInitScript() {
185
207
  ' Object.defineProperty(Screen.prototype,"height",{get:_gh,configurable:true});',
186
208
  ' Object.defineProperty(Screen.prototype,"availWidth",{get:_gw,configurable:true});',
187
209
  ' Object.defineProperty(Screen.prototype,"availHeight",{get:_gah,configurable:true});',
210
+ // S174: UA Headless 标记清洗——headless launch 的 UA 含 HeadlessChrome/xxx
211
+ // (最高频检测面),同步清洗 userAgent/appVersion/userAgentData(brands+高熵值)。
212
+ // 原型+实例双层覆写(S172 模式)。
213
+ " try{",
214
+ ' var fixUA=function(ua){return String(ua).replace("HeadlessChrome","Chrome");};',
215
+ ' var fixBrands=function(list){return (list||[]).map(function(b){return (b.brand&&b.brand.indexOf("Headless")!==-1)?Object.assign({},b,{brand:b.brand.replace("HeadlessChrome","Chrome")}):b;});};',
216
+ ' var _nuad=Object.getOwnPropertyDescriptor(Navigator.prototype,"userAgentData");',
217
+ " var wrapUAD=function(uad){",
218
+ " if(!uad||uad.__xbUA)return uad;",
219
+ " try{",
220
+ ' Object.defineProperty(uad,"brands",{get:function(){return fixBrands(_nuad.get.call(navigator).brands);},configurable:true});',
221
+ " var _ghev=uad.getHighEntropyValues.bind(uad);",
222
+ " uad.getHighEntropyValues=function(hints){return _ghev(hints).then(function(v){",
223
+ ' var o={};for(var k in v){o[k]=(typeof v[k]==="string")?fixUA(v[k]):(Array.isArray(v[k])?fixBrands(v[k]):v[k]);}',
224
+ " return o;});};",
225
+ " uad.__xbUA=true;",
226
+ " }catch(e){}",
227
+ " return uad;",
228
+ " };",
229
+ ' var _nua=Object.getOwnPropertyDescriptor(Navigator.prototype,"userAgent");',
230
+ " if(_nua&&_nua.get){",
231
+ " var _nuag=function(){return fixUA(_nua.get.call(this));};",
232
+ ' _nuag.toString=function(){return "function get userAgent() { [native code] }";};',
233
+ ' Object.defineProperty(Navigator.prototype,"userAgent",{get:_nuag,configurable:true});',
234
+ " }",
235
+ ' Object.defineProperty(navigator,"userAgent",{get:function(){return fixUA(_nua?_nua.get.call(this):"");},configurable:true});',
236
+ " if(_nuad&&_nuad.get){",
237
+ " var _nuadg=function(){return wrapUAD(_nuad.get.call(this));};",
238
+ ' _nuadg.toString=function(){return "function get userAgentData() { [native code] }";};',
239
+ ' Object.defineProperty(Navigator.prototype,"userAgentData",{get:_nuadg,configurable:true});',
240
+ " }",
241
+ // S190: userAgentData 垫片——headless Chrome 151 不暴露 UA-CH API,
242
+ // 缺失本身即 headless 指纹。从 UA 派生 brands/platform 垫片 + getHighEntropyValues。
243
+ " if(!navigator.userAgentData && window.__xbUaChProf){",
244
+ " var prof = window.__xbUaChProf;",
245
+ " var uad={",
246
+ ' brands:prof.brands.map(function(b){return {brand:b.brand,version:b.version.split(".")[0]}}),',
247
+ " mobile:false,",
248
+ " platform:prof.platform,",
249
+ " getHighEntropyValues:function(hints){",
250
+ " return Promise.resolve().then(function(){",
251
+ " var all={brands:uad.brands,mobile:false,platform:prof.platform,",
252
+ " platformVersion:prof.platformVersion,architecture:prof.architecture,bitness:prof.bitness,model:prof.model,",
253
+ " uaFullVersion:prof.uaFullVersion,",
254
+ " fullVersionList:prof.fullVersionList};",
255
+ " var out={};",
256
+ " (hints||[]).forEach(function(h){if(h in all)out[h]=all[h]});",
257
+ " return out;",
258
+ " });",
259
+ " },",
260
+ " toJSON:function(){return{brands:uad.brands,mobile:false,platform:prof.platform}}",
261
+ " };",
262
+ ' Object.defineProperty(navigator,"userAgentData",{get:function(){return uad},configurable:true});',
263
+ " }",
264
+ " }catch(e){}",
188
265
  // hasFocus 与 visibilityState 联动(d29):恒 true 在 hidden 标签下暴露
189
266
  // —— 真实浏览器失焦/后台时 hasFocus=false
190
- ' document.hasFocus=function(){return document.visibilityState==="visible";};',
267
+ // S172: 原型层覆写——实例赋值可被 Document.prototype.hasFocus.call(document) 逃逸
268
+ ' Document.prototype.hasFocus=function(){return document.visibilityState==="visible";};',
191
269
  // 4. Canvas/WebGL fingerprint: per-session stable noise (d20).
192
270
  // Headless software raster differs subtly from Chrome GPU raster —
193
271
  // toDataURL hashes fingerprint the rasterizer. Inject a stable
@@ -250,26 +328,25 @@ function buildStealthInitScript() {
250
328
  " }catch(e){}",
251
329
  // 3b. Font metrics + speechSynthesis + battery (d22): headless tells
252
330
  " try{",
253
- " var _mt=CanvasRenderingContext2D.prototype.measureText;",
254
331
  ' var _tmw=Object.getOwnPropertyDescriptor(TextMetrics.prototype,"width");',
255
- " CanvasRenderingContext2D.prototype.measureText=function(t){",
256
- " var m=_mt.call(this,t);",
257
- ' try{Object.defineProperty(m,"width",{get:function(){return _tmw.get.call(m)+_dx*0.01;},configurable:true});}catch(e){}',
258
- " return m;",
259
- " };",
332
+ " if(_tmw&&_tmw.get){",
333
+ " // S172: \u539F\u578B\u5C42\u8986\u5199\u2014\u2014\u5B9E\u4F8B(m)\u8986\u5199\u53EF\u88AB TextMetrics.prototype.width getter \u9003\u9038",
334
+ ' Object.defineProperty(TextMetrics.prototype,"width",{get:function(){return _tmw.get.call(this)+_dx*0.01;},configurable:true});',
335
+ " }",
260
336
  " }catch(e){}",
261
337
  " try{",
262
338
  ' var _fakeVoices=["Alex","Daniel","Karen","Moira","Ralph","Samantha","Ting-Ting","Mei-Jia","Sinji","Yunda"];',
263
- " var _sv=speechSynthesis.getVoices.bind(speechSynthesis);",
264
- " speechSynthesis.getVoices=function(){",
265
- " var real=_sv();",
339
+ // S172: 原型层覆写——实例赋值可被 SpeechSynthesis.prototype.getVoices.call() 逃逸
340
+ " var _sv=SpeechSynthesis.prototype.getVoices;",
341
+ " SpeechSynthesis.prototype.getVoices=function(){",
342
+ " var real=_sv.call(this);",
266
343
  " if(real&&real.length>50)return real;",
267
344
  // 注意阈值从 0 改 50:iframe 原生约 10 个 —— 原阈值下 iframe 返回原生 10 个
268
345
  // 而不是伪装 180(d33 暴露)。>50 = 主文档伪装列表已生效,其余返回伪装。
269
346
  ' return _fakeVoices.map(function(n,i){return {name:n,lang:i<2?"en-US":i<6?"en-GB":"zh-TW",localService:true,default:i===0,voiceURI:n};});',
270
347
  " };",
271
348
  // d33 修复:iframe 的 speechSynthesis 是独立实例(主文档 hook 不可达)——
272
- // 轮询同源 iframe,对其 contentWindow.speechSynthesis 同样 patch
349
+ // 轮询同源 iframe,对其 contentWindow 的原型同样 patch(S172: 原型层,防逃逸)
273
350
  " try{",
274
351
  " var _piv=function(){",
275
352
  ' var frs=document.querySelectorAll("iframe");',
@@ -277,8 +354,8 @@ function buildStealthInitScript() {
277
354
  " var cw=frs[i].contentWindow;",
278
355
  " if(!cw||!cw.speechSynthesis||cw.speechSynthesis.__xbP)continue;",
279
356
  " cw.speechSynthesis.__xbP=true;",
280
- " var _sv2=cw.speechSynthesis.getVoices.bind(cw.speechSynthesis);",
281
- " cw.speechSynthesis.getVoices=function(){var r=_sv2();if(r&&r.length>50)return r;",
357
+ " var _sv2=cw.SpeechSynthesis.prototype.getVoices;",
358
+ " cw.SpeechSynthesis.prototype.getVoices=function(){var r=_sv2.call(this);if(r&&r.length>50)return r;",
282
359
  ' return _fakeVoices.map(function(n,j){return{name:n,lang:j<2?"en-US":j<6?"en-GB":"zh-TW",localService:true,default:j===0,voiceURI:n};});};',
283
360
  " }catch(e2){}}",
284
361
  " };",
@@ -313,10 +390,21 @@ function buildStealthInitScript() {
313
390
  // 3d-2. performance.now precision clamp (d34): full-precision timestamps
314
391
  // (11 decimals) differ from site-isolation-clamped real Chrome (~100μs).
315
392
  " try{",
316
- " var _pn=performance.now.bind(performance);",
317
- " performance.now=function(){return Math.round(_pn()*10)/10;};",
393
+ " var _pn=Performance.prototype.now;",
394
+ " // S172: \u539F\u578B\u5C42\u8986\u5199\u2014\u2014\u5B9E\u4F8B\u8D4B\u503C\u53EF\u88AB Performance.prototype.now.call(performance) \u9003\u9038",
395
+ " Performance.prototype.now=function(){return Math.round(_pn.call(this)*10)/10;};",
318
396
  ' var _pto=Object.getOwnPropertyDescriptor(Performance.prototype,"timeOrigin");',
319
- ' if(_pto&&_pto.get){Object.defineProperty(performance,"timeOrigin",{get:function(){return _pto.get.call(performance);},configurable:true});}',
397
+ " if(_pto&&_pto.get){",
398
+ " // S173: timeOrigin \u771F\u4F2A\u88C5\u2014\u2014\u5360\u4F4D\u5C42\uFF08\u8FD4\u56DE\u771F\u503C\uFF09\u5347\u7EA7\u4E3A\u4F1A\u8BDD\u7EA7\u968F\u673A\u504F\u79FB\u3002",
399
+ " // \u9632 cross-page \u5BFC\u822A\u65F6\u523B\u5173\u8054\uFF1A\u4E0D\u540C\u7AD9\u70B9\u7528 timeOrigin \u7CBE\u786E\u5BF9\u9F50\u7528\u6237\u884C\u4E3A\u6863\u6848\u3002",
400
+ " // \u504F\u79FB\u7531\u672C\u9875 _seed \u6D3E\u751F\uFF08\u9875\u5185\u81EA\u6D3D\uFF0Cnow()+\u504F\u79FB\u4E00\u81F4\uFF09\uFF0C\u8DE8\u9875\u968F\u673A\uFF08\u4E0D\u53EF\u5173\u8054\uFF09\u3002",
401
+ " var _toff=Math.floor((_seed/2147483647)*600000-300000);",
402
+ " var _tog=function(){return _pto.get.call(performance)+_toff;};",
403
+ ' _tog.toString=function(){return "function get timeOrigin() { [native code] }";};',
404
+ " // S172 \u5BA1\u8BA1\uFF1A\u5B9E\u4F8B\u8986\u5199\u53EF\u88AB\u539F\u578B getter \u9003\u9038\u2014\u2014\u539F\u578B\u5C42\u8986\u5199",
405
+ ' Object.defineProperty(Performance.prototype,"timeOrigin",{get:_tog,configurable:true});',
406
+ ' Object.defineProperty(performance,"timeOrigin",{get:_tog,configurable:true});',
407
+ " }",
320
408
  " }catch(e){}",
321
409
  // 3f. getCoalescedEvents 合成(d47):真实鼠标 125Hz 采样被浏览器按帧合并,
322
410
  // 快速移动时 pointermove.getCoalescedEvents() 返回 2~6 个事件(群内
@@ -430,6 +518,9 @@ function buildStealthInitScript() {
430
518
  ' if(this===AnalyserNode.prototype.getFloatFrequencyData)return"function getFloatFrequencyData() { [native code] }";',
431
519
  ' if(this===AudioBuffer.prototype.getChannelData)return"function getChannelData() { [native code] }";',
432
520
  ' if(this===_gceH)return"function getCoalescedEvents() { [native code] }";',
521
+ ' if(this===Document.prototype.hasFocus)return"function hasFocus() { [native code] }";',
522
+ ' if(this===Performance.prototype.now)return"function now() { [native code] }";',
523
+ ' if(this===SpeechSynthesis.prototype.getVoices)return"function getVoices() { [native code] }";',
433
524
  " return _ts.call(this);",
434
525
  " };",
435
526
  // 4. onclick prototype hijack (dual-stream consistency)
@@ -1984,7 +2075,7 @@ var XBPageImpl = class _XBPageImpl {
1984
2075
  try {
1985
2076
  await this.conn.send(
1986
2077
  "Page.addScriptToEvaluateOnNewDocument",
1987
- { source: buildStealthInitScript() },
2078
+ { source: buildStealthInitScript({ ...DEFAULT_STEALTH_CONFIG, ...this._contextImpl.stealthConfig ?? {} }) },
1988
2079
  this.sessionId
1989
2080
  );
1990
2081
  } catch {
@@ -3241,6 +3332,10 @@ var XBContextImpl = class {
3241
3332
  this.options = opts;
3242
3333
  this.setupAutoAttach();
3243
3334
  }
3335
+ /** S194: 任务 tab 的 stealth 配置(UA-CH 档案等)经 context 传给 page 层 */
3336
+ get stealthConfig() {
3337
+ return this.options.stealthConfig;
3338
+ }
3244
3339
  async newPage() {
3245
3340
  if (this.closed) throw new Error("Context is closed");
3246
3341
  const { targetId } = await this._browser._createTarget(this.contextId);
@@ -3407,20 +3502,8 @@ var XBContextImpl = class {
3407
3502
 
3408
3503
  // src/cdp-driver/browser.ts
3409
3504
  var XBBrowserImpl = class {
3410
- conn;
3411
- _emitter = new EventEmitter3();
3412
- _contexts = /* @__PURE__ */ new Map();
3413
- _disconnected = false;
3414
- childProcess = null;
3415
- tmpDir;
3416
- _exitHandler = null;
3417
- /**
3418
- * Original CDP endpoint (HTTP or ws URL) used to construct this browser.
3419
- * Used by discoverContexts() as a fallback to HTTP /json/list when
3420
- * Target.getTargets doesn't return page-type targets (e.g. cdp-tunnel proxy).
3421
- */
3422
- cdpEndpoint;
3423
- constructor(conn, childProcess, tmpDir, cdpEndpoint) {
3505
+ constructor(conn, childProcess, tmpDir, cdpEndpoint, launchOpts) {
3506
+ this.launchOpts = launchOpts;
3424
3507
  this.conn = conn;
3425
3508
  this.childProcess = childProcess ?? null;
3426
3509
  this.tmpDir = tmpDir;
@@ -3448,6 +3531,20 @@ var XBBrowserImpl = class {
3448
3531
  process.on("exit", this._exitHandler);
3449
3532
  }
3450
3533
  }
3534
+ launchOpts;
3535
+ conn;
3536
+ _emitter = new EventEmitter3();
3537
+ _contexts = /* @__PURE__ */ new Map();
3538
+ _disconnected = false;
3539
+ childProcess = null;
3540
+ tmpDir;
3541
+ _exitHandler = null;
3542
+ /**
3543
+ * Original CDP endpoint (HTTP or ws URL) used to construct this browser.
3544
+ * Used by discoverContexts() as a fallback to HTTP /json/list when
3545
+ * Target.getTargets doesn't return page-type targets (e.g. cdp-tunnel proxy).
3546
+ */
3547
+ cdpEndpoint;
3451
3548
  get disconnected() {
3452
3549
  return this._disconnected;
3453
3550
  }
@@ -3475,6 +3572,9 @@ var XBBrowserImpl = class {
3475
3572
  this._emitter.emit("disconnected");
3476
3573
  }
3477
3574
  async newContext(opts = {}) {
3575
+ if (this.launchOpts?.stealthConfig && !opts.stealthConfig) {
3576
+ opts = { ...opts, stealthConfig: this.launchOpts.stealthConfig };
3577
+ }
3478
3578
  if (this._disconnected) {
3479
3579
  throw new Error("Browser is disconnected");
3480
3580
  }
@@ -3701,6 +3801,9 @@ function extractAllStrings(rawArgs) {
3701
3801
  }
3702
3802
  return strings.length > 0 ? strings.join("\n") : null;
3703
3803
  }
3804
+ function isProbeMarked(code) {
3805
+ return code.includes("/* @xb-probe */");
3806
+ }
3704
3807
 
3705
3808
  // src/cdp-interceptor/rules/dom-mutation.ts
3706
3809
  var DOM_PATTERNS = [
@@ -4263,6 +4366,7 @@ var eventSimulationRule = {
4263
4366
  evaluate(ctx) {
4264
4367
  const userCode = extractUserCode(ctx);
4265
4368
  if (!userCode) return null;
4369
+ if (isProbeMarked(userCode)) return null;
4266
4370
  for (const p of EVENT_PATTERNS) {
4267
4371
  if (p.pattern.test(userCode)) {
4268
4372
  return {
@@ -4590,6 +4694,11 @@ function createRuleEngine(customRules) {
4590
4694
  ...ctx,
4591
4695
  sessionState: getSessionState(ctx.sessionId)
4592
4696
  };
4697
+ try {
4698
+ const code = typeof ctx.params?.expression === "string" ? ctx.params.expression : JSON.stringify(ctx.params ?? "");
4699
+ if (isProbeMarked(code)) return null;
4700
+ } catch {
4701
+ }
4593
4702
  for (const rule of rules) {
4594
4703
  if (rule.canHandle && !rule.canHandle(fullCtx)) continue;
4595
4704
  const decision = rule.evaluate(fullCtx);
@@ -4971,7 +5080,13 @@ async function launch(options = {}) {
4971
5080
  const conn = new CDPConnection(wsEndpoint);
4972
5081
  await conn.ready();
4973
5082
  const httpEndpoint = options.cdpEndpoint && !options.cdpEndpoint.startsWith("ws") ? options.cdpEndpoint : void 0;
4974
- const browser = new XBBrowserImpl(conn, childProcess, tmpDir, httpEndpoint);
5083
+ const browser = new XBBrowserImpl(
5084
+ conn,
5085
+ childProcess,
5086
+ tmpDir,
5087
+ httpEndpoint,
5088
+ options.stealthConfig ? { stealthConfig: options.stealthConfig } : void 0
5089
+ );
4975
5090
  return { browser, wsEndpoint };
4976
5091
  }
4977
5092
  export {
@@ -14,7 +14,7 @@ import {
14
14
  scrollIntoView,
15
15
  waitForActionable,
16
16
  waitForNetworkIdle
17
- } from "./chunk-GKYSCHT4.js";
17
+ } from "./chunk-CLYZPSDR.js";
18
18
  import "./chunk-3FWLW7FS.js";
19
19
  import {
20
20
  connectToCDP,
@@ -39,6 +39,9 @@ function extractAllStrings(rawArgs) {
39
39
  }
40
40
  return strings.length > 0 ? strings.join("\n") : null;
41
41
  }
42
+ function isProbeMarked(code) {
43
+ return code.includes("/* @xb-probe */");
44
+ }
42
45
 
43
46
  // src/cdp-interceptor/rules/dom-mutation.ts
44
47
  var DOM_PATTERNS = [
@@ -601,6 +604,7 @@ var eventSimulationRule = {
601
604
  evaluate(ctx) {
602
605
  const userCode = extractUserCode(ctx);
603
606
  if (!userCode) return null;
607
+ if (isProbeMarked(userCode)) return null;
604
608
  for (const p of EVENT_PATTERNS) {
605
609
  if (p.pattern.test(userCode)) {
606
610
  return {
@@ -928,6 +932,11 @@ function createRuleEngine(customRules) {
928
932
  ...ctx,
929
933
  sessionState: getSessionState(ctx.sessionId)
930
934
  };
935
+ try {
936
+ const code = typeof ctx.params?.expression === "string" ? ctx.params.expression : JSON.stringify(ctx.params ?? "");
937
+ if (isProbeMarked(code)) return null;
938
+ } catch {
939
+ }
931
940
  for (const rule of rules) {
932
941
  if (rule.canHandle && !rule.canHandle(fullCtx)) continue;
933
942
  const decision = rule.evaluate(fullCtx);
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  createRuleEngine,
3
3
  launch
4
- } from "./chunk-GKYSCHT4.js";
4
+ } from "./chunk-CLYZPSDR.js";
5
5
  import {
6
6
  errMsg
7
7
  } from "./chunk-GDKLH7ZY.js";