@ecency/render-helper 2.5.4 → 2.5.6

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 CHANGED
@@ -32,7 +32,7 @@
32
32
  - `catchPostImage` - Extract first image from post content or metadata
33
33
  - `postBodySummary` - Generate text summary from post body
34
34
  - `proxifyImageSrc` - Proxify image URLs through Ecency's image server
35
- - `setProxyBase` - Configure image proxy endpoint (default: `https://images.ecency.com`)
35
+ - `setProxyBase` - Configure image proxy endpoint (default: `https://i.ecency.com`)
36
36
  - `setCacheSize` - Configure LRU cache size (default: 60 entries)
37
37
 
38
38
  ### Processing Pipeline
@@ -64,14 +64,14 @@ var INTERNAL_POST_TAG_REGEX = /^(.+?)\/(@[\w.\d-]+)\/(.*)$/i;
64
64
  var INTERNAL_POST_REGEX = /^\/(@[\w.\d-]+)\/(.*)$/i;
65
65
  var CUSTOM_COMMUNITY_REGEX = /^https?:\/\/(.*)\/c\/(hive-\d+)(.*)/i;
66
66
  var YOUTUBE_REGEX = /(?:youtube\.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|shorts\/|.*[?&]v=)|youtu\.be\/)([^"&?\/\s]{11})/i;
67
- var YOUTUBE_EMBED_REGEX = /^(https?:)?\/\/www.youtube.com\/(embed|shorts)\/.*/i;
67
+ var YOUTUBE_EMBED_REGEX = /^(https?:)?\/\/www\.youtube\.com\/(embed|shorts)\/.*/i;
68
68
  var VIMEO_REGEX = /(https?:\/\/)?(www\.)?(?:vimeo)\.com.*(?:videos|video|channels|)\/([\d]+)/i;
69
69
  var VIMEO_EMBED_REGEX = /https:\/\/player\.vimeo\.com\/video\/([0-9]+)(?:$|[?#])/;
70
- var BITCHUTE_REGEX = /^(?:https?:\/\/)?(?:www\.)?bitchute.com\/(?:video|embed)\/([a-z0-9]+)/i;
70
+ var BITCHUTE_REGEX = /^(?:https?:\/\/)?(?:www\.)?bitchute\.com\/(?:video|embed)\/([a-z0-9]+)/i;
71
71
  var D_TUBE_REGEX = /(https?:\/\/d\.tube\/#!\/v\/)(\w+)\/(\w+)/g;
72
72
  var D_TUBE_REGEX2 = /(https?:\/\/d\.tube\/v\/)(\w+)\/(\w+)/g;
73
73
  var D_TUBE_EMBED_REGEX = /^https:\/\/emb.d.tube\/#!\/[^/?#]+\/[^/?#]+(?:$|[?#])/i;
74
- var TWITCH_REGEX = /https?:\/\/(?:www.)?twitch.tv\/(?:(videos)\/)?([a-zA-Z0-9][\w]{3,24})/i;
74
+ var TWITCH_REGEX = /https?:\/\/(?:www\.)?twitch\.tv\/(?:(videos)\/)?([a-zA-Z0-9][\w]{3,24})/i;
75
75
  var DAPPLR_REGEX = /^(https?:)?\/\/[a-z]*\.dapplr.in\/file\/dapplr-videos\/.*/i;
76
76
  var TRUVVL_REGEX = /^https?:\/\/embed.truvvl.com\/(@[\w.\d-]+)\/(.*)/i;
77
77
  var LBRY_REGEX = /^(https?:)?\/\/lbry.tv\/\$\/embed\/[^?#]+(?:$|[?#])/i;
@@ -84,15 +84,15 @@ var SPEAK_AUDIO_REGEX = /https?:\/\/audio\.3speak\.tv\/play\?[^\s]+/i;
84
84
  var SPEAK_AUDIO_EMBED_REGEX = /^https?:\/\/audio\.3speak\.tv\/play\?.+$/i;
85
85
  var TWITTER_REGEX = /(?:https?:\/\/(?:(?:twitter\.com\/(.*?)\/status\/(.*))))/gi;
86
86
  var SPOTIFY_REGEX = /^https:\/\/open\.spotify\.com\/playlist\/(.*)?$/gi;
87
- var RUMBLE_REGEX = /^https:\/\/rumble.com\/embed\/([a-zA-Z0-9-]+)\/\?pub=\w+/;
87
+ var RUMBLE_REGEX = /^https:\/\/rumble\.com\/embed\/([a-zA-Z0-9-]+)\/\?pub=\w+/;
88
88
  var BRIGHTEON_REGEX = /^https?:\/\/(www\.)?brighteon\.com\/(?:embed\/)?(.*[0-9].*)/i;
89
- var VIMM_EMBED_REGEX = /^https:\/\/www.vimm.tv\/[^?#]+(?:$|[?#])/i;
89
+ var VIMM_EMBED_REGEX = /^https:\/\/www\.vimm\.tv\/[^?#]+(?:$|[?#])/i;
90
90
  var SPOTIFY_EMBED_REGEX = /^https:\/\/open\.spotify\.com\/(embed|embed-podcast)\/(playlist|show|episode|track|album)\/([^/?#]+)(?:$|[?#])/i;
91
- var SOUNDCLOUD_EMBED_REGEX = /^https:\/\/w.soundcloud.com\/player\/\?[^#]+$/i;
92
- var TWITCH_EMBED_REGEX = /^(https?:)?\/\/player.twitch.tv\/(?:\?[^/]+)?$/i;
91
+ var SOUNDCLOUD_EMBED_REGEX = /^https:\/\/w\.soundcloud\.com\/player\/\?[^#]+$/i;
92
+ var TWITCH_EMBED_REGEX = /^(https?:)?\/\/player\.twitch\.tv\/(?:\?[^/]+)?$/i;
93
93
  var BRAND_NEW_TUBE_REGEX = /^https:\/\/brandnewtube\.com\/embed\/[a-z0-9]+$/i;
94
- var LOOM_REGEX = /^(https?:)?\/\/www.loom.com\/share\/([^/?#]+)(?:$|[?#])/i;
95
- var LOOM_EMBED_REGEX = /^(https?:)?\/\/www.loom.com\/embed\/([^/?#]+)(?:$|[?#])/i;
94
+ var LOOM_REGEX = /^(https?:)?\/\/www\.loom\.com\/share\/([^/?#]+)(?:$|[?#])/i;
95
+ var LOOM_EMBED_REGEX = /^(https?:)?\/\/www\.loom\.com\/embed\/([^/?#]+)(?:$|[?#])/i;
96
96
  var AUREAL_EMBED_REGEX = /^(https?:)?\/\/(www\.)?(?:aureal-embed)\.web\.app\/([0-9]+)(?:$|[?#])/i;
97
97
  var ENTITY_REGEX = /&([a-z0-9]+|#[0-9]{1,6}|#x[0-9a-fA-F]{1,6});/ig;
98
98
  var SECTION_REGEX = /\B(\#[\da-zA-Z-_]+\b)(?!;)/i;
@@ -444,12 +444,12 @@ function sanitizeHtml(html) {
444
444
  const decoded = decodeEntities(value.trim());
445
445
  const decodedLower = decoded.toLowerCase();
446
446
  if (name.startsWith("on")) return "";
447
- if (tag === "img" && name === "src" && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
447
+ if (tag === "img" && name === "src" && !/^https?:\/\//.test(decodedLower)) return "";
448
448
  if (tag === "img" && name === "srcset") {
449
449
  const candidates = decoded.split(",").map((c) => c.trim().split(/\s+/)[0]);
450
- if (candidates.some((url) => !/^https?:\/\//.test(url))) return "";
450
+ if (candidates.some((url) => !/^https?:\/\//i.test(url))) return "";
451
451
  }
452
- if (tag === "video" && ["src", "poster"].includes(name) && (!/^https?:\/\//.test(decodedLower) || decodedLower.startsWith("javascript:"))) return "";
452
+ if (tag === "video" && ["src", "poster"].includes(name) && !/^https?:\/\//.test(decodedLower)) return "";
453
453
  if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
454
454
  if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
455
455
  if (name === "id") {
@@ -527,11 +527,11 @@ function proxifyImageSrc(url, width = 0, height = 0, _format = "match") {
527
527
  var SRCSET_WIDTHS = [320, 600, 800, 1024, 1280];
528
528
  function buildSrcSet(url) {
529
529
  if (!url || typeof url !== "string") return "";
530
- const escapedBase = proxyBase.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
531
- const proxyPattern = new RegExp(`^${escapedBase}/p/([^?]+)`);
532
- const match = url.match(proxyPattern);
533
- if (match) {
534
- const phash = extractPHash(url) || match[1];
530
+ const proxyPrefix = `${proxyBase}/p/`;
531
+ if (url.startsWith(proxyPrefix)) {
532
+ const rest = url.slice(proxyPrefix.length);
533
+ const q = rest.indexOf("?");
534
+ const phash = extractPHash(url) || (q >= 0 ? rest.slice(0, q) : rest);
535
535
  return SRCSET_WIDTHS.map((w) => `${proxyBase}/p/${phash}?format=match&mode=fit&width=${w} ${w}w`).join(", ");
536
536
  }
537
537
  return SRCSET_WIDTHS.map((w) => {
@@ -670,7 +670,10 @@ function a(el, forApp, parentDomain = "ecency.com", seoContext, renderOptions) {
670
670
  if (className && (["markdown-author-link", "markdown-tag-link"].includes(className) || className.includes("er-author") || className.includes("er-tag"))) {
671
671
  return;
672
672
  }
673
- if (href && href.trim().toLowerCase().startsWith("javascript:")) {
673
+ const trimmed = href.trim().replace(/[\t\n\r\f\v\0]/g, "").toLowerCase();
674
+ const isSafeScheme = /^(https?|mailto|hive|tel|web\+[a-z0-9.+-]+):/i.test(trimmed);
675
+ const isRelative = /^(\/\/|\/[^/]?|#|\?|[a-z0-9._\-]+(\/|$))/i.test(trimmed);
676
+ if (!isSafeScheme && !isRelative) {
674
677
  el.removeAttribute("href");
675
678
  return;
676
679
  }
@@ -1445,7 +1448,7 @@ function linkify(content, forApp, renderOptions) {
1445
1448
  content = content.replace(/(^|\s|>)(#[-a-z\d]+)/gi, (tag) => {
1446
1449
  if (/#[\d]+$/.test(tag)) return tag;
1447
1450
  const preceding = /^\s|>/.test(tag) ? tag[0] : "";
1448
- tag = tag.replace(">", "");
1451
+ tag = tag.replace(/^>/, "");
1449
1452
  const tag2 = tag.trim().substring(1);
1450
1453
  const tagLower = tag2.toLowerCase();
1451
1454
  if (!forApp) {