siarashield_workspace 0.0.33 → 0.0.36

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.
@@ -112,6 +112,133 @@ function installVendorRuntimeErrorSuppression() {
112
112
  patchState.installed = true;
113
113
  }
114
114
 
115
+ const SUBMIT_GUARD_STATE_KEY = '__siaraShieldSubmitGuardInstalled__';
116
+ const SYNTHETIC_CLICK_GUARD_WINDOW_MS = 1200;
117
+ function installCaptchaSubmitGuard() {
118
+ const globalState = globalThis;
119
+ if (globalState[SUBMIT_GUARD_STATE_KEY])
120
+ return;
121
+ const lastSyntheticClickByButton = new WeakMap();
122
+ document.addEventListener('click', (event) => {
123
+ const target = event.target;
124
+ if (!(target instanceof Element))
125
+ return;
126
+ const submitButton = target.closest('.CaptchaSubmit');
127
+ if (!submitButton)
128
+ return;
129
+ // Vendor runtime can emit synthetic clicks on .CaptchaSubmit.
130
+ // Allow one synthetic click (needed for vendor auto-submit) but block rapid duplicates.
131
+ if (!event.isTrusted) {
132
+ const now = Date.now();
133
+ const lastSyntheticClickAt = lastSyntheticClickByButton.get(submitButton) ?? 0;
134
+ if (now - lastSyntheticClickAt < SYNTHETIC_CLICK_GUARD_WINDOW_MS) {
135
+ event.preventDefault();
136
+ event.stopImmediatePropagation();
137
+ return;
138
+ }
139
+ lastSyntheticClickByButton.set(submitButton, now);
140
+ }
141
+ }, true);
142
+ globalState[SUBMIT_GUARD_STATE_KEY] = true;
143
+ }
144
+
145
+ const VENDOR_RUNTIME_PATCH_KEY = '__siaraShieldVendorRuntimePatchInstalled__';
146
+ const VENDOR_TOKEN_BRIDGE_KEY = '__siaraShieldVendorTokenBridgeInstalled__';
147
+ const WRAPPED_FN_PREFIX = '__siaraShieldWrappedVendorFn__';
148
+ function isKnownVendorNullDomError(error) {
149
+ const msg = error instanceof Error ? error.message.toLowerCase() : String(error ?? '').toLowerCase();
150
+ return (msg.includes("cannot read properties of null (reading 'style')") ||
151
+ msg.includes("cannot read properties of null (reading 'queryselector')") ||
152
+ msg.includes("cannot read properties of null (reading 'removechild')"));
153
+ }
154
+ function runFallbackChallengeOpen(excluding) {
155
+ const globals = getSiaraShieldGlobals();
156
+ const candidates = ['OpenCaptchaSlid', 'GetCyberSiara', 'Open_Pluin', 'Open_Plugin', 'Open_Plugin_'];
157
+ for (const name of candidates) {
158
+ if (name === excluding)
159
+ continue;
160
+ const fn = globals[name];
161
+ if (typeof fn !== 'function')
162
+ continue;
163
+ try {
164
+ fn();
165
+ return;
166
+ }
167
+ catch {
168
+ // Try next candidate.
169
+ }
170
+ }
171
+ }
172
+ function wrapVendorOpenFunction(name) {
173
+ const globals = getSiaraShieldGlobals();
174
+ const marker = `${WRAPPED_FN_PREFIX}${name}`;
175
+ if (globals[marker] === true)
176
+ return;
177
+ const candidate = globals[name];
178
+ if (typeof candidate !== 'function')
179
+ return;
180
+ const original = candidate;
181
+ globals[name] = ((...args) => {
182
+ try {
183
+ return original(...args);
184
+ }
185
+ catch (error) {
186
+ if (!isKnownVendorNullDomError(error)) {
187
+ throw error;
188
+ }
189
+ // Vendor challenge opener hit a missing DOM node.
190
+ // Try alternative open functions so challenge can still render.
191
+ runFallbackChallengeOpen(name);
192
+ return undefined;
193
+ }
194
+ });
195
+ globals[marker] = true;
196
+ }
197
+ function installVerifiedSubmitTokenBridge() {
198
+ const globals = getSiaraShieldGlobals();
199
+ if (globals[VENDOR_TOKEN_BRIDGE_KEY])
200
+ return;
201
+ const patchAjaxOwner = (owner) => {
202
+ if (!owner || typeof owner.ajax !== 'function')
203
+ return;
204
+ const originalAjax = owner.ajax.bind(owner);
205
+ owner.ajax = ((...args) => {
206
+ const firstArg = args[0];
207
+ if (!firstArg || typeof firstArg !== 'object') {
208
+ return originalAjax(...args);
209
+ }
210
+ const options = firstArg;
211
+ const url = String(options['url'] ?? '').toLowerCase();
212
+ const originalSuccess = options['success'];
213
+ if (!url.includes('/submitcaptcha/verifiedsubmit') || typeof originalSuccess !== 'function') {
214
+ return originalAjax(options);
215
+ }
216
+ options['success'] = ((response, ...rest) => {
217
+ const payload = response;
218
+ const maybeTokenRaw = payload?.data ?? payload?.Data;
219
+ if (typeof maybeTokenRaw === 'string' && maybeTokenRaw.trim()) {
220
+ globals.CyberSiaraToken = maybeTokenRaw.trim();
221
+ }
222
+ return originalSuccess(response, ...rest);
223
+ });
224
+ return originalAjax(options);
225
+ });
226
+ };
227
+ patchAjaxOwner(globals.$);
228
+ patchAjaxOwner(globals.JQuryName);
229
+ globals[VENDOR_TOKEN_BRIDGE_KEY] = true;
230
+ }
231
+ function installVendorChallengeRuntimePatch() {
232
+ const globals = getSiaraShieldGlobals();
233
+ if (globals[VENDOR_RUNTIME_PATCH_KEY])
234
+ return;
235
+ wrapVendorOpenFunction('Open_Pluin');
236
+ wrapVendorOpenFunction('Open_Plugin');
237
+ wrapVendorOpenFunction('Open_Plugin_');
238
+ installVerifiedSubmitTokenBridge();
239
+ globals[VENDOR_RUNTIME_PATCH_KEY] = true;
240
+ }
241
+
115
242
  const SCRIPT_STATUS_BY_DOCUMENT = new WeakMap();
116
243
  const SCRIPT_PENDING_BY_DOCUMENT = new WeakMap();
117
244
  const DYNAMIC_SCRIPT_NONCE_STATE_KEY = '__siaraShieldDynamicScriptNonceState__';
@@ -299,7 +426,8 @@ function prepareScriptNonce(documentRef, explicitNonce) {
299
426
  return resolvedNonce;
300
427
  }
301
428
  function loadScript(documentRef, src, options) {
302
- const nonce = prepareScriptNonce(documentRef, options?.nonce);
429
+ // CSP nonce propagation is intentionally disabled in plugin runtime path for compatibility testing.
430
+ const nonce = normalizeNonce(options?.nonce);
303
431
  const statusBySrc = getStatusBySrc(documentRef);
304
432
  const pendingBySrc = getPendingBySrc(documentRef);
305
433
  const existing = documentRef.querySelector(`script[src="${src}"]`);
@@ -337,36 +465,6 @@ function loadScript(documentRef, src, options) {
337
465
  return pending;
338
466
  }
339
467
 
340
- const SUBMIT_GUARD_STATE_KEY = '__siaraShieldSubmitGuardInstalled__';
341
- const SYNTHETIC_CLICK_GUARD_WINDOW_MS = 1200;
342
- function installCaptchaSubmitGuard() {
343
- const globalState = globalThis;
344
- if (globalState[SUBMIT_GUARD_STATE_KEY])
345
- return;
346
- const lastSyntheticClickByButton = new WeakMap();
347
- document.addEventListener('click', (event) => {
348
- const target = event.target;
349
- if (!(target instanceof Element))
350
- return;
351
- const submitButton = target.closest('.CaptchaSubmit');
352
- if (!submitButton)
353
- return;
354
- // Vendor runtime can emit synthetic clicks on .CaptchaSubmit.
355
- // Allow one synthetic click (needed for vendor auto-submit) but block rapid duplicates.
356
- if (!event.isTrusted) {
357
- const now = Date.now();
358
- const lastSyntheticClickAt = lastSyntheticClickByButton.get(submitButton) ?? 0;
359
- if (now - lastSyntheticClickAt < SYNTHETIC_CLICK_GUARD_WINDOW_MS) {
360
- event.preventDefault();
361
- event.stopImmediatePropagation();
362
- return;
363
- }
364
- lastSyntheticClickByButton.set(submitButton, now);
365
- }
366
- }, true);
367
- globalState[SUBMIT_GUARD_STATE_KEY] = true;
368
- }
369
-
370
468
  class SiaraShieldLoaderService {
371
469
  document;
372
470
  constructor(document) {
@@ -447,22 +545,18 @@ class SiaraShieldComponent {
447
545
  if (!options.publicKey) {
448
546
  throw new Error('SiaraShieldComponent: publicKey is required.');
449
547
  }
450
- const cspNonce = prepareScriptNonce(this.host.nativeElement.ownerDocument, options.cspNonce);
451
548
  if ((options.loadJQuery ?? true) && !this.isJQueryAlreadyAvailable()) {
452
- await this.loader.loadScript(JQUERY_FALLBACK_SRC$1, { nonce: cspNonce });
549
+ await this.loader.loadScript(JQUERY_FALLBACK_SRC$1);
453
550
  }
454
- await this.loader.loadScript(CAPTCHA_SCRIPT_SRC$1, {
455
- nonce: cspNonce,
456
- });
457
- await this.loader.loadScript(VALIDATION_SCRIPT_SRC$1, {
458
- nonce: cspNonce,
459
- });
551
+ await this.loader.loadScript(CAPTCHA_SCRIPT_SRC$1);
552
+ await this.loader.loadScript(VALIDATION_SCRIPT_SRC$1);
460
553
  const g = getSiaraShieldGlobals();
461
554
  ensureAccessibilityPopupAliases$1();
462
555
  this.preventDuplicateValidationBootstrap(g);
556
+ installVendorChallengeRuntimePatch();
463
557
  const initCaptchaFn = getInitCaptchaFn(g);
464
558
  if (!initCaptchaFn) {
465
- throw new Error('SiaraShield: InitCaptcha() is not available after loading scripts. Check whether CSP blocked vendor scripts or inline execution.');
559
+ throw new Error('SiaraShield: InitCaptcha() is not available after loading scripts.');
466
560
  }
467
561
  if (!options.allowVendorConsoleLogs) {
468
562
  suppressVendorConsoleWindow();
@@ -504,7 +598,7 @@ class SiaraShieldComponent {
504
598
  }
505
599
  await new Promise((resolve) => setTimeout(resolve, 100));
506
600
  }
507
- throw new Error('SiaraShield: CheckCaptcha() was not available within timeout. This can happen when CSP blocks the captcha runtime.');
601
+ throw new Error('SiaraShield: CheckCaptcha() was not available within timeout.');
508
602
  }
509
603
  /**
510
604
  * Calls the global `CheckCaptcha()` from SiaraShield script.
@@ -513,7 +607,7 @@ class SiaraShieldComponent {
513
607
  checkCaptcha() {
514
608
  const g = getSiaraShieldGlobals();
515
609
  if (!g.CheckCaptcha) {
516
- throw new Error('SiaraShield: CheckCaptcha() is not available. Did init() run, and is CSP allowing the captcha scripts?');
610
+ throw new Error('SiaraShield: CheckCaptcha() is not available. Did init() run?');
517
611
  }
518
612
  const existingToken = typeof g.CyberSiaraToken === 'string' && g.CyberSiaraToken.length > 0 ? g.CyberSiaraToken : undefined;
519
613
  if (existingToken) {
@@ -537,15 +631,33 @@ class SiaraShieldComponent {
537
631
  const timeoutMs = options?.timeoutMs ?? 2000;
538
632
  const pollIntervalMs = options?.pollIntervalMs ?? 120;
539
633
  const beforeCheckDelayMs = options?.beforeCheckDelayMs ?? 140;
634
+ const retryOnFalseMs = options?.retryOnFalseMs ?? 0;
635
+ const falseResultTokenWaitMs = options?.falseResultTokenWaitMs ?? 900;
540
636
  const existingToken = getSiaraShieldGlobals().CyberSiaraToken;
541
637
  if (typeof existingToken === 'string' && existingToken.length > 0) {
542
638
  this.token.emit(existingToken);
543
639
  return true;
544
640
  }
545
641
  await new Promise((resolve) => setTimeout(resolve, beforeCheckDelayMs));
546
- const ok = this.checkCaptcha();
547
- if (!ok)
642
+ let ok = this.checkCaptcha();
643
+ // Optional retry for integrations that observe late vendor-state settling.
644
+ if (!ok && retryOnFalseMs > 0) {
645
+ await new Promise((resolve) => setTimeout(resolve, retryOnFalseMs));
646
+ ok = this.checkCaptcha();
647
+ }
648
+ if (!ok) {
649
+ // Some vendor flows return false first, then set token shortly after.
650
+ const falseStartedAt = Date.now();
651
+ while (Date.now() - falseStartedAt < falseResultTokenWaitMs) {
652
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
653
+ const token = getSiaraShieldGlobals().CyberSiaraToken;
654
+ if (typeof token === 'string' && token.length > 0) {
655
+ this.token.emit(token);
656
+ return true;
657
+ }
658
+ }
548
659
  return false;
660
+ }
549
661
  const g = getSiaraShieldGlobals();
550
662
  if (typeof g.CyberSiaraToken === 'string' && g.CyberSiaraToken.length > 0) {
551
663
  return true;
@@ -644,7 +756,7 @@ async function waitForCheckCaptchaApi(timeoutMs = CAPTCHA_READY_TIMEOUT_MS) {
644
756
  }
645
757
  await new Promise((resolve) => setTimeout(resolve, 100));
646
758
  }
647
- throw new Error('SiaraShield: CheckCaptcha() was not available within timeout. This can happen when CSP blocks the captcha runtime.');
759
+ throw new Error('SiaraShield: CheckCaptcha() was not available within timeout.');
648
760
  }
649
761
  /**
650
762
  * Drop-in initializer for SiaraShield.
@@ -664,22 +776,18 @@ async function initSiaraShield(options) {
664
776
  }
665
777
  pending = (async () => {
666
778
  installCaptchaSubmitGuard();
667
- const cspNonce = prepareScriptNonce(document, options.cspNonce);
668
779
  if ((options.loadJQuery ?? true) && !isJQueryAlreadyAvailable()) {
669
- await loadScript(document, JQUERY_FALLBACK_SRC, { nonce: cspNonce });
780
+ await loadScript(document, JQUERY_FALLBACK_SRC);
670
781
  }
671
- await loadScript(document, CAPTCHA_SCRIPT_SRC, {
672
- nonce: cspNonce,
673
- });
674
- await loadScript(document, VALIDATION_SCRIPT_SRC, {
675
- nonce: cspNonce,
676
- });
782
+ await loadScript(document, CAPTCHA_SCRIPT_SRC);
783
+ await loadScript(document, VALIDATION_SCRIPT_SRC);
677
784
  const g = getSiaraShieldGlobals();
678
785
  ensureAccessibilityPopupAliases();
679
786
  preventDuplicateValidationBootstrap(g);
787
+ installVendorChallengeRuntimePatch();
680
788
  const initCaptchaFn = getInitCaptchaFn(g);
681
789
  if (!initCaptchaFn) {
682
- throw new Error('SiaraShield: InitCaptcha() is not available after loading scripts. Check whether CSP blocked vendor scripts or inline execution.');
790
+ throw new Error('SiaraShield: InitCaptcha() is not available after loading scripts.');
683
791
  }
684
792
  if (!options.allowVendorConsoleLogs) {
685
793
  suppressVendorConsoleWindow();
@@ -703,7 +811,7 @@ async function initSiaraShield(options) {
703
811
  function checkSiaraShieldCaptcha(options) {
704
812
  const g = getSiaraShieldGlobals();
705
813
  if (!g.CheckCaptcha) {
706
- throw new Error('SiaraShield: CheckCaptcha() is not available. Did initSiaraShield() run, and is CSP allowing the captcha scripts?');
814
+ throw new Error('SiaraShield: CheckCaptcha() is not available. Did initSiaraShield() run?');
707
815
  }
708
816
  const existingToken = typeof g.CyberSiaraToken === 'string' && g.CyberSiaraToken.length > 0 ? g.CyberSiaraToken : undefined;
709
817
  if (existingToken) {
@@ -723,14 +831,31 @@ async function checkSiaraShieldCaptchaAsync(options) {
723
831
  const timeoutMs = options?.timeoutMs ?? 1200;
724
832
  const pollIntervalMs = options?.pollIntervalMs ?? 120;
725
833
  const beforeCheckDelayMs = options?.beforeCheckDelayMs ?? 140;
834
+ const retryOnFalseMs = options?.retryOnFalseMs ?? 0;
835
+ const falseResultTokenWaitMs = options?.falseResultTokenWaitMs ?? 900;
726
836
  const existingToken = getSiaraShieldGlobals().CyberSiaraToken;
727
837
  if (typeof existingToken === 'string' && existingToken.length > 0) {
728
838
  return { ok: true, token: existingToken };
729
839
  }
730
840
  await new Promise((resolve) => setTimeout(resolve, beforeCheckDelayMs));
731
- const firstCheck = checkSiaraShieldCaptcha(); // one API call only
732
- if (!firstCheck.ok)
841
+ let firstCheck = checkSiaraShieldCaptcha(); // one API call only
842
+ if (!firstCheck.ok && retryOnFalseMs > 0) {
843
+ // Optional retry for integrations that observe late vendor-state settling.
844
+ await new Promise((resolve) => setTimeout(resolve, retryOnFalseMs));
845
+ firstCheck = checkSiaraShieldCaptcha();
846
+ }
847
+ if (!firstCheck.ok) {
848
+ // Some vendor flows return false first, then set token shortly after.
849
+ const falseStartedAt = Date.now();
850
+ while (Date.now() - falseStartedAt < falseResultTokenWaitMs) {
851
+ await new Promise((resolve) => setTimeout(resolve, pollIntervalMs));
852
+ const token = getSiaraShieldGlobals().CyberSiaraToken;
853
+ if (typeof token === 'string' && token.length > 0) {
854
+ return { ok: true, token };
855
+ }
856
+ }
733
857
  return firstCheck;
858
+ }
734
859
  if (firstCheck.token)
735
860
  return firstCheck;
736
861
  const startedAt = Date.now();