@ozura/elements 1.2.4-next.53 → 1.2.4-next.55

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.
@@ -3,6 +3,41 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var react = require('react');
5
5
 
6
+ /******************************************************************************
7
+ Copyright (c) Microsoft Corporation.
8
+
9
+ Permission to use, copy, modify, and/or distribute this software for any
10
+ purpose with or without fee is hereby granted.
11
+
12
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
13
+ REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
14
+ AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
15
+ INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
16
+ LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
17
+ OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
18
+ PERFORMANCE OF THIS SOFTWARE.
19
+ ***************************************************************************** */
20
+ /* global Reflect, Promise, SuppressedError, Symbol, Iterator */
21
+
22
+
23
+ function __classPrivateFieldGet(receiver, state, kind, f) {
24
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
25
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
26
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
27
+ }
28
+
29
+ function __classPrivateFieldSet(receiver, state, value, kind, f) {
30
+ if (kind === "m") throw new TypeError("Private method is not writable");
31
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
32
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
33
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
34
+ }
35
+
36
+ typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
37
+ var e = new Error(message);
38
+ return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
39
+ };
40
+
6
41
  const THEME_DEFAULT = {
7
42
  base: {
8
43
  color: '#1a1a2e',
@@ -183,11 +218,24 @@ const OZ_ERROR_CODES = new Set(['network', 'timeout', 'auth', 'validation', 'ser
183
218
  function isOzErrorCode(value) {
184
219
  return typeof value === 'string' && OZ_ERROR_CODES.has(value);
185
220
  }
221
+ /**
222
+ * Strips PAN-shaped digit sequences from raw vault / Pay API error strings
223
+ * before they are stored on OzError.raw.
224
+ *
225
+ * The vault and Pay API should never echo card data in error messages — but
226
+ * this is a defense-in-depth layer. 13–19 digit runs (optionally separated
227
+ * by spaces or dashes) match every major card number format. Shorter digit
228
+ * runs (CVV, ZIP, HTTP status codes) are left untouched.
229
+ */
230
+ const RAW_PAN_RE = /\b\d{4}[\s-]?\d{4}[\s-]?\d{4}[\s-]?\d{1,7}\b|\b\d{13,19}\b/g;
231
+ function redactRawString(s) {
232
+ return s.replace(RAW_PAN_RE, '[REDACTED]');
233
+ }
186
234
  class OzError extends Error {
187
235
  constructor(message, raw, errorCode) {
188
236
  super(message);
189
237
  this.name = 'OzError';
190
- this.raw = raw !== null && raw !== void 0 ? raw : message;
238
+ this.raw = redactRawString(raw !== null && raw !== void 0 ? raw : message);
191
239
  this.errorCode = errorCode !== null && errorCode !== void 0 ? errorCode : 'unknown';
192
240
  this.retryable = this.errorCode === 'network' || this.errorCode === 'timeout' || this.errorCode === 'server';
193
241
  }
@@ -954,6 +1002,7 @@ function createSessionFetcher(url) {
954
1002
  };
955
1003
  }
956
1004
 
1005
+ var _OzVault_waxKey;
957
1006
  function isCardMetadata(v) {
958
1007
  if (!v || typeof v !== 'object')
959
1008
  return false;
@@ -997,6 +1046,11 @@ class OzVault {
997
1046
  */
998
1047
  constructor(options, waxKey, tokenizationSessionId) {
999
1048
  var _a, _b, _c, _d, _e;
1049
+ // Hard-private: JavaScript WeakMap-based enforcement (not just TypeScript
1050
+ // compile-time). Runtime code cannot read this via vault['waxKey'] or
1051
+ // (vault as any).waxKey — prevents wax key exfiltration if merchant-page
1052
+ // JS were somehow inspected at runtime (e.g. after an XSS).
1053
+ _OzVault_waxKey.set(this, '');
1000
1054
  this.elements = new Map();
1001
1055
  this.elementsByType = new Map();
1002
1056
  this.bankElementsByType = new Map();
@@ -1022,7 +1076,7 @@ class OzVault {
1022
1076
  this.loadErrorTimeoutId = null;
1023
1077
  // Proactive wax refresh on visibility restore after long idle
1024
1078
  this._hiddenAt = null;
1025
- this.waxKey = waxKey;
1079
+ __classPrivateFieldSet(this, _OzVault_waxKey, waxKey, "f");
1026
1080
  this.tokenizationSessionId = tokenizationSessionId;
1027
1081
  this.pubKey = options.pubKey;
1028
1082
  // Strip trailing slash so URL construction never produces double-slash paths
@@ -1134,8 +1188,8 @@ class OzVault {
1134
1188
  vault.destroy();
1135
1189
  throw new OzError('Session fetch returned an empty key. Check your session endpoint response — it must return { sessionKey: "..." }.');
1136
1190
  }
1137
- // Static methods can access private fields of instances of the same class.
1138
- vault.waxKey = waxKey;
1191
+ // Static methods can access hard-private fields of instances of the same class.
1192
+ __classPrivateFieldSet(vault, _OzVault_waxKey, waxKey, "f");
1139
1193
  vault._storedFetchWaxKey = resolvedFetchKey;
1140
1194
  // If the tokenizer iframe fired OZ_FRAME_READY before fetchWaxKey resolved,
1141
1195
  // the OZ_INIT sent at that point had an empty waxKey. Send a follow-up now
@@ -1652,7 +1706,7 @@ class OzVault {
1652
1706
  isReady: this.tokenizerReady,
1653
1707
  tokenizing: this._tokenizing,
1654
1708
  destroyed: this._destroyed,
1655
- waxKeyPresent: Boolean(this.waxKey),
1709
+ waxKeyPresent: Boolean(__classPrivateFieldGet(this, _OzVault_waxKey, "f")),
1656
1710
  tokenizeSuccessCount: this._tokenizeSuccessCount,
1657
1711
  maxTokenizeCalls: this._maxTokenizeCalls,
1658
1712
  resetCount: this._resetCount,
@@ -1670,6 +1724,17 @@ class OzVault {
1670
1724
  iframe.style.cssText = 'position:absolute;top:-9999px;left:-9999px;width:1px;height:1px;';
1671
1725
  iframe.setAttribute('aria-hidden', 'true');
1672
1726
  iframe.tabIndex = -1;
1727
+ // allow-scripts: JS runs. allow-same-origin: frame keeps its actual origin
1728
+ // (elements.ozura.com) so fetch() CORS requests carry the correct Origin
1729
+ // header. Without allow-same-origin the frame gets a null opaque origin and
1730
+ // the vault API's CORS policy would reject it.
1731
+ // NOT included: allow-top-navigation, allow-popups, allow-forms — prevents
1732
+ // a compromised tokenizer frame from navigating the merchant page or opening
1733
+ // popups even if the CDN bundle were somehow replaced.
1734
+ // Note: allow-scripts + allow-same-origin on a cross-origin iframe does NOT
1735
+ // expose window.parent — Same Origin Policy still applies between
1736
+ // elements.ozura.com and the merchant domain.
1737
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');
1673
1738
  const parentOrigin = typeof window !== 'undefined' ? window.location.origin : '';
1674
1739
  iframe.src = `${this.frameBaseUrl}/frame/tokenizer-frame.html#vaultId=${encodeURIComponent(this.vaultId)}${parentOrigin ? `&parentOrigin=${encodeURIComponent(parentOrigin)}` : ''}`;
1675
1740
  document.body.appendChild(iframe);
@@ -1805,7 +1870,7 @@ class OzVault {
1805
1870
  // Deliver the wax key via OZ_INIT so the tokenizer stores it internally.
1806
1871
  // If waxKey is still empty (fetchWaxKey hasn't resolved yet), it will be
1807
1872
  // sent again from create() once the key is available.
1808
- this.sendToTokenizer(Object.assign(Object.assign({ type: 'OZ_INIT', frameId: '__tokenizer__' }, (this.waxKey ? { waxKey: this.waxKey } : {})), { debug: this._debug }));
1873
+ this.sendToTokenizer(Object.assign(Object.assign({ type: 'OZ_INIT', frameId: '__tokenizer__' }, (__classPrivateFieldGet(this, _OzVault_waxKey, "f") ? { waxKey: __classPrivateFieldGet(this, _OzVault_waxKey, "f") } : {})), { debug: this._debug }));
1809
1874
  (_c = this._onReady) === null || _c === void 0 ? void 0 : _c.call(this);
1810
1875
  this.log('tokenizer iframe ready', { protocolVersion: (_d = msg.__ozVersion) !== null && _d !== void 0 ? _d : null });
1811
1876
  this.log('vault state', this.debugState());
@@ -2111,7 +2176,7 @@ class OzVault {
2111
2176
  throw new OzError('fetchWaxKey returned an empty string during auto-refresh.', undefined, 'auth');
2112
2177
  }
2113
2178
  if (!this._destroyed) {
2114
- this.waxKey = newWaxKey;
2179
+ __classPrivateFieldSet(this, _OzVault_waxKey, newWaxKey, "f");
2115
2180
  this.tokenizationSessionId = newSessionId;
2116
2181
  this._tokenizeSuccessCount = 0;
2117
2182
  }
@@ -2135,6 +2200,7 @@ class OzVault {
2135
2200
  (_a = this.tokenizerWindow) === null || _a === void 0 ? void 0 : _a.postMessage(msg, this.frameOrigin, transfer !== null && transfer !== void 0 ? transfer : []);
2136
2201
  }
2137
2202
  }
2203
+ _OzVault_waxKey = new WeakMap();
2138
2204
 
2139
2205
  const OzContext = react.createContext({
2140
2206
  vault: null,