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.
- package/README.md +105 -195
- package/esm2022/lib/siara-shield-script-utils.mjs +3 -2
- package/esm2022/lib/siara-shield-vendor-runtime.mjs +98 -0
- package/esm2022/lib/siara-shield.component.mjs +29 -15
- package/esm2022/lib/siara-shield.mjs +29 -15
- package/fesm2022/siarashield_workspace.mjs +182 -57
- package/fesm2022/siarashield_workspace.mjs.map +1 -1
- package/lib/siara-shield-vendor-runtime.d.ts +1 -0
- package/lib/siara-shield.component.d.ts +3 -1
- package/lib/siara-shield.d.ts +3 -1
- package/package.json +1 -1
- package/siarashield_workspace-0.0.36.tgz +0 -0
- package/siarashield_workspace-0.0.33.tgz +0 -0
|
@@ -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
|
-
|
|
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
|
|
549
|
+
await this.loader.loadScript(JQUERY_FALLBACK_SRC$1);
|
|
453
550
|
}
|
|
454
|
-
await this.loader.loadScript(CAPTCHA_SCRIPT_SRC$1
|
|
455
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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
|
-
|
|
547
|
-
|
|
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.
|
|
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
|
|
780
|
+
await loadScript(document, JQUERY_FALLBACK_SRC);
|
|
670
781
|
}
|
|
671
|
-
await loadScript(document, CAPTCHA_SCRIPT_SRC
|
|
672
|
-
|
|
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.
|
|
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
|
|
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
|
-
|
|
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();
|