@paydock/client-sdk 1.145.0-beta → 1.147.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/README.md +129 -1
- package/bundles/index.cjs +746 -102
- package/bundles/index.cjs.d.ts +131 -14
- package/bundles/index.mjs +746 -103
- package/bundles/index.mjs.d.ts +131 -14
- package/bundles/types/api/api-base.d.ts +6 -0
- package/bundles/types/api/api-base.d.ts.map +1 -1
- package/bundles/types/api/api-charge-internal.d.ts +1 -5
- package/bundles/types/api/api-charge-internal.d.ts.map +1 -1
- package/bundles/types/canvas-3ds/canvas-3ds-event-map.d.ts +50 -0
- package/bundles/types/canvas-3ds/canvas-3ds-event-map.d.ts.map +1 -1
- package/bundles/types/canvas-3ds/canvas-3ds.d.ts +7 -5
- package/bundles/types/canvas-3ds/canvas-3ds.d.ts.map +1 -1
- package/bundles/types/canvas-3ds/index.d.ts +1 -1
- package/bundles/types/canvas-3ds/index.d.ts.map +1 -1
- package/bundles/types/canvas-3ds/services/gpayments-service.d.ts +117 -2
- package/bundles/types/canvas-3ds/services/gpayments-service.d.ts.map +1 -1
- package/bundles/types/canvas-3ds/services/standalone3ds-service.d.ts +6 -0
- package/bundles/types/canvas-3ds/services/standalone3ds-service.d.ts.map +1 -1
- package/bundles/types/components/iframe-event.d.ts +4 -3
- package/bundles/types/components/iframe-event.d.ts.map +1 -1
- package/bundles/types/components/iframe.d.ts +5 -0
- package/bundles/types/components/iframe.d.ts.map +1 -1
- package/bundles/types/components/param.d.ts +19 -0
- package/bundles/types/components/param.d.ts.map +1 -1
- package/bundles/types/index-cba.d.ts +1 -1
- package/bundles/types/index-cba.d.ts.map +1 -1
- package/bundles/types/index.d.ts +2 -2
- package/bundles/types/index.d.ts.map +1 -1
- package/bundles/types/widget/multi-widget.d.ts +19 -0
- package/bundles/types/widget/multi-widget.d.ts.map +1 -1
- package/bundles/widget.umd.js +746 -102
- package/bundles/widget.umd.js.d.ts +131 -14
- package/bundles/widget.umd.js.min.d.ts +131 -14
- package/bundles/widget.umd.min.js +1 -1
- package/docs/api-canvas3ds.md +10 -8
- package/docs/api-checkout-button.md +15 -0
- package/docs/api-ps-widget.md +15 -0
- package/docs/api-widget.md +15 -0
- package/docs/canvas3ds-examples.md +43 -1
- package/docs/widget-examples.md +41 -0
- package/package.json +15 -9
- package/slate.md +84 -1
package/bundles/index.cjs
CHANGED
|
@@ -1865,7 +1865,7 @@ SDK.headerKeys = Object.freeze({
|
|
|
1865
1865
|
version: 'x-sdk-version',
|
|
1866
1866
|
type: 'x-sdk-type'
|
|
1867
1867
|
});
|
|
1868
|
-
SDK._version = 'v1.
|
|
1868
|
+
SDK._version = 'v1.147.0';
|
|
1869
1869
|
|
|
1870
1870
|
var ACCESS_HEADER_NAME = {
|
|
1871
1871
|
accessToken: 'x-access-token',
|
|
@@ -4721,6 +4721,15 @@ var ApiBase = /*#__PURE__*/function () {
|
|
|
4721
4721
|
this.env.setEnv(env, alias);
|
|
4722
4722
|
return this;
|
|
4723
4723
|
}
|
|
4724
|
+
/**
|
|
4725
|
+
* Origin (`scheme://host[:port]`) of the API this client talks to, for the current env/alias.
|
|
4726
|
+
* Used to validate the `origin` of postMessages the API itself sends to the page.
|
|
4727
|
+
*/
|
|
4728
|
+
}, {
|
|
4729
|
+
key: "getApiOrigin",
|
|
4730
|
+
value: function getApiOrigin() {
|
|
4731
|
+
return ApiBase.toOrigin(this.env.getConf().url);
|
|
4732
|
+
}
|
|
4724
4733
|
}, {
|
|
4725
4734
|
key: "requestObservable",
|
|
4726
4735
|
value: function requestObservable(method, url, requestBody) {
|
|
@@ -4933,6 +4942,18 @@ var ApiBase = /*#__PURE__*/function () {
|
|
|
4933
4942
|
environment: this.env.getEnv()
|
|
4934
4943
|
};
|
|
4935
4944
|
}
|
|
4945
|
+
}], [{
|
|
4946
|
+
key: "toOrigin",
|
|
4947
|
+
value: function toOrigin(url) {
|
|
4948
|
+
try {
|
|
4949
|
+
return new URL(url).origin;
|
|
4950
|
+
} catch (_a) {
|
|
4951
|
+
// Not a parsable URL: the returned string is not an origin and will never match a
|
|
4952
|
+
// MessageEvent.origin — surface the misconfiguration instead of failing silently.
|
|
4953
|
+
console.warn("[paydock/client-sdk] cannot derive an origin from configured URL \"".concat(url, "\""));
|
|
4954
|
+
return url.replace(/\/+$/, '');
|
|
4955
|
+
}
|
|
4956
|
+
}
|
|
4936
4957
|
}]);
|
|
4937
4958
|
}();
|
|
4938
4959
|
|
|
@@ -4993,7 +5014,6 @@ var Api = /*#__PURE__*/function (_ApiBase) {
|
|
|
4993
5014
|
|
|
4994
5015
|
var WALLET_CAPTURE_LINK = '/v1/charges/wallet/capture';
|
|
4995
5016
|
var WALLET_CALLBACK_LINK = '/v1/charges/wallet/callback';
|
|
4996
|
-
var STANDALONE_3DS_PROCESS_LINK = '/v1/charges/standalone-3ds/process';
|
|
4997
5017
|
var STANDALONE_3DS_HANDLE_LINK = '/v1/charges/standalone-3ds/handle';
|
|
4998
5018
|
var ApiChargeInternal = /*#__PURE__*/function () {
|
|
4999
5019
|
function ApiChargeInternal(api) {
|
|
@@ -5010,11 +5030,6 @@ var ApiChargeInternal = /*#__PURE__*/function () {
|
|
|
5010
5030
|
value: function walletCallback(payload) {
|
|
5011
5031
|
return this.api.getClientPromise('POST', WALLET_CALLBACK_LINK).send(payload);
|
|
5012
5032
|
}
|
|
5013
|
-
}, {
|
|
5014
|
-
key: "standalone3dsProcess",
|
|
5015
|
-
value: function standalone3dsProcess(payload) {
|
|
5016
|
-
return this.api.getClientPromise('POST', STANDALONE_3DS_PROCESS_LINK).send(payload);
|
|
5017
|
-
}
|
|
5018
5033
|
}, {
|
|
5019
5034
|
key: "standalone3dsHandle",
|
|
5020
5035
|
value: function standalone3dsHandle() {
|
|
@@ -5153,6 +5168,10 @@ var IFrame = /*#__PURE__*/function () {
|
|
|
5153
5168
|
_classCallCheck(this, IFrame);
|
|
5154
5169
|
this.container = container;
|
|
5155
5170
|
}
|
|
5171
|
+
/**
|
|
5172
|
+
* @param options.onLoad Called on every `load` of the iframe (initial document and each
|
|
5173
|
+
* subsequent navigation). Attached before `src` is set so a fast load is never missed.
|
|
5174
|
+
*/
|
|
5156
5175
|
return _createClass(IFrame, [{
|
|
5157
5176
|
key: "load",
|
|
5158
5177
|
value: function load(link) {
|
|
@@ -5161,6 +5180,9 @@ var IFrame = /*#__PURE__*/function () {
|
|
|
5161
5180
|
if (!this.container.isExist() || this.isExist()) return;
|
|
5162
5181
|
var iFrame = document.createElement('iframe');
|
|
5163
5182
|
if (options.title) iFrame.title = options.title;
|
|
5183
|
+
if (options.onLoad) {
|
|
5184
|
+
iFrame.addEventListener('load', options.onLoad);
|
|
5185
|
+
}
|
|
5164
5186
|
if (iframeClass) iFrame.classList.add(iframeClass);
|
|
5165
5187
|
// Set additional attributes before src and DOM insertion so that
|
|
5166
5188
|
// Permissions-Policy (e.g. allow="payment") is in place when the
|
|
@@ -5301,14 +5323,14 @@ var IFrameEvent = /*#__PURE__*/function () {
|
|
|
5301
5323
|
data = JSON.parse(event.data);
|
|
5302
5324
|
} catch (error) {}
|
|
5303
5325
|
if (!data) return;
|
|
5304
|
-
_this.emit(data);
|
|
5326
|
+
_this.emit(data, event.origin);
|
|
5305
5327
|
});
|
|
5306
5328
|
}
|
|
5307
5329
|
return _createClass(IFrameEvent, [{
|
|
5308
5330
|
key: "emit",
|
|
5309
|
-
value: function emit(data) {
|
|
5331
|
+
value: function emit(data, origin) {
|
|
5310
5332
|
for (var key in this.listeners) {
|
|
5311
|
-
if (this.listeners[key].event === data.event && data.widget_id === this.listeners[key].widget_id) this.listeners[key].listener.apply(this, [data]);
|
|
5333
|
+
if (this.listeners[key].event === data.event && data.widget_id === this.listeners[key].widget_id) this.listeners[key].listener.apply(this, [data, origin]);
|
|
5312
5334
|
}
|
|
5313
5335
|
}
|
|
5314
5336
|
}, {
|
|
@@ -5381,6 +5403,20 @@ var GPAYMENTS_EVENT = {
|
|
|
5381
5403
|
DECOUPLED: 'chargeAuthDecoupled',
|
|
5382
5404
|
/** Challenge required — issuer demands user interaction. Payload: `Canvas3dsAuthPayload`. */
|
|
5383
5405
|
CHALLENGE: 'chargeAuthChallenge',
|
|
5406
|
+
/**
|
|
5407
|
+
* The challenge iframe fired its FIRST `load` after `chargeAuthChallenge` (the ACS page has painted),
|
|
5408
|
+
* or the `CHALLENGE_LOADED_FALLBACK_MS` safety fallback elapsed without one. Emitted exactly once per
|
|
5409
|
+
* challenge; never emitted for a decoupled challenge (no challenge iframe). Payload: `Canvas3dsChallengeLoadedPayload`.
|
|
5410
|
+
*/
|
|
5411
|
+
CHALLENGE_LOADED: 'chargeAuthChallengeLoaded',
|
|
5412
|
+
/**
|
|
5413
|
+
* The issuer delivered the final authentication result to the 3DS Server (the shopper finished the
|
|
5414
|
+
* challenge, or approved a decoupled authentication) and the SDK is now obtaining the outcome. Emitted at
|
|
5415
|
+
* most once per authentication, always before the terminal event. Detected through the `secondary_url`
|
|
5416
|
+
* result monitor (`source: 'poll'`) or, when the callback message arrives first, right before the outcome
|
|
5417
|
+
* it carries (`source: 'callback'`). Payload: `Canvas3dsChallengeCompletedPayload`.
|
|
5418
|
+
*/
|
|
5419
|
+
CHALLENGE_COMPLETED: 'chargeAuthChallengeCompleted',
|
|
5384
5420
|
/** Informational signal emitted by the GPayments flow. Internal — not part of the public typed surface. */
|
|
5385
5421
|
INFO: 'chargeAuthInfo',
|
|
5386
5422
|
/** Error during the flow (network, parsing, etc.). Payload: `Canvas3dsErrorPayload`. */
|
|
@@ -5392,13 +5428,56 @@ var GPAYMENTS_EVENT = {
|
|
|
5392
5428
|
* backwards-compatible internal usage.
|
|
5393
5429
|
*/
|
|
5394
5430
|
var CANVAS_3DS_EVENT = GPAYMENTS_EVENT;
|
|
5431
|
+
/**
|
|
5432
|
+
* Upper bound on how long `chargeAuthChallengeLoaded` waits for the challenge iframe's first `load`.
|
|
5433
|
+
* When it elapses the event is emitted with `reason: 'timeout'` so a merchant loader is never held
|
|
5434
|
+
* forever; the real `load` arriving afterwards is the paint and is simply ignored.
|
|
5435
|
+
*/
|
|
5436
|
+
var CHALLENGE_LOADED_FALLBACK_MS = 2500;
|
|
5437
|
+
/**
|
|
5438
|
+
* Interval of the `secondary_url` (GPayments `resultMonUrl`) poll while an ACS challenge is on screen.
|
|
5439
|
+
* The monitor flips to `AuthResultReady` when the issuer's RReq reaches the 3DS Server — before the ACS has
|
|
5440
|
+
* even returned its final page to the browser — so this poll is what makes `chargeAuthChallengeCompleted`
|
|
5441
|
+
* land before the shopper sees any post-submit page. GPayments recommends 2 s and leaves the interval to the
|
|
5442
|
+
* requestor; 1 s matches the cadence of GPayments' own monitoring frame.
|
|
5443
|
+
*/
|
|
5444
|
+
var CHALLENGE_RESULT_POLL_INTERVAL_MS = 1000;
|
|
5445
|
+
/**
|
|
5446
|
+
* Interval of the `secondary_url` poll for a decoupled challenge (shopper approves out-of-band, possibly
|
|
5447
|
+
* minutes later; nothing to cover on screen) — the GPayments-recommended 2 s.
|
|
5448
|
+
*/
|
|
5449
|
+
var DECOUPLED_RESULT_POLL_INTERVAL_MS = 2000;
|
|
5450
|
+
/** The result-monitor request failed at transport level (network, abort, timeout) — retried, not fatal. */
|
|
5451
|
+
var ResultMonitorTransportError = /*#__PURE__*/function (_Error) {
|
|
5452
|
+
function ResultMonitorTransportError() {
|
|
5453
|
+
var _this;
|
|
5454
|
+
_classCallCheck(this, ResultMonitorTransportError);
|
|
5455
|
+
_this = _callSuper(this, ResultMonitorTransportError, ['Standalone 3DS result monitor request failed']);
|
|
5456
|
+
_this.name = 'ResultMonitorTransportError';
|
|
5457
|
+
return _this;
|
|
5458
|
+
}
|
|
5459
|
+
_inherits(ResultMonitorTransportError, _Error);
|
|
5460
|
+
return _createClass(ResultMonitorTransportError);
|
|
5461
|
+
}(/*#__PURE__*/_wrapNativeSuper(Error));
|
|
5395
5462
|
var GPaymentsService = /*#__PURE__*/function () {
|
|
5396
5463
|
function GPaymentsService(container, api, eventEmitter) {
|
|
5464
|
+
var trustedOrigins = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : [];
|
|
5397
5465
|
_classCallCheck(this, GPaymentsService);
|
|
5398
5466
|
this.container = container;
|
|
5399
5467
|
this.api = api;
|
|
5400
5468
|
this.eventEmitter = eventEmitter;
|
|
5469
|
+
this.trustedOrigins = trustedOrigins;
|
|
5401
5470
|
this.resultRead = false;
|
|
5471
|
+
/** `chargeAuthChallengeLoaded` already emitted for the current challenge (by `load` or by the fallback). */
|
|
5472
|
+
this.challengeLoadedEmitted = false;
|
|
5473
|
+
/** `chargeAuthChallengeCompleted` already emitted for the current authentication (poll or callback). */
|
|
5474
|
+
this.challengeCompletedEmitted = false;
|
|
5475
|
+
/** Set once a terminal event was emitted: no lifecycle event may follow (reset when a new challenge starts). */
|
|
5476
|
+
this.challengeLifecycleClosed = false;
|
|
5477
|
+
this.challengeLoadedTimer = null;
|
|
5478
|
+
this.resultPollTimer = null;
|
|
5479
|
+
/** One-shot guard for the `chargeAuth` leg — the callback message may be delivered more than once. */
|
|
5480
|
+
this.authHandled = false;
|
|
5402
5481
|
this.iFrameEvent = new IFrameEvent(window);
|
|
5403
5482
|
}
|
|
5404
5483
|
return _createClass(GPaymentsService, [{
|
|
@@ -5418,6 +5497,7 @@ var GPaymentsService = /*#__PURE__*/function () {
|
|
|
5418
5497
|
key: "initializeIFrames",
|
|
5419
5498
|
value: function initializeIFrames(initializationUrl, secondaryUrl, title) {
|
|
5420
5499
|
var hideAuthorization = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : true;
|
|
5500
|
+
var onLoad = arguments.length > 4 ? arguments[4] : undefined;
|
|
5421
5501
|
var container = this.container.getElement();
|
|
5422
5502
|
if (!container) {
|
|
5423
5503
|
return;
|
|
@@ -5428,7 +5508,8 @@ var GPaymentsService = /*#__PURE__*/function () {
|
|
|
5428
5508
|
this.browserAndChallengeContainer = new Container('#paydock_authorization_iframe');
|
|
5429
5509
|
this.iFrameAuthorization = new IFrame(this.browserAndChallengeContainer);
|
|
5430
5510
|
this.iFrameAuthorization.load(initializationUrl, {
|
|
5431
|
-
title: title
|
|
5511
|
+
title: title,
|
|
5512
|
+
onLoad: onLoad
|
|
5432
5513
|
});
|
|
5433
5514
|
if (secondaryUrl) {
|
|
5434
5515
|
var divSecondaryURL = document.createElement('div');
|
|
@@ -5462,25 +5543,192 @@ var GPaymentsService = /*#__PURE__*/function () {
|
|
|
5462
5543
|
}, {
|
|
5463
5544
|
key: "setupIFrameEvents",
|
|
5464
5545
|
value: function setupIFrameEvents(widgetId) {
|
|
5465
|
-
var
|
|
5466
|
-
this.iFrameEvent.on(EVENT$4.CHARGE_AUTH, widgetId, function (data) {
|
|
5546
|
+
var _this2 = this;
|
|
5547
|
+
this.iFrameEvent.on(EVENT$4.CHARGE_AUTH, widgetId, function (data, origin) {
|
|
5467
5548
|
if (data.status === IFRAME_STATUS.SKIPPED || data.status === IFRAME_STATUS.FINISHED) {
|
|
5549
|
+
// this channel is spoofable — any frame that knows the charge_3ds_id can post to it.
|
|
5550
|
+
// A message from an unknown sender must never terminate a live authentication: ignore it.
|
|
5551
|
+
if (!_this2.isTrustedOrigin(origin)) {
|
|
5552
|
+
return;
|
|
5553
|
+
}
|
|
5554
|
+
// one-shot: the callback message can be delivered more than once (it is posted to both
|
|
5555
|
+
// `parent` and `top`) — a duplicate must not re-emit events or duplicate challenge iframes
|
|
5556
|
+
if (_this2.authHandled) {
|
|
5557
|
+
return;
|
|
5558
|
+
}
|
|
5559
|
+
_this2.authHandled = true;
|
|
5468
5560
|
if (data.info) {
|
|
5469
|
-
|
|
5561
|
+
_this2.eventEmitter.emit(GPAYMENTS_EVENT.INFO, {
|
|
5470
5562
|
info: data.info
|
|
5471
5563
|
});
|
|
5472
5564
|
}
|
|
5473
|
-
|
|
5565
|
+
_this2.removeIframes();
|
|
5566
|
+
if (typeof data.process_status === 'string') {
|
|
5567
|
+
// the API already ran the authentication inside the callback: no /process call needed
|
|
5568
|
+
_this2.handleAuthenticationOutcome({
|
|
5569
|
+
status: data.process_status,
|
|
5570
|
+
result: data.result,
|
|
5571
|
+
error: data.error
|
|
5572
|
+
}, data.charge_3ds_id);
|
|
5573
|
+
} else {
|
|
5574
|
+
// The API always runs the authentication inside the callback (PAYRAC-2480). A trusted
|
|
5575
|
+
// message without the outcome means an outdated API — fail, never verify via /process.
|
|
5576
|
+
_this2.finishChallengeLifecycle();
|
|
5577
|
+
_this2.eventEmitter.emit(GPAYMENTS_EVENT.ERROR, _this2.parseError({
|
|
5578
|
+
message: 'Standalone 3DS authentication outcome missing from the callback message'
|
|
5579
|
+
}, data.charge_3ds_id));
|
|
5580
|
+
}
|
|
5474
5581
|
} else if (data.status === IFRAME_STATUS.TIMEOUT || data.status === IFRAME_STATUS.INVALID) {
|
|
5475
|
-
|
|
5582
|
+
_this2.finishChallengeLifecycle();
|
|
5583
|
+
_this2.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_ERROR, _this2.parseHandleResponse({
|
|
5476
5584
|
status: data.status
|
|
5477
5585
|
}, data.charge_3ds_id));
|
|
5478
5586
|
}
|
|
5479
5587
|
});
|
|
5480
|
-
this.iFrameEvent.on(EVENT$4.CHARGE_AUTH_SUCCESS, widgetId, function (data) {
|
|
5481
|
-
|
|
5588
|
+
this.iFrameEvent.on(EVENT$4.CHARGE_AUTH_SUCCESS, widgetId, function (data, origin) {
|
|
5589
|
+
// the callback iframe reached Paydock with `AuthResultReady`: the issuer has answered. Announce it
|
|
5590
|
+
// before the outcome so merchants always see completed → terminal, whichever transport won.
|
|
5591
|
+
_this2.emitChallengeCompleted(data.charge_3ds_id, 'callback');
|
|
5592
|
+
if (_this2.hasChainedOutcome(data, origin)) {
|
|
5593
|
+
// the API already fetched the challenge result inside the callback: no GET /handle needed
|
|
5594
|
+
_this2.handleResultOutcome({
|
|
5595
|
+
status: data.process_status,
|
|
5596
|
+
result: data.result,
|
|
5597
|
+
error: data.error
|
|
5598
|
+
}, data.charge_3ds_id);
|
|
5599
|
+
} else {
|
|
5600
|
+
_this2.processResult(data.charge_3ds_id);
|
|
5601
|
+
}
|
|
5602
|
+
});
|
|
5603
|
+
}
|
|
5604
|
+
/**
|
|
5605
|
+
* A message carries a processed outcome only when it has `process_status` AND comes from a trusted
|
|
5606
|
+
* origin. Used by the result leg: an untrusted `process_status` is never acted upon — the SDK falls
|
|
5607
|
+
* back to `GET /handle` (still needed for decoupled challenges / polling). The authentication leg
|
|
5608
|
+
* ignores untrusted senders outright, before this check.
|
|
5609
|
+
*/
|
|
5610
|
+
}, {
|
|
5611
|
+
key: "hasChainedOutcome",
|
|
5612
|
+
value: function hasChainedOutcome(data, origin) {
|
|
5613
|
+
return typeof data.process_status === 'string' && this.isTrustedOrigin(origin);
|
|
5614
|
+
}
|
|
5615
|
+
}, {
|
|
5616
|
+
key: "isTrustedOrigin",
|
|
5617
|
+
value: function isTrustedOrigin(origin) {
|
|
5618
|
+
// 'null' is the serialized opaque origin (e.g. a sandboxed iframe) — never trust it, even if a
|
|
5619
|
+
// misconfigured env URL were ever to put the literal string 'null' into the trusted list
|
|
5620
|
+
return typeof origin === 'string' && origin !== 'null' && this.trustedOrigins.includes(origin);
|
|
5621
|
+
}
|
|
5622
|
+
}, {
|
|
5623
|
+
key: "removeIframes",
|
|
5624
|
+
value: function removeIframes() {
|
|
5625
|
+
var _a, _b;
|
|
5626
|
+
this.clearChallengeLoadedTimer();
|
|
5627
|
+
(_a = this.iFrameAuthorization) === null || _a === void 0 ? void 0 : _a.remove();
|
|
5628
|
+
(_b = this.iFrameSecondaryUrl) === null || _b === void 0 ? void 0 : _b.remove();
|
|
5629
|
+
}
|
|
5630
|
+
}, {
|
|
5631
|
+
key: "clearChallengeLoadedTimer",
|
|
5632
|
+
value: function clearChallengeLoadedTimer() {
|
|
5633
|
+
if (this.challengeLoadedTimer !== null) {
|
|
5634
|
+
clearTimeout(this.challengeLoadedTimer);
|
|
5635
|
+
this.challengeLoadedTimer = null;
|
|
5636
|
+
}
|
|
5637
|
+
}
|
|
5638
|
+
/**
|
|
5639
|
+
* Challenge iframe lifecycle (`chargeAuthChallengeLoaded`).
|
|
5640
|
+
*
|
|
5641
|
+
* The challenge iframe is cross-origin, but its `load` event still fires when the ACS page finishes loading:
|
|
5642
|
+
* the first one means the page painted (merchants can drop their loader). A fallback timer
|
|
5643
|
+
* (`armChallengeLoadedFallback`) guarantees `chargeAuthChallengeLoaded` within `CHALLENGE_LOADED_FALLBACK_MS`
|
|
5644
|
+
* even if no `load` ever fires; a late paint after that is simply ignored. Later `load`s are NOT a usable
|
|
5645
|
+
* signal for "the shopper submitted": the post-submit hops are HTTP redirects and self-submitting pages whose
|
|
5646
|
+
* loads abort, and the final callback page posts its message before its own `load` — so the post-submit
|
|
5647
|
+
* moment is `chargeAuthChallengeCompleted`, driven by the `secondary_url` result monitor instead.
|
|
5648
|
+
*
|
|
5649
|
+
* Nothing is emitted once the flow reached a terminal outcome (`finishChallengeLifecycle`), and a
|
|
5650
|
+
* decoupled challenge (no challenge iframe) never emits `chargeAuthChallengeLoaded`.
|
|
5651
|
+
*
|
|
5652
|
+
* Resets the per-challenge state and returns the `load` handler to attach to the challenge iframe.
|
|
5653
|
+
*/
|
|
5654
|
+
}, {
|
|
5655
|
+
key: "startChallengeLifecycle",
|
|
5656
|
+
value: function startChallengeLifecycle(charge3dsId) {
|
|
5657
|
+
var _this3 = this;
|
|
5658
|
+
this.clearChallengeLoadedTimer();
|
|
5659
|
+
this.challengeLoadedEmitted = false;
|
|
5660
|
+
this.challengeCompletedEmitted = false;
|
|
5661
|
+
this.challengeLifecycleClosed = false;
|
|
5662
|
+
return function () {
|
|
5663
|
+
var _a;
|
|
5664
|
+
if (_this3.challengeLifecycleClosed || _this3.challengeLoadedEmitted || !((_a = _this3.iFrameAuthorization) === null || _a === void 0 ? void 0 : _a.isExist())) {
|
|
5665
|
+
return;
|
|
5666
|
+
}
|
|
5667
|
+
_this3.clearChallengeLoadedTimer();
|
|
5668
|
+
_this3.emitChallengeLoaded(charge3dsId, 'load');
|
|
5669
|
+
};
|
|
5670
|
+
}
|
|
5671
|
+
/**
|
|
5672
|
+
* Arms the `chargeAuthChallengeLoaded` fallback — only when a challenge iframe actually exists (the
|
|
5673
|
+
* container may be gone, or `IFrame.load` may have no-op'd on an already-present iframe).
|
|
5674
|
+
*/
|
|
5675
|
+
}, {
|
|
5676
|
+
key: "armChallengeLoadedFallback",
|
|
5677
|
+
value: function armChallengeLoadedFallback(charge3dsId) {
|
|
5678
|
+
var _this4 = this;
|
|
5679
|
+
var _a;
|
|
5680
|
+
if (!((_a = this.iFrameAuthorization) === null || _a === void 0 ? void 0 : _a.isExist())) {
|
|
5681
|
+
return;
|
|
5682
|
+
}
|
|
5683
|
+
this.challengeLoadedTimer = setTimeout(function () {
|
|
5684
|
+
_this4.challengeLoadedTimer = null;
|
|
5685
|
+
if (_this4.challengeLifecycleClosed || _this4.challengeLoadedEmitted) {
|
|
5686
|
+
return;
|
|
5687
|
+
}
|
|
5688
|
+
_this4.emitChallengeLoaded(charge3dsId, 'timeout');
|
|
5689
|
+
}, CHALLENGE_LOADED_FALLBACK_MS);
|
|
5690
|
+
}
|
|
5691
|
+
}, {
|
|
5692
|
+
key: "emitChallengeLoaded",
|
|
5693
|
+
value: function emitChallengeLoaded(charge3dsId, reason) {
|
|
5694
|
+
this.challengeLoadedEmitted = true;
|
|
5695
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.CHALLENGE_LOADED, {
|
|
5696
|
+
charge_3ds_id: charge3dsId,
|
|
5697
|
+
reason: reason
|
|
5698
|
+
});
|
|
5699
|
+
}
|
|
5700
|
+
/**
|
|
5701
|
+
* `chargeAuthChallengeCompleted` — once per authentication, never after a terminal event. Idempotent so the
|
|
5702
|
+
* poll and the callback message can both call it: the first transport to notice the issuer's result wins.
|
|
5703
|
+
*/
|
|
5704
|
+
}, {
|
|
5705
|
+
key: "emitChallengeCompleted",
|
|
5706
|
+
value: function emitChallengeCompleted(charge3dsId, source) {
|
|
5707
|
+
if (this.challengeLifecycleClosed || this.challengeCompletedEmitted) {
|
|
5708
|
+
return;
|
|
5709
|
+
}
|
|
5710
|
+
this.challengeCompletedEmitted = true;
|
|
5711
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.CHALLENGE_COMPLETED, {
|
|
5712
|
+
charge_3ds_id: charge3dsId,
|
|
5713
|
+
source: source
|
|
5482
5714
|
});
|
|
5483
5715
|
}
|
|
5716
|
+
/** Call before emitting any terminal event: no lifecycle event may follow it, and the result poll stops. */
|
|
5717
|
+
}, {
|
|
5718
|
+
key: "finishChallengeLifecycle",
|
|
5719
|
+
value: function finishChallengeLifecycle() {
|
|
5720
|
+
this.clearChallengeLoadedTimer();
|
|
5721
|
+
this.stopResultPolling();
|
|
5722
|
+
this.challengeLifecycleClosed = true;
|
|
5723
|
+
}
|
|
5724
|
+
}, {
|
|
5725
|
+
key: "stopResultPolling",
|
|
5726
|
+
value: function stopResultPolling() {
|
|
5727
|
+
if (this.resultPollTimer !== null) {
|
|
5728
|
+
clearTimeout(this.resultPollTimer);
|
|
5729
|
+
this.resultPollTimer = null;
|
|
5730
|
+
}
|
|
5731
|
+
}
|
|
5484
5732
|
}, {
|
|
5485
5733
|
key: "parseResultData",
|
|
5486
5734
|
value: function parseResultData(_ref2, charge3dsId) {
|
|
@@ -5514,24 +5762,49 @@ var GPaymentsService = /*#__PURE__*/function () {
|
|
|
5514
5762
|
}, {
|
|
5515
5763
|
key: "processResult",
|
|
5516
5764
|
value: function processResult(charge3dsId) {
|
|
5517
|
-
var
|
|
5765
|
+
var _this5 = this;
|
|
5518
5766
|
if (this.resultRead) {
|
|
5519
5767
|
return;
|
|
5520
5768
|
}
|
|
5521
5769
|
this.resultRead = true;
|
|
5770
|
+
this.finishChallengeLifecycle();
|
|
5522
5771
|
this.api.charge().standalone3dsHandle().then(function (result) {
|
|
5523
5772
|
var _a;
|
|
5524
|
-
|
|
5525
|
-
(_a =
|
|
5773
|
+
_this5.iFrameAuthorization.remove();
|
|
5774
|
+
(_a = _this5.iFrameSecondaryUrl) === null || _a === void 0 ? void 0 : _a.remove();
|
|
5526
5775
|
if (result.status === PROCESS_STANDALONE_3DS_STATUS.SUCCESS) {
|
|
5527
|
-
|
|
5776
|
+
_this5.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_SUCCESS, _this5.parseResultData(result, charge3dsId));
|
|
5528
5777
|
} else {
|
|
5529
|
-
|
|
5778
|
+
_this5.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_ERROR, _this5.parseResultData(result, charge3dsId));
|
|
5530
5779
|
}
|
|
5531
5780
|
}, function (err) {
|
|
5532
|
-
|
|
5781
|
+
_this5.eventEmitter.emit(GPAYMENTS_EVENT.ERROR, _this5.parseError(err, charge3dsId));
|
|
5533
5782
|
});
|
|
5534
5783
|
}
|
|
5784
|
+
/** Final (post-challenge) outcome delivered by the API through the callback message. */
|
|
5785
|
+
}, {
|
|
5786
|
+
key: "handleResultOutcome",
|
|
5787
|
+
value: function handleResultOutcome(_ref4, charge3dsId) {
|
|
5788
|
+
var status = _ref4.status,
|
|
5789
|
+
error = _ref4.error;
|
|
5790
|
+
if (this.resultRead) {
|
|
5791
|
+
return;
|
|
5792
|
+
}
|
|
5793
|
+
this.resultRead = true;
|
|
5794
|
+
this.finishChallengeLifecycle();
|
|
5795
|
+
this.removeIframes();
|
|
5796
|
+
if (status === PROCESS_STANDALONE_3DS_STATUS.SUCCESS) {
|
|
5797
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_SUCCESS, this.parseResultData({
|
|
5798
|
+
status: status
|
|
5799
|
+
}, charge3dsId));
|
|
5800
|
+
} else if (status === PROCESS_STANDALONE_3DS_STATUS.ERROR) {
|
|
5801
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.ERROR, this.parseError(error, charge3dsId));
|
|
5802
|
+
} else {
|
|
5803
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_ERROR, this.parseResultData({
|
|
5804
|
+
status: status
|
|
5805
|
+
}, charge3dsId));
|
|
5806
|
+
}
|
|
5807
|
+
}
|
|
5535
5808
|
}, {
|
|
5536
5809
|
key: "externalAPI",
|
|
5537
5810
|
value: function externalAPI(method, url) {
|
|
@@ -5545,60 +5818,106 @@ var GPaymentsService = /*#__PURE__*/function () {
|
|
|
5545
5818
|
reject(error);
|
|
5546
5819
|
}
|
|
5547
5820
|
};
|
|
5821
|
+
// without these a failed request left the promise pending forever and the poll silently dead
|
|
5822
|
+
var fail = function fail() {
|
|
5823
|
+
return reject(new ResultMonitorTransportError());
|
|
5824
|
+
};
|
|
5825
|
+
request.onerror = fail;
|
|
5826
|
+
request.onabort = fail;
|
|
5827
|
+
request.ontimeout = fail;
|
|
5548
5828
|
request.send();
|
|
5549
5829
|
});
|
|
5550
5830
|
}
|
|
5831
|
+
/**
|
|
5832
|
+
* Polls the GPayments result monitor (`secondary_url`) until `AuthResultReady`. It is the primary source of
|
|
5833
|
+
* `chargeAuthChallengeCompleted` (the monitor flips as soon as the issuer's RReq reaches the 3DS Server) and
|
|
5834
|
+
* the fallback transport for the outcome itself (`GET /handle`) when the callback message never arrives —
|
|
5835
|
+
* the only transport for decoupled challenges. Stops once an outcome was obtained or the lifecycle closed.
|
|
5836
|
+
* Transport failures are retried on the same cadence; an unparsable body or an unknown event is fatal.
|
|
5837
|
+
*/
|
|
5551
5838
|
}, {
|
|
5552
5839
|
key: "doPolling",
|
|
5553
|
-
value: function doPolling(url, charge3dsId) {
|
|
5554
|
-
var
|
|
5840
|
+
value: function doPolling(url, charge3dsId, intervalMs) {
|
|
5841
|
+
var _this6 = this;
|
|
5842
|
+
this.stopResultPolling();
|
|
5843
|
+
if (this.resultRead || this.challengeLifecycleClosed) {
|
|
5844
|
+
return;
|
|
5845
|
+
}
|
|
5846
|
+
var schedule = function schedule() {
|
|
5847
|
+
_this6.resultPollTimer = setTimeout(function () {
|
|
5848
|
+
_this6.resultPollTimer = null;
|
|
5849
|
+
_this6.doPolling(url, charge3dsId, intervalMs);
|
|
5850
|
+
}, intervalMs);
|
|
5851
|
+
};
|
|
5555
5852
|
this.externalAPI('GET', url).then(function (data) {
|
|
5853
|
+
if (_this6.resultRead || _this6.challengeLifecycleClosed) {
|
|
5854
|
+
return;
|
|
5855
|
+
}
|
|
5556
5856
|
if (!data.event || data.event === 'AuthResultNotReady') {
|
|
5557
|
-
|
|
5558
|
-
_this3.doPolling(url, charge3dsId);
|
|
5559
|
-
}, 2000);
|
|
5857
|
+
schedule();
|
|
5560
5858
|
} else if (data.event === 'AuthResultReady') {
|
|
5561
|
-
|
|
5859
|
+
_this6.emitChallengeCompleted(charge3dsId, 'poll');
|
|
5860
|
+
_this6.processResult(charge3dsId);
|
|
5562
5861
|
} else {
|
|
5563
5862
|
throw new Error('Event not supported');
|
|
5564
5863
|
}
|
|
5565
5864
|
})["catch"](function (err) {
|
|
5566
|
-
|
|
5865
|
+
if (err instanceof ResultMonitorTransportError) {
|
|
5866
|
+
if (!_this6.resultRead && !_this6.challengeLifecycleClosed) {
|
|
5867
|
+
schedule();
|
|
5868
|
+
}
|
|
5869
|
+
return;
|
|
5870
|
+
}
|
|
5871
|
+
_this6.finishChallengeLifecycle();
|
|
5872
|
+
_this6.eventEmitter.emit(GPAYMENTS_EVENT.ERROR, _this6.parseError(err, charge3dsId));
|
|
5567
5873
|
});
|
|
5568
5874
|
}
|
|
5875
|
+
/**
|
|
5876
|
+
* Branches on the authentication outcome delivered by the API through the callback message
|
|
5877
|
+
* (`process_status` → `status`; same shape as the `/process` response body).
|
|
5878
|
+
*/
|
|
5569
5879
|
}, {
|
|
5570
|
-
key: "
|
|
5571
|
-
value: function
|
|
5572
|
-
var
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
5580
|
-
|
|
5581
|
-
if (authenticationResult.
|
|
5582
|
-
|
|
5583
|
-
|
|
5584
|
-
|
|
5585
|
-
|
|
5586
|
-
|
|
5587
|
-
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5880
|
+
key: "handleAuthenticationOutcome",
|
|
5881
|
+
value: function handleAuthenticationOutcome(authenticationResult, charge_3ds_id) {
|
|
5882
|
+
var _a, _b, _c;
|
|
5883
|
+
if (authenticationResult.status !== AUTHENTICATION_STATUS.PENDING) {
|
|
5884
|
+
// terminal outcome — a later `chargeAuthSuccess` message or poll must not emit a second result
|
|
5885
|
+
this.resultRead = true;
|
|
5886
|
+
}
|
|
5887
|
+
if (authenticationResult.status === AUTHENTICATION_STATUS.SUCCESS) {
|
|
5888
|
+
this.finishChallengeLifecycle();
|
|
5889
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_SUCCESS, this.parseHandleResponse(authenticationResult, charge_3ds_id));
|
|
5890
|
+
} else if (authenticationResult.status === AUTHENTICATION_STATUS.PENDING) {
|
|
5891
|
+
if ((_a = authenticationResult === null || authenticationResult === void 0 ? void 0 : authenticationResult.result) === null || _a === void 0 ? void 0 : _a.challenge) {
|
|
5892
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.CHALLENGE, this.parseHandleResponse(authenticationResult, charge_3ds_id));
|
|
5893
|
+
var onChallengeLoad = this.startChallengeLifecycle(charge_3ds_id);
|
|
5894
|
+
this.initializeIFrames(authenticationResult.result.challenge_url, undefined, 'Authentication Challenge', false, onChallengeLoad);
|
|
5895
|
+
this.armChallengeLoadedFallback(charge_3ds_id);
|
|
5896
|
+
if (authenticationResult.result.secondary_url) {
|
|
5897
|
+
this.doPolling(authenticationResult.result.secondary_url, charge_3ds_id, CHALLENGE_RESULT_POLL_INTERVAL_MS);
|
|
5898
|
+
}
|
|
5899
|
+
} else if ((_b = authenticationResult === null || authenticationResult === void 0 ? void 0 : authenticationResult.result) === null || _b === void 0 ? void 0 : _b.decoupled_challenge) {
|
|
5900
|
+
this.challengeCompletedEmitted = false;
|
|
5901
|
+
this.challengeLifecycleClosed = false;
|
|
5902
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.DECOUPLED, this.parseHandleResponse(authenticationResult, charge_3ds_id));
|
|
5903
|
+
if (authenticationResult.result.secondary_url) {
|
|
5904
|
+
this.doPolling(authenticationResult.result.secondary_url, charge_3ds_id, DECOUPLED_RESULT_POLL_INTERVAL_MS);
|
|
5595
5905
|
}
|
|
5596
5906
|
} else {
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5907
|
+
// pending but nothing to run (no challenge delivered) — fail rather than hang silently
|
|
5908
|
+
// with the iframes already removed and no further message ever coming
|
|
5909
|
+
this.resultRead = true;
|
|
5910
|
+
this.finishChallengeLifecycle();
|
|
5911
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_ERROR, this.parseHandleResponse(authenticationResult, charge_3ds_id));
|
|
5912
|
+
}
|
|
5913
|
+
} else if (authenticationResult.status === AUTHENTICATION_STATUS.ERROR) {
|
|
5914
|
+
// the API rejected the authentication, or could not run it inside the callback
|
|
5915
|
+
this.finishChallengeLifecycle();
|
|
5916
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.ERROR, this.parseError((_c = authenticationResult.error) !== null && _c !== void 0 ? _c : authenticationResult, charge_3ds_id));
|
|
5917
|
+
} else {
|
|
5918
|
+
this.finishChallengeLifecycle();
|
|
5919
|
+
this.eventEmitter.emit(GPAYMENTS_EVENT.AUTH_ERROR, this.parseHandleResponse(authenticationResult, charge_3ds_id));
|
|
5920
|
+
}
|
|
5602
5921
|
}
|
|
5603
5922
|
}]);
|
|
5604
5923
|
}();
|
|
@@ -5625,10 +5944,27 @@ var Standalone3dsService = /*#__PURE__*/function () {
|
|
|
5625
5944
|
api.setEnv(this.env, this.alias);
|
|
5626
5945
|
switch (tokenData.service_type) {
|
|
5627
5946
|
case STANDALONE_3DS_GATEWAYS.GPAYMENTS:
|
|
5628
|
-
new GPaymentsService(this.container, api, this.eventEmitter).load(tokenData, options.title);
|
|
5947
|
+
new GPaymentsService(this.container, api, this.eventEmitter, this.getTrustedMessageOrigins(api)).load(tokenData, options.title);
|
|
5629
5948
|
break;
|
|
5630
5949
|
}
|
|
5631
5950
|
}
|
|
5951
|
+
/**
|
|
5952
|
+
* Origins allowed to deliver a processed 3DS outcome to the page via postMessage: the Paydock API
|
|
5953
|
+
* (inline callback page) and the Paydock widget (remote-app landing page), for the current env/alias.
|
|
5954
|
+
*/
|
|
5955
|
+
}, {
|
|
5956
|
+
key: "getTrustedMessageOrigins",
|
|
5957
|
+
value: function getTrustedMessageOrigins(api) {
|
|
5958
|
+
var origins = [api.getApiOrigin()];
|
|
5959
|
+
try {
|
|
5960
|
+
var widgetEnv = new Env(WIDGET_URL, this.env);
|
|
5961
|
+
widgetEnv.setEnv(this.env, this.alias);
|
|
5962
|
+
origins.push(ApiBase.toOrigin(widgetEnv.getConf().url));
|
|
5963
|
+
} catch (_a) {
|
|
5964
|
+
// widget origin unknown for this env — the API origin is enough
|
|
5965
|
+
}
|
|
5966
|
+
return origins;
|
|
5967
|
+
}
|
|
5632
5968
|
}, {
|
|
5633
5969
|
key: "setEnv",
|
|
5634
5970
|
value: function setEnv(env, alias) {
|
|
@@ -5797,11 +6133,13 @@ var TOKEN_FORMAT$2;
|
|
|
5797
6133
|
* @const STANDALONE_3DS_EVENT
|
|
5798
6134
|
*
|
|
5799
6135
|
* @type {object}
|
|
5800
|
-
* @param {string}
|
|
5801
|
-
* @param {string}
|
|
5802
|
-
* @param {string}
|
|
5803
|
-
* @param {string}
|
|
5804
|
-
* @param {string}
|
|
6136
|
+
* @param {string} AUTH_SUCCESS=chargeAuthSuccess
|
|
6137
|
+
* @param {string} AUTH_ERROR=chargeAuthReject
|
|
6138
|
+
* @param {string} DECOUPLED=chargeAuthDecoupled
|
|
6139
|
+
* @param {string} CHALLENGE=chargeAuthChallenge
|
|
6140
|
+
* @param {string} CHALLENGE_LOADED=chargeAuthChallengeLoaded - challenge iframe painted (first `load`, or 2.5 s fallback); reveal your challenge UI here
|
|
6141
|
+
* @param {string} CHALLENGE_COMPLETED=chargeAuthChallengeCompleted - the shopper finished the challenge (issuer result delivered to the 3DS Server); cover the challenge iframe with your own loader until the terminal event
|
|
6142
|
+
* @param {string} INFO=chargeAuthInfo
|
|
5805
6143
|
* @param {string} ERROR=error
|
|
5806
6144
|
*/
|
|
5807
6145
|
/**
|
|
@@ -6183,6 +6521,24 @@ var SUPPORTED_CARD_TYPES = {
|
|
|
6183
6521
|
EFTPOS_WHITE: 'eftpos_white',
|
|
6184
6522
|
UNIONPAY: 'unionpay'
|
|
6185
6523
|
};
|
|
6524
|
+
/**
|
|
6525
|
+
* Card schemes the widget is able to resolve from a card number, and therefore the only values
|
|
6526
|
+
* `setAllowedCardSchemes` accepts. This is a separate vocabulary from SUPPORTED_CARD_TYPES, which drives the
|
|
6527
|
+
* card icons: it omits the icon-only variants `visa_white` / `eftpos_white`, and the `laser` / `maestro`
|
|
6528
|
+
* identifiers the widget can never resolve — allowing those would be configuration that can never match.
|
|
6529
|
+
*/
|
|
6530
|
+
var CARD_SCHEME = {
|
|
6531
|
+
AMEX: 'amex',
|
|
6532
|
+
AUSBC: 'ausbc',
|
|
6533
|
+
DINERS: 'diners',
|
|
6534
|
+
DISCOVER: 'discover',
|
|
6535
|
+
EFTPOS: 'eftpos',
|
|
6536
|
+
JAPCB: 'japcb',
|
|
6537
|
+
MASTERCARD: 'mastercard',
|
|
6538
|
+
SOLO: 'solo',
|
|
6539
|
+
UNIONPAY: 'unionpay',
|
|
6540
|
+
VISA: 'visa'
|
|
6541
|
+
};
|
|
6186
6542
|
var SUPPORTED_CHECKOUT_META_COLLECTION = [].concat([
|
|
6187
6543
|
// Paypal
|
|
6188
6544
|
'brand_name', 'cart_border_color', 'reference', 'email', 'hdr_img', 'logo_img', 'pay_flow_color', 'first_name', 'last_name', 'address_line', 'address_line2', 'address_city', 'address_state', 'address_postcode', 'address_country', 'phone', 'hide_shipping_address'], [
|
|
@@ -8367,7 +8723,7 @@ var AfterPayWalletService = /*#__PURE__*/function (_WalletService) {
|
|
|
8367
8723
|
}]);
|
|
8368
8724
|
}(WalletService);
|
|
8369
8725
|
|
|
8370
|
-
/*! @license DOMPurify 3.4.
|
|
8726
|
+
/*! @license DOMPurify 3.4.13 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/3.4.13/LICENSE */
|
|
8371
8727
|
|
|
8372
8728
|
var purify_cjs;
|
|
8373
8729
|
var hasRequiredPurify_cjs;
|
|
@@ -8687,7 +9043,7 @@ function requirePurify_cjs () {
|
|
|
8687
9043
|
const text = freeze(['#text']);
|
|
8688
9044
|
|
|
8689
9045
|
const html = freeze(['accept', 'action', 'align', 'alt', 'autocapitalize', 'autocomplete', 'autopictureinpicture', 'autoplay', 'background', 'bgcolor', 'border', 'capture', 'cellpadding', 'cellspacing', 'checked', 'cite', 'class', 'clear', 'color', 'cols', 'colspan', 'command', 'commandfor', 'controls', 'controlslist', 'coords', 'crossorigin', 'datetime', 'decoding', 'default', 'dir', 'disabled', 'disablepictureinpicture', 'disableremoteplayback', 'download', 'draggable', 'enctype', 'enterkeyhint', 'exportparts', 'face', 'for', 'headers', 'height', 'hidden', 'high', 'href', 'hreflang', 'id', 'inert', 'inputmode', 'integrity', 'ismap', 'kind', 'label', 'lang', 'list', 'loading', 'loop', 'low', 'max', 'maxlength', 'media', 'method', 'min', 'minlength', 'multiple', 'muted', 'name', 'nonce', 'noshade', 'novalidate', 'nowrap', 'open', 'optimum', 'part', 'pattern', 'placeholder', 'playsinline', 'popover', 'popovertarget', 'popovertargetaction', 'poster', 'preload', 'pubdate', 'radiogroup', 'readonly', 'rel', 'required', 'rev', 'reversed', 'role', 'rows', 'rowspan', 'spellcheck', 'scope', 'selected', 'shape', 'size', 'sizes', 'slot', 'span', 'srclang', 'start', 'src', 'srcset', 'step', 'style', 'summary', 'tabindex', 'title', 'translate', 'type', 'usemap', 'valign', 'value', 'width', 'wrap', 'xmlns']);
|
|
8690
|
-
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
9046
|
+
const svg = freeze(['accent-height', 'accumulate', 'additive', 'alignment-baseline', 'amplitude', 'ascent', 'attributename', 'attributetype', 'azimuth', 'basefrequency', 'baseline-shift', 'begin', 'bias', 'by', 'class', 'clip', 'clippathunits', 'clip-path', 'clip-rule', 'color', 'color-interpolation', 'color-interpolation-filters', 'color-profile', 'color-rendering', 'cx', 'cy', 'd', 'dx', 'dy', 'diffuseconstant', 'direction', 'display', 'divisor', 'dominant-baseline', 'dur', 'edgemode', 'elevation', 'end', 'exponent', 'fill', 'fill-opacity', 'fill-rule', 'filter', 'filterunits', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style', 'font-variant', 'font-weight', 'fx', 'fy', 'g1', 'g2', 'glyph-name', 'glyphref', 'gradientunits', 'gradienttransform', 'height', 'href', 'id', 'image-rendering', 'in', 'in2', 'intercept', 'k', 'k1', 'k2', 'k3', 'k4', 'kerning', 'keypoints', 'keysplines', 'keytimes', 'lang', 'lengthadjust', 'letter-spacing', 'kernelmatrix', 'kernelunitlength', 'lighting-color', 'local', 'marker-end', 'marker-mid', 'marker-start', 'markerheight', 'markerunits', 'markerwidth', 'maskcontentunits', 'maskunits', 'max', 'mask', 'mask-type', 'media', 'method', 'mode', 'min', 'name', 'numoctaves', 'offset', 'operator', 'opacity', 'order', 'orient', 'orientation', 'origin', 'overflow', 'paint-order', 'path', 'pathlength', 'patterncontentunits', 'patterntransform', 'patternunits', 'points', 'preservealpha', 'preserveaspectratio', 'primitiveunits', 'r', 'rx', 'ry', 'radius', 'refx', 'refy', 'repeatcount', 'repeatdur', 'restart', 'result', 'rotate', 'scale', 'seed', 'shape-rendering', 'slope', 'specularconstant', 'specularexponent', 'spreadmethod', 'startoffset', 'stddeviation', 'stitchtiles', 'stop-color', 'stop-opacity', 'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke', 'stroke-width', 'style', 'surfacescale', 'systemlanguage', 'tabindex', 'tablevalues', 'targetx', 'targety', 'transform', 'transform-origin', 'text-anchor', 'text-decoration', 'text-orientation', 'text-rendering', 'textlength', 'type', 'u1', 'u2', 'unicode', 'values', 'viewbox', 'visibility', 'version', 'vert-adv-y', 'vert-origin-x', 'vert-origin-y', 'width', 'word-spacing', 'wrap', 'writing-mode', 'xchannelselector', 'ychannelselector', 'x', 'x1', 'x2', 'xmlns', 'y', 'y1', 'y2', 'z', 'zoomandpan']);
|
|
8691
9047
|
const mathMl = freeze(['accent', 'accentunder', 'align', 'bevelled', 'close', 'columnalign', 'columnlines', 'columnspacing', 'columnspan', 'denomalign', 'depth', 'dir', 'display', 'displaystyle', 'encoding', 'fence', 'frame', 'height', 'href', 'id', 'largeop', 'length', 'linethickness', 'lquote', 'lspace', 'mathbackground', 'mathcolor', 'mathsize', 'mathvariant', 'maxsize', 'minsize', 'movablelimits', 'notation', 'numalign', 'open', 'rowalign', 'rowlines', 'rowspacing', 'rowspan', 'rspace', 'rquote', 'scriptlevel', 'scriptminsize', 'scriptsizemultiplier', 'selection', 'separator', 'separators', 'stretchy', 'subscriptshift', 'supscriptshift', 'symmetric', 'voffset', 'width', 'xmlns']);
|
|
8692
9048
|
const xml = freeze(['xlink:href', 'xml:id', 'xlink:title', 'xml:space', 'xmlns:xlink']);
|
|
8693
9049
|
|
|
@@ -8800,7 +9156,7 @@ function requirePurify_cjs () {
|
|
|
8800
9156
|
function createDOMPurify() {
|
|
8801
9157
|
let window = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : getGlobal();
|
|
8802
9158
|
const DOMPurify = root => createDOMPurify(root);
|
|
8803
|
-
DOMPurify.version = '3.4.
|
|
9159
|
+
DOMPurify.version = '3.4.13';
|
|
8804
9160
|
DOMPurify.removed = [];
|
|
8805
9161
|
if (!window || !window.document || window.document.nodeType !== NODE_TYPE.document || !window.Element) {
|
|
8806
9162
|
// Not running in a browser, provide a factory function
|
|
@@ -8831,6 +9187,7 @@ function requirePurify_cjs () {
|
|
|
8831
9187
|
const getAttributes = lookupGetter(ElementPrototype, 'attributes');
|
|
8832
9188
|
const getNodeType = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeType') : null;
|
|
8833
9189
|
const getNodeName = Node && Node.prototype ? lookupGetter(Node.prototype, 'nodeName') : null;
|
|
9190
|
+
const getOwnerDocument = Node && Node.prototype ? lookupGetter(Node.prototype, 'ownerDocument') : null;
|
|
8834
9191
|
// As per issue #47, the web-components registry is inherited by a
|
|
8835
9192
|
// new document created via createHTMLDocument. As per the spec
|
|
8836
9193
|
// (http://w3c.github.io/webcomponents/spec/custom/#creating-and-passing-registries)
|
|
@@ -9482,6 +9839,13 @@ function requirePurify_cjs () {
|
|
|
9482
9839
|
* @param root the in-place root to empty
|
|
9483
9840
|
*/
|
|
9484
9841
|
const _neutralizeRoot = function _neutralizeRoot(root) {
|
|
9842
|
+
/* Strip every disallowed attribute (on* handlers included) off the whole
|
|
9843
|
+
subtree BEFORE detaching anything. Detaching first would hand back
|
|
9844
|
+
handler-bearing originals (e.g. an already-loading `<img onerror>`)
|
|
9845
|
+
whose queued resource event still fires in page scope after we throw.
|
|
9846
|
+
Clobber-safe reads; a doomed clobbered node's own attributes are
|
|
9847
|
+
irrelevant while its non-clobbered descendants are reached and scrubbed. */
|
|
9848
|
+
_neutralizeSubtree(root);
|
|
9485
9849
|
const childNodes = getChildNodes(root);
|
|
9486
9850
|
if (childNodes) {
|
|
9487
9851
|
const snapshot = [];
|
|
@@ -9609,6 +9973,82 @@ function requirePurify_cjs () {
|
|
|
9609
9973
|
}
|
|
9610
9974
|
}
|
|
9611
9975
|
};
|
|
9976
|
+
/**
|
|
9977
|
+
* _neutralizePatchLinkage
|
|
9978
|
+
*
|
|
9979
|
+
* IN_PLACE entry pre-pass (declarative-partial-updates / streaming
|
|
9980
|
+
* hardening, https://github.com/WICG/declarative-partial-updates).
|
|
9981
|
+
*
|
|
9982
|
+
* The main walk strips patch linkage (`for`/`patchsrc`) and removes range
|
|
9983
|
+
* markers (PIs / markup comments) node-by-node, in document order, AS it
|
|
9984
|
+
* reaches each node. On a live in-place root that leaves a window: from the
|
|
9985
|
+
* moment the root is connected until the walk arrives at a given node, that
|
|
9986
|
+
* node's linkage is live. A patch applied on connection/stream can fire as
|
|
9987
|
+
* a microtask during the walk and inject or teleport an unsanitized DOM
|
|
9988
|
+
* range into a region the iterator has already passed and will not revisit,
|
|
9989
|
+
* so the post-return "tree is sanitized" contract is violated. Sweep the
|
|
9990
|
+
* whole tree once up front and sever every linkage before the walk begins,
|
|
9991
|
+
* closing that window.
|
|
9992
|
+
*
|
|
9993
|
+
* This CANNOT undo a patch that already fired before sanitize ran — that is
|
|
9994
|
+
* the irreducible "do not IN_PLACE a live-connected attacker tree" caveat —
|
|
9995
|
+
* but it closes everything from sanitize-start onward. Gated on SAFE_FOR_XML
|
|
9996
|
+
* to group with the rest of the declarative-partial-updates handling and
|
|
9997
|
+
* stay overridable, consistent with the codebase.
|
|
9998
|
+
*
|
|
9999
|
+
* Clobber-safe traversal (cached childNodes getter); per-node try/catch so a
|
|
10000
|
+
* clobbered root cannot defeat the sweep of its non-clobbered descendants.
|
|
10001
|
+
*
|
|
10002
|
+
* NOTE (pending real-Chrome confirmation, see test/declarative-patch-probe
|
|
10003
|
+
* .html Q1): this mirrors the existing policy of keeping `for` on
|
|
10004
|
+
* <label>/<output>. If the shipping feature can drive a patch through a
|
|
10005
|
+
* surviving `for`-on-label/output + `id` pair, this pre-pass and the
|
|
10006
|
+
* attribute check at _isBasicCustomElement's caller must additionally drop
|
|
10007
|
+
* that pair on the IN_PLACE path. Left as-is until the taxonomy is verified.
|
|
10008
|
+
*
|
|
10009
|
+
* @param root the in-place root to sweep
|
|
10010
|
+
*/
|
|
10011
|
+
const _neutralizePatchLinkage = function _neutralizePatchLinkage(root) {
|
|
10012
|
+
if (!SAFE_FOR_XML) {
|
|
10013
|
+
return;
|
|
10014
|
+
}
|
|
10015
|
+
const stack = [root];
|
|
10016
|
+
while (stack.length > 0) {
|
|
10017
|
+
const node = stack.pop();
|
|
10018
|
+
const nodeType = getNodeType ? getNodeType(node) : node.nodeType;
|
|
10019
|
+
/* Remove range markers (the target side of a patch linkage): every
|
|
10020
|
+
processing instruction, and any markup-bearing comment. */
|
|
10021
|
+
if (nodeType === NODE_TYPE.processingInstruction || nodeType === NODE_TYPE.comment && regExpTest(COMMENT_MARKUP_PROBE, node.data)) {
|
|
10022
|
+
try {
|
|
10023
|
+
remove(node);
|
|
10024
|
+
} catch (_) {
|
|
10025
|
+
/* Best-effort */
|
|
10026
|
+
}
|
|
10027
|
+
continue;
|
|
10028
|
+
}
|
|
10029
|
+
/* Strip patch-source attributes (the source side) off elements. */
|
|
10030
|
+
if (nodeType === NODE_TYPE.element) {
|
|
10031
|
+
const element = node;
|
|
10032
|
+
const lcTag = transformCaseFunc(getNodeName ? getNodeName(node) : node.nodeName);
|
|
10033
|
+
try {
|
|
10034
|
+
if (element.hasAttribute && element.hasAttribute('patchsrc')) {
|
|
10035
|
+
element.removeAttribute('patchsrc');
|
|
10036
|
+
}
|
|
10037
|
+
if (element.hasAttribute && element.hasAttribute('for') && lcTag !== 'label' && lcTag !== 'output') {
|
|
10038
|
+
element.removeAttribute('for');
|
|
10039
|
+
}
|
|
10040
|
+
} catch (_) {
|
|
10041
|
+
/* Clobbered removeAttribute/hasAttribute on a doomed node — ignore */
|
|
10042
|
+
}
|
|
10043
|
+
}
|
|
10044
|
+
const childNodes = getChildNodes(node);
|
|
10045
|
+
if (childNodes) {
|
|
10046
|
+
for (let i = childNodes.length - 1; i >= 0; --i) {
|
|
10047
|
+
stack.push(childNodes[i]);
|
|
10048
|
+
}
|
|
10049
|
+
}
|
|
10050
|
+
}
|
|
10051
|
+
};
|
|
9612
10052
|
/**
|
|
9613
10053
|
* _initDocument
|
|
9614
10054
|
*
|
|
@@ -9666,7 +10106,17 @@ function requirePurify_cjs () {
|
|
|
9666
10106
|
* @return The created NodeIterator
|
|
9667
10107
|
*/
|
|
9668
10108
|
const _createNodeIterator = function _createNodeIterator(root) {
|
|
9669
|
-
|
|
10109
|
+
/* Read ownerDocument through the cached Node.prototype getter, never the
|
|
10110
|
+
direct property. HTMLFormElement has [LegacyOverrideBuiltIns], so a
|
|
10111
|
+
clobbering child (<input name="ownerDocument"> or a form-associated
|
|
10112
|
+
external input) shadows the prototype getter and makes a direct read
|
|
10113
|
+
return that <input>. createNodeIterator.call(<input>, ...) then throws
|
|
10114
|
+
"Illegal invocation", and on the IN_PLACE path that throw lands before
|
|
10115
|
+
the walk's fail-closed barrier - leaving the caller's live tree, with
|
|
10116
|
+
any already-armed handler in it, un-neutralized. The cached getter
|
|
10117
|
+
returns the real Document regardless of the clobber. */
|
|
10118
|
+
const doc = getOwnerDocument ? getOwnerDocument(root) : root.ownerDocument;
|
|
10119
|
+
return createNodeIterator.call(doc || root, root,
|
|
9670
10120
|
// eslint-disable-next-line no-bitwise
|
|
9671
10121
|
NodeFilter.SHOW_ELEMENT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_TEXT | NodeFilter.SHOW_PROCESSING_INSTRUCTION | NodeFilter.SHOW_CDATA_SECTION, null);
|
|
9672
10122
|
};
|
|
@@ -9706,7 +10156,11 @@ function requirePurify_cjs () {
|
|
|
9706
10156
|
const _scrubTemplateExpressions2 = function _scrubTemplateExpressions(node) {
|
|
9707
10157
|
var _node$querySelectorAl;
|
|
9708
10158
|
node.normalize();
|
|
9709
|
-
|
|
10159
|
+
/* Clobber-safe ownerDocument read, same reasoning as _createNodeIterator:
|
|
10160
|
+
under SAFE_FOR_TEMPLATES this runs on the live IN_PLACE root, which may
|
|
10161
|
+
carry a form-named-getter override of ownerDocument. */
|
|
10162
|
+
const doc = getOwnerDocument ? getOwnerDocument(node) : node.ownerDocument;
|
|
10163
|
+
const walker = createNodeIterator.call(doc || node, node,
|
|
9710
10164
|
// eslint-disable-next-line no-bitwise
|
|
9711
10165
|
NodeFilter.SHOW_TEXT | NodeFilter.SHOW_COMMENT | NodeFilter.SHOW_CDATA_SECTION | NodeFilter.SHOW_PROCESSING_INSTRUCTION, null);
|
|
9712
10166
|
let currentNode = walker.nextNode();
|
|
@@ -9859,15 +10313,21 @@ function requirePurify_cjs () {
|
|
|
9859
10313
|
/**
|
|
9860
10314
|
* Handle a node whose tag is forbidden or not allowlisted: keep
|
|
9861
10315
|
* allowed custom elements (false return exits _sanitizeElements
|
|
9862
|
-
* early - namespace
|
|
9863
|
-
*
|
|
9864
|
-
*
|
|
10316
|
+
* early - the namespace and fallback-tag removal checks are
|
|
10317
|
+
* intentionally skipped for kept custom elements), else hoist
|
|
10318
|
+
* content per KEEP_CONTENT and remove.
|
|
10319
|
+
*
|
|
10320
|
+
* A kept custom element is the ONLY case in which this function
|
|
10321
|
+
* returns false, so the caller uses that return value to run the
|
|
10322
|
+
* afterSanitizeElements hook on the kept element and keep the
|
|
10323
|
+
* element-hook lifecycle consistent with normal allowlisted
|
|
10324
|
+
* elements (GHSA-c2j3-45gr-mqc4).
|
|
9865
10325
|
*
|
|
9866
10326
|
* @param currentNode the disallowed node
|
|
9867
10327
|
* @param tagName the node's transformCaseFunc'd tag name
|
|
9868
10328
|
* @return true if the node was removed, false if kept
|
|
9869
10329
|
*/
|
|
9870
|
-
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName) {
|
|
10330
|
+
const _sanitizeDisallowedNode = function _sanitizeDisallowedNode(currentNode, tagName, root) {
|
|
9871
10331
|
/* Check if we have a custom element to handle */
|
|
9872
10332
|
if (!FORBID_TAGS[tagName] && _isBasicCustomElement(tagName)) {
|
|
9873
10333
|
if (CUSTOM_ELEMENT_HANDLING.tagNameCheck instanceof RegExp && regExpTest(CUSTOM_ELEMENT_HANDLING.tagNameCheck, tagName)) {
|
|
@@ -9890,27 +10350,26 @@ function requirePurify_cjs () {
|
|
|
9890
10350
|
const childNodes = getChildNodes(currentNode);
|
|
9891
10351
|
if (childNodes && parentNode) {
|
|
9892
10352
|
const childCount = childNodes.length;
|
|
9893
|
-
/*
|
|
9894
|
-
|
|
9895
|
-
|
|
9896
|
-
|
|
9897
|
-
|
|
9898
|
-
|
|
9899
|
-
|
|
9900
|
-
|
|
9901
|
-
|
|
9902
|
-
|
|
9903
|
-
|
|
9904
|
-
|
|
9905
|
-
|
|
9906
|
-
|
|
9907
|
-
|
|
9908
|
-
event to neutralise.
|
|
10353
|
+
/* Hoist by moving each child up one level rather than deep-cloning
|
|
10354
|
+
it. Moving transfers every descendant exactly once, so a chain of
|
|
10355
|
+
nested disallowed elements costs O(n) instead of the O(n^2) that
|
|
10356
|
+
re-cloning the shrinking subtree at each level produced; it also
|
|
10357
|
+
empties the removed original, so `DOMPurify.removed` no longer
|
|
10358
|
+
pins whole subtrees. Moving preserves the in-place guarantee too:
|
|
10359
|
+
an original carrying already-queued resource events (`<img
|
|
10360
|
+
onerror>`, `<video>`/`<audio>` error, lazy/`onload`, …) is
|
|
10361
|
+
relocated and sanitised rather than left detached but still armed.
|
|
10362
|
+
The sole case that must clone is removing the walk root itself.
|
|
10363
|
+
The result is serialised from the root's subtree, so a restrictive
|
|
10364
|
+
ALLOWED_TAGS that strips the root (`body` on the string path) must
|
|
10365
|
+
leave the content inside it, which only cloning does. In IN_PLACE
|
|
10366
|
+
the root is pre-validated as an allowed tag and so is never removed
|
|
10367
|
+
here, so that path always takes the move branch.
|
|
9909
10368
|
`childNodes` is live; a tail-to-head walk keeps `childNodes[i]`
|
|
9910
10369
|
valid whether we move (drops the trailing entry) or clone (leaves
|
|
9911
10370
|
the list intact). */
|
|
9912
10371
|
for (let i = childCount - 1; i >= 0; --i) {
|
|
9913
|
-
const hoisted =
|
|
10372
|
+
const hoisted = currentNode === root ? cloneNode(childNodes[i], true) : childNodes[i];
|
|
9914
10373
|
parentNode.insertBefore(hoisted, getNextSibling(currentNode));
|
|
9915
10374
|
}
|
|
9916
10375
|
}
|
|
@@ -9918,6 +10377,26 @@ function requirePurify_cjs () {
|
|
|
9918
10377
|
_forceRemove(currentNode);
|
|
9919
10378
|
return true;
|
|
9920
10379
|
};
|
|
10380
|
+
/**
|
|
10381
|
+
* Fork a hook-mutable allowlist off its shared binding the first time a
|
|
10382
|
+
* (possibly lazily-installed) uponSanitize* hook is about to see it, so the
|
|
10383
|
+
* hook cannot widen the per-instance default or the setConfig binding by
|
|
10384
|
+
* reference and leak past the call. Returns the set unchanged once it is
|
|
10385
|
+
* already call-local, so repeated calls across elements are idempotent.
|
|
10386
|
+
*
|
|
10387
|
+
* @param hookList the uponSanitize* hook array for this event
|
|
10388
|
+
* @param set the current ALLOWED_TAGS / ALLOWED_ATTR binding
|
|
10389
|
+
* @param defaultSet the per-instance DEFAULT_ALLOWED_* constant
|
|
10390
|
+
* @param setConfigSet the captured setConfig() binding, or null
|
|
10391
|
+
* @return a call-local clone if a hook is present and set is still shared,
|
|
10392
|
+
* else set unchanged
|
|
10393
|
+
*/
|
|
10394
|
+
const _forkSharedAllowlist = function _forkSharedAllowlist(hookList, set, defaultSet, setConfigSet) {
|
|
10395
|
+
if (hookList.length === 0) {
|
|
10396
|
+
return set;
|
|
10397
|
+
}
|
|
10398
|
+
return set === defaultSet || set === setConfigSet ? clone(set) : set;
|
|
10399
|
+
};
|
|
9921
10400
|
/**
|
|
9922
10401
|
* _sanitizeElements
|
|
9923
10402
|
*
|
|
@@ -9927,9 +10406,20 @@ function requirePurify_cjs () {
|
|
|
9927
10406
|
* @param currentNode to check for permission to exist
|
|
9928
10407
|
* @return true if node was killed, false if left alive
|
|
9929
10408
|
*/
|
|
9930
|
-
|
|
10409
|
+
// eslint-disable-next-line complexity
|
|
10410
|
+
const _sanitizeElements = function _sanitizeElements(currentNode, root) {
|
|
9931
10411
|
/* Execute a hook if present */
|
|
9932
10412
|
_executeHooks(hooks.beforeSanitizeElements, currentNode, null);
|
|
10413
|
+
/* A hook may have detached the node - treat it as removed (see the
|
|
10414
|
+
detached-node comment after the uponSanitizeElement hook below). On
|
|
10415
|
+
the IN_PLACE path, neutralize the detached subtree first so a queued
|
|
10416
|
+
resource handler on it cannot fire in page scope after we return. */
|
|
10417
|
+
if (currentNode !== root && getParentNode(currentNode) === null) {
|
|
10418
|
+
if (IN_PLACE) {
|
|
10419
|
+
_neutralizeSubtree(currentNode);
|
|
10420
|
+
}
|
|
10421
|
+
return true;
|
|
10422
|
+
}
|
|
9933
10423
|
/* Check if element is clobbered or can clobber */
|
|
9934
10424
|
if (_isClobbered(currentNode)) {
|
|
9935
10425
|
_forceRemove(currentNode);
|
|
@@ -9937,11 +10427,47 @@ function requirePurify_cjs () {
|
|
|
9937
10427
|
}
|
|
9938
10428
|
/* Now let's check the element's type and name */
|
|
9939
10429
|
const tagName = transformCaseFunc(getNodeName ? getNodeName(currentNode) : currentNode.nodeName);
|
|
10430
|
+
/* Close the pre-walk clone-guard's timing gap: an uponSanitizeElement
|
|
10431
|
+
hook may have been installed after that guard sampled the hook arrays
|
|
10432
|
+
(e.g. lazily from beforeSanitizeElements), leaving ALLOWED_TAGS still
|
|
10433
|
+
aliasing a shared binding that a widening hook would mutate by
|
|
10434
|
+
reference. Fork it before exposing it to the hook. */
|
|
10435
|
+
ALLOWED_TAGS = _forkSharedAllowlist(hooks.uponSanitizeElement, ALLOWED_TAGS, DEFAULT_ALLOWED_TAGS, SET_CONFIG_ALLOWED_TAGS);
|
|
9940
10436
|
/* Execute a hook if present */
|
|
9941
10437
|
_executeHooks(hooks.uponSanitizeElement, currentNode, {
|
|
9942
10438
|
tagName,
|
|
9943
10439
|
allowedTags: ALLOWED_TAGS
|
|
9944
10440
|
});
|
|
10441
|
+
/* A hook may have detached the node from the tree — a long-standing
|
|
10442
|
+
user pattern (issue #469; draw.io-style foreignObject filtering).
|
|
10443
|
+
Per the cached, unclobberable parentNode getter the node is
|
|
10444
|
+
genuinely out of the tree, so it can reach neither the serialized
|
|
10445
|
+
output nor an IN_PLACE live tree; treat it as removed and stop
|
|
10446
|
+
processing it. Without this guard, the unsafe-node / namespace
|
|
10447
|
+
checks below would call _forceRemove on a parentless node and hit
|
|
10448
|
+
the REPORT-3 fail-closed throw — which exists for nodes DOMPurify
|
|
10449
|
+
wants gone but *cannot* detach (clobbered / parentless roots), the
|
|
10450
|
+
opposite of a node that is already safely gone. The walk root is
|
|
10451
|
+
exempt: a detached IN_PLACE root is legitimate input and must still
|
|
10452
|
+
be fully sanitized, and a kill-decision on it must keep hitting the
|
|
10453
|
+
REPORT-3 throw. Nodes detached by hooks stay the hook's
|
|
10454
|
+
responsibility for placement: they are not recorded in
|
|
10455
|
+
DOMPurify.removed, so the post-walk IN_PLACE pass (which iterates
|
|
10456
|
+
DOMPurify.removed) does not reach them. But a hook-detached subtree
|
|
10457
|
+
can still hold a queued resource-event handler - e.g. an <img onload>
|
|
10458
|
+
that began loading when the caller built the live tree - which fires
|
|
10459
|
+
in page scope after sanitize returns even though the handler never
|
|
10460
|
+
reached the returned tree. That is the audit-5 F1 hazard, and the
|
|
10461
|
+
documented node.remove() hook pattern walks straight into it. So on
|
|
10462
|
+
the IN_PLACE path we neutralize the detached subtree inline here,
|
|
10463
|
+
stripping its non-allow-listed attributes before returning, exactly
|
|
10464
|
+
as the post-walk pass does for _forceRemove'd subtrees. */
|
|
10465
|
+
if (currentNode !== root && getParentNode(currentNode) === null) {
|
|
10466
|
+
if (IN_PLACE) {
|
|
10467
|
+
_neutralizeSubtree(currentNode);
|
|
10468
|
+
}
|
|
10469
|
+
return true;
|
|
10470
|
+
}
|
|
9945
10471
|
/* Remove mXSS vectors, processing instructions and risky comments */
|
|
9946
10472
|
if (_isUnsafeNode(currentNode, tagName)) {
|
|
9947
10473
|
_forceRemove(currentNode);
|
|
@@ -9949,7 +10475,22 @@ function requirePurify_cjs () {
|
|
|
9949
10475
|
}
|
|
9950
10476
|
/* Remove element if anything forbids its presence */
|
|
9951
10477
|
if (FORBID_TAGS[tagName] || !(EXTRA_ELEMENT_HANDLING.tagCheck instanceof Function && EXTRA_ELEMENT_HANDLING.tagCheck(tagName)) && !ALLOWED_TAGS[tagName]) {
|
|
9952
|
-
|
|
10478
|
+
const removed = _sanitizeDisallowedNode(currentNode, tagName, root);
|
|
10479
|
+
/* A false return means the node is a custom element kept via
|
|
10480
|
+
CUSTOM_ELEMENT_HANDLING - the only keep path through
|
|
10481
|
+
_sanitizeDisallowedNode. Run afterSanitizeElements on it so the
|
|
10482
|
+
element-hook lifecycle matches normal allowlisted elements: a
|
|
10483
|
+
security policy applied in this hook (e.g. stripping an attribute
|
|
10484
|
+
from every surviving element) must not silently skip kept custom
|
|
10485
|
+
elements (GHSA-c2j3-45gr-mqc4). This mirrors the normal-element
|
|
10486
|
+
tail below - the hook runs, then the walker's subsequent
|
|
10487
|
+
_sanitizeAttributes pass sanitizes the element's attributes. The
|
|
10488
|
+
deliberately skipped namespace and fallback-tag removal checks stay
|
|
10489
|
+
skipped; they are removal decisions, not the hook contract. */
|
|
10490
|
+
if (removed === false) {
|
|
10491
|
+
_executeHooks(hooks.afterSanitizeElements, currentNode, null);
|
|
10492
|
+
}
|
|
10493
|
+
return removed;
|
|
9953
10494
|
}
|
|
9954
10495
|
/* Check whether element has a valid namespace.
|
|
9955
10496
|
Realm-safe check (GHSA-hpcv-96wg-7vj8): use the cached Node.prototype
|
|
@@ -9996,6 +10537,33 @@ function requirePurify_cjs () {
|
|
|
9996
10537
|
if (FORBID_ATTR[lcName]) {
|
|
9997
10538
|
return false;
|
|
9998
10539
|
}
|
|
10540
|
+
/* Reject declarative-partial-updates patch-linkage attributes
|
|
10541
|
+
(https://github.com/WICG/declarative-partial-updates).
|
|
10542
|
+
Empirical note (Chrome 150, verified — see
|
|
10543
|
+
test/declarative-patch-probe-v3.html): expansion is NOT applied after
|
|
10544
|
+
sanitization. For the string path it fires during sanitize()'s own
|
|
10545
|
+
parse, so the walk sees and sanitizes the fully materialized expanded
|
|
10546
|
+
tree — teleports into MathML/SVG integration points included; a
|
|
10547
|
+
weaponized `<template for>`->`<img onerror>` comes back with the handler
|
|
10548
|
+
stripped. For the IN_PLACE path it fires on connection, before the walk.
|
|
10549
|
+
Either way DOMPurify is NOT blind to the patch.
|
|
10550
|
+
This removal is therefore defense-in-depth rather than the sole barrier:
|
|
10551
|
+
it prevents live linkage from surviving into the OUTPUT and re-expanding
|
|
10552
|
+
in the caller's context, and keeps behaviour deterministic if a future
|
|
10553
|
+
engine defers expansion. `for` is legitimate only on <label>/<output>;
|
|
10554
|
+
anywhere else (notably <template for>) it links the element to a patch
|
|
10555
|
+
target and teleports or removes an arbitrary DOM range by id/marker name.
|
|
10556
|
+
`patchsrc` fetches remote markup and is treated as a script-loading
|
|
10557
|
+
mechanism (CSP). Gated on SAFE_FOR_XML so the removal groups with the
|
|
10558
|
+
other structural-threat checks and stays overridable, consistent with
|
|
10559
|
+
the rest of the codebase. PI range markers are already removed by
|
|
10560
|
+
_isUnsafeNode. */
|
|
10561
|
+
if (SAFE_FOR_XML && lcName === 'patchsrc') {
|
|
10562
|
+
return false;
|
|
10563
|
+
}
|
|
10564
|
+
if (SAFE_FOR_XML && lcName === 'for' && lcTag !== 'label' && lcTag !== 'output') {
|
|
10565
|
+
return false;
|
|
10566
|
+
}
|
|
9999
10567
|
/* Make sure attribute cannot clobber */
|
|
10000
10568
|
if (SANITIZE_DOM && (lcName === 'id' || lcName === 'name') && (value in document || value in formElement)) {
|
|
10001
10569
|
return false;
|
|
@@ -10112,6 +10680,9 @@ function requirePurify_cjs () {
|
|
|
10112
10680
|
if (!attributes || _isClobbered(currentNode)) {
|
|
10113
10681
|
return;
|
|
10114
10682
|
}
|
|
10683
|
+
/* Same lazy-install guard as uponSanitizeElement (see there): fork the
|
|
10684
|
+
attribute allowlist off its shared binding before a hook can see it. */
|
|
10685
|
+
ALLOWED_ATTR = _forkSharedAllowlist(hooks.uponSanitizeAttribute, ALLOWED_ATTR, DEFAULT_ALLOWED_ATTR, SET_CONFIG_ALLOWED_ATTR);
|
|
10115
10686
|
const hookEvent = {
|
|
10116
10687
|
attrName: '',
|
|
10117
10688
|
attrValue: '',
|
|
@@ -10205,7 +10776,7 @@ function requirePurify_cjs () {
|
|
|
10205
10776
|
/* Execute a hook if present */
|
|
10206
10777
|
_executeHooks(hooks.uponSanitizeShadowNode, shadowNode, null);
|
|
10207
10778
|
/* Sanitize tags and elements */
|
|
10208
|
-
_sanitizeElements(shadowNode);
|
|
10779
|
+
_sanitizeElements(shadowNode, fragment);
|
|
10209
10780
|
/* Check attributes next */
|
|
10210
10781
|
_sanitizeAttributes(shadowNode);
|
|
10211
10782
|
/* Deep shadow DOM detected.
|
|
@@ -10396,6 +10967,11 @@ function requirePurify_cjs () {
|
|
|
10396
10967
|
keep using and whose return value they ignore — unsanitized. REPORT-2. */
|
|
10397
10968
|
const inPlace = IN_PLACE && typeof dirty !== 'string' && _isNode(dirty);
|
|
10398
10969
|
if (inPlace) {
|
|
10970
|
+
/* Declarative-partial-updates / streaming pre-pass: sever every patch
|
|
10971
|
+
linkage across the live tree BEFORE the walk, so no patch can fire
|
|
10972
|
+
mid-walk and inject into an already-processed region. Runs first, so
|
|
10973
|
+
it also covers the forbidden/clobbered roots that throw below. */
|
|
10974
|
+
_neutralizePatchLinkage(dirty);
|
|
10399
10975
|
/* Do some early pre-sanitization to avoid unsafe root nodes.
|
|
10400
10976
|
Read nodeName through the cached prototype getter — a clobbering
|
|
10401
10977
|
child named "nodeName" on the form root would otherwise shadow
|
|
@@ -10405,6 +10981,9 @@ function requirePurify_cjs () {
|
|
|
10405
10981
|
if (typeof nn === 'string') {
|
|
10406
10982
|
const tagName = transformCaseFunc(nn);
|
|
10407
10983
|
if (!ALLOWED_TAGS[tagName] || FORBID_TAGS[tagName]) {
|
|
10984
|
+
/* Fail closed on a live root: neutralize handlers/children before
|
|
10985
|
+
throwing, exactly as the mid-walk abort path does. */
|
|
10986
|
+
_neutralizeRoot(dirty);
|
|
10408
10987
|
throw typeErrorCreate('root node is forbidden and cannot be sanitized in-place');
|
|
10409
10988
|
}
|
|
10410
10989
|
}
|
|
@@ -10419,6 +10998,10 @@ function requirePurify_cjs () {
|
|
|
10419
10998
|
the application unsanitized. Refuse to sanitize such a root
|
|
10420
10999
|
the same way we refuse a forbidden tag. GHSA-r47g-fvhr-h676. */
|
|
10421
11000
|
if (_isClobbered(dirty)) {
|
|
11001
|
+
/* Fail closed on a live clobbered root before throwing.
|
|
11002
|
+
_neutralizeRoot's reads are clobber-safe (cached getters); the
|
|
11003
|
+
form's non-clobbered descendants, e.g. an armed <img>, are scrubbed. */
|
|
11004
|
+
_neutralizeRoot(dirty);
|
|
10422
11005
|
throw typeErrorCreate('root node is clobbered and cannot be sanitized in-place');
|
|
10423
11006
|
}
|
|
10424
11007
|
/* Sanitize attached shadow roots before the main iterator runs.
|
|
@@ -10471,20 +11054,24 @@ function requirePurify_cjs () {
|
|
|
10471
11054
|
_forceRemove(body.firstChild);
|
|
10472
11055
|
}
|
|
10473
11056
|
/* Get node iterator */
|
|
10474
|
-
const
|
|
11057
|
+
const walkRoot = inPlace ? dirty : body;
|
|
10475
11058
|
/* Now start iterating over the created document.
|
|
10476
11059
|
The walk runs inside an exception barrier (campaign-3 F2): a re-entrant
|
|
10477
11060
|
engine/custom-element mutation can detach a node mid-walk so
|
|
10478
11061
|
`_forceRemove`'s parentless guard throws, aborting the loop. Without the
|
|
10479
11062
|
barrier the caller's in-place tree would be left half-sanitized with the
|
|
10480
|
-
unvisited tail still armed.
|
|
10481
|
-
|
|
10482
|
-
|
|
10483
|
-
|
|
11063
|
+
unvisited tail still armed. _createNodeIterator itself is inside the
|
|
11064
|
+
barrier too: constructing the iterator dereferences the root's document,
|
|
11065
|
+
and any failure there (e.g. an exotic/clobbered root) must still fail
|
|
11066
|
+
closed rather than skip the neutralize. On any throw we fail closed -
|
|
11067
|
+
strip the in-place root bare - then rethrow so the existing throw
|
|
11068
|
+
contract is preserved. (String/DOM-copy paths never return the partial
|
|
11069
|
+
body, so the propagating throw is already fail-closed there.) */
|
|
10484
11070
|
try {
|
|
11071
|
+
const nodeIterator = _createNodeIterator(walkRoot);
|
|
10485
11072
|
while (currentNode = nodeIterator.nextNode()) {
|
|
10486
11073
|
/* Sanitize tags and elements */
|
|
10487
|
-
_sanitizeElements(currentNode);
|
|
11074
|
+
_sanitizeElements(currentNode, walkRoot);
|
|
10488
11075
|
/* Check attributes next */
|
|
10489
11076
|
_sanitizeAttributes(currentNode);
|
|
10490
11077
|
/* Shadow DOM detected, sanitize it.
|
|
@@ -10498,6 +11085,14 @@ function requirePurify_cjs () {
|
|
|
10498
11085
|
} catch (error) {
|
|
10499
11086
|
if (inPlace) {
|
|
10500
11087
|
_neutralizeRoot(dirty);
|
|
11088
|
+
/* Nodes _forceRemove'd earlier in the aborted walk are already
|
|
11089
|
+
detached from the root, so _neutralizeRoot's subtree pass does not
|
|
11090
|
+
reach them. Defuse them too, mirroring the success-path loop below. */
|
|
11091
|
+
arrayForEach(DOMPurify.removed, entry => {
|
|
11092
|
+
if (entry.element) {
|
|
11093
|
+
_neutralizeSubtree(entry.element);
|
|
11094
|
+
}
|
|
11095
|
+
});
|
|
10501
11096
|
}
|
|
10502
11097
|
throw error;
|
|
10503
11098
|
}
|
|
@@ -14924,6 +15519,54 @@ var MultiWidget = /*#__PURE__*/function () {
|
|
|
14924
15519
|
validate_card_types: validateCardNumberInput
|
|
14925
15520
|
});
|
|
14926
15521
|
}
|
|
15522
|
+
/**
|
|
15523
|
+
* Current method restricts which card schemes the card number input accepts, without changing which card
|
|
15524
|
+
* icons the widget displays. Scheme identifiers are matched case-insensitively and ignoring surrounding
|
|
15525
|
+
* whitespace; unsupported identifiers are ignored with a warning, so a list in which every identifier is
|
|
15526
|
+
* unsupported applies no restriction at all.
|
|
15527
|
+
*
|
|
15528
|
+
* Takes precedence over setSupportedCardIcons: when a non-empty list is supplied here, it is the only list
|
|
15529
|
+
* used for validation and the validateCardNumberInput flag of setSupportedCardIcons no longer affects it.
|
|
15530
|
+
* Calling this method more than once replaces the previous list rather than merging with it.
|
|
15531
|
+
*
|
|
15532
|
+
* Must be called before load().
|
|
15533
|
+
*
|
|
15534
|
+
* @example
|
|
15535
|
+
*
|
|
15536
|
+
* widget.setAllowedCardSchemes(['visa', 'mastercard']);
|
|
15537
|
+
*
|
|
15538
|
+
* @param {string[]} schemes - [CARD_SCHEME]{@link CARD_SCHEME}
|
|
15539
|
+
*/
|
|
15540
|
+
}, {
|
|
15541
|
+
key: "setAllowedCardSchemes",
|
|
15542
|
+
value: function setAllowedCardSchemes(schemes) {
|
|
15543
|
+
var vocabulary = ObjectHelper.values(CARD_SCHEME);
|
|
15544
|
+
var allowedSchemes = [];
|
|
15545
|
+
var _iterator = _createForOfIteratorHelper(Array.isArray(schemes) ? schemes : []),
|
|
15546
|
+
_step;
|
|
15547
|
+
try {
|
|
15548
|
+
for (_iterator.s(); !(_step = _iterator.n()).done;) {
|
|
15549
|
+
var entry = _step.value;
|
|
15550
|
+
var scheme = typeof entry === 'string' ? entry.trim().toLowerCase() : '';
|
|
15551
|
+
if (vocabulary.indexOf(scheme) === -1) {
|
|
15552
|
+
console.warn("Widget::setAllowedCardSchemes: unsupported card scheme ".concat(entry));
|
|
15553
|
+
continue;
|
|
15554
|
+
}
|
|
15555
|
+
if (allowedSchemes.indexOf(scheme) === -1) {
|
|
15556
|
+
allowedSchemes.push(scheme);
|
|
15557
|
+
}
|
|
15558
|
+
}
|
|
15559
|
+
} catch (err) {
|
|
15560
|
+
_iterator.e(err);
|
|
15561
|
+
} finally {
|
|
15562
|
+
_iterator.f();
|
|
15563
|
+
}
|
|
15564
|
+
if (allowedSchemes.length) {
|
|
15565
|
+
this.link.setParams({
|
|
15566
|
+
allowed_card_schemes: allowedSchemes.join(',')
|
|
15567
|
+
});
|
|
15568
|
+
}
|
|
15569
|
+
}
|
|
14927
15570
|
/**
|
|
14928
15571
|
* Current method can hide prevent the widget from showing the error messages
|
|
14929
15572
|
*
|
|
@@ -46175,6 +46818,7 @@ exports.Api = Api;
|
|
|
46175
46818
|
exports.ApplePayOpenWalletButton = ApplePayOpenWalletButton;
|
|
46176
46819
|
exports.ApplePayWalletButtonExpress = ApplePayWalletButtonExpress;
|
|
46177
46820
|
exports.CANVAS_3DS_EVENT = CANVAS_3DS_EVENT;
|
|
46821
|
+
exports.CARD_SCHEME = CARD_SCHEME;
|
|
46178
46822
|
exports.CHECKOUT_BUTTON_EVENT = CHECKOUT_BUTTON_EVENT;
|
|
46179
46823
|
exports.CHECKOUT_EVENT = EVENT$2;
|
|
46180
46824
|
exports.Canvas3ds = Canvas3ds;
|