@loafmarkets/ui 0.1.409 → 0.1.410

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/dist/index.mjs CHANGED
@@ -2644,6 +2644,22 @@ var PropertyTour = React5.forwardRef(
2644
2644
  }
2645
2645
  );
2646
2646
  PropertyTour.displayName = "PropertyTour";
2647
+
2648
+ // src/lib/isSafeUrl.ts
2649
+ var CONTROL_CHARS = /[\u0000-\u001F\u007F]/g;
2650
+ function isSafeUrl(url) {
2651
+ if (!url) return false;
2652
+ const cleaned = url.replace(CONTROL_CHARS, "").replace(/\\/g, "/").trim();
2653
+ if (cleaned === "") return false;
2654
+ if (cleaned.startsWith("//")) return false;
2655
+ if (cleaned.startsWith("/")) return true;
2656
+ try {
2657
+ const { protocol } = new URL(cleaned);
2658
+ return protocol === "http:" || protocol === "https:";
2659
+ } catch {
2660
+ return false;
2661
+ }
2662
+ }
2647
2663
  var ITEMS_PER_PAGE = 5;
2648
2664
  var ITEMS_PER_PAGE_MOBILE = 5;
2649
2665
  var ensureAnimationsInjected = () => {
@@ -2839,7 +2855,7 @@ function NewsArticleModal({ item, onClose }) {
2839
2855
  ] }),
2840
2856
  /* @__PURE__ */ jsx("div", { style: { height: "1px", background: "rgba(255,255,255,0.08)", marginBottom: "1.25rem" } }),
2841
2857
  item.summary ? /* @__PURE__ */ jsx("p", { style: { fontSize: "0.875rem", color: "rgba(255,255,255,0.8)", lineHeight: 1.75, margin: 0 }, children: item.summary }) : /* @__PURE__ */ jsx("p", { style: { fontSize: "0.8rem", color: "rgba(255,255,255,0.35)", fontStyle: "italic", margin: 0 }, children: "Summary not available for this article." }),
2842
- item.url && /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxs(
2858
+ item.url && isSafeUrl(item.url) && /* @__PURE__ */ jsx("div", { style: { marginTop: "1.25rem", paddingTop: "1rem", borderTop: "1px solid rgba(255,255,255,0.08)" }, children: /* @__PURE__ */ jsxs(
2843
2859
  "a",
2844
2860
  {
2845
2861
  href: item.url,
@@ -6615,6 +6631,16 @@ var PropertySubheader = React5.forwardRef(
6615
6631
  }
6616
6632
  );
6617
6633
  PropertySubheader.displayName = "PropertySubheader";
6634
+
6635
+ // src/lib/isTrustedTransakOrigin.ts
6636
+ function isTrustedTransakOrigin(eventOrigin, widgetUrl) {
6637
+ if (!widgetUrl) return false;
6638
+ try {
6639
+ return eventOrigin === new URL(widgetUrl).origin;
6640
+ } catch {
6641
+ return false;
6642
+ }
6643
+ }
6618
6644
  var DEFAULT_LOGO_SRC = Loaf_logo_Banner_default;
6619
6645
  var DEFAULT_LOGO_ALT = "Loaf";
6620
6646
  var OTP_INPUT_LENGTH = 6;
@@ -6755,7 +6781,7 @@ var LoginPopup = ({
6755
6781
  useEffect(() => {
6756
6782
  if (!transakWidgetUrl) return;
6757
6783
  const handleTransakMessage = (event) => {
6758
- if (!event.origin.includes("transak.com") && !event.origin.includes("global.transak.com")) {
6784
+ if (!isTrustedTransakOrigin(event.origin, transakWidgetUrl)) {
6759
6785
  return;
6760
6786
  }
6761
6787
  try {
@@ -12272,7 +12298,7 @@ function PropertyDocuments({ documentsData, highlightDocument, onClearHighlight
12272
12298
  const [previewTitle, setPreviewTitle] = useState(null);
12273
12299
  const backendDocuments = Array.isArray(documentsData?.documents) ? documentsData.documents : [];
12274
12300
  const liveDocs = backendDocuments.filter(
12275
- (d) => Boolean(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12301
+ (d) => Boolean(d.documentUrl) && isSafeUrl(d.documentUrl) && LIVE_TITLE_PATTERNS.some((rx) => rx.test(d.title))
12276
12302
  );
12277
12303
  const comingSoonDocs = COMING_SOON_TITLES.filter(
12278
12304
  (title) => !liveDocs.some((d) => d.title.toLowerCase().includes(title.toLowerCase()) || title.toLowerCase().includes(d.title.toLowerCase()))