@ecency/render-helper 2.5.11 → 2.5.12

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.
@@ -205,6 +205,96 @@ var ALLOWED_ATTRIBUTES = {
205
205
  "del": [],
206
206
  "ins": []
207
207
  };
208
+
209
+ // src/consts/embed-hosts.const.ts
210
+ var ALLOWED_EMBED_HOSTS = /* @__PURE__ */ new Set([
211
+ // YouTube
212
+ "www.youtube.com",
213
+ "youtube.com",
214
+ "www.youtube-nocookie.com",
215
+ "youtube-nocookie.com",
216
+ // Vimeo
217
+ "player.vimeo.com",
218
+ // Twitch
219
+ "player.twitch.tv",
220
+ // DTube
221
+ "emb.d.tube",
222
+ // 3Speak (video + audio)
223
+ "play.3speak.tv",
224
+ "3speak.tv",
225
+ "audio.3speak.tv",
226
+ // Loom
227
+ "www.loom.com",
228
+ // Spotify
229
+ "open.spotify.com",
230
+ // SoundCloud
231
+ "w.soundcloud.com",
232
+ // BitChute
233
+ "www.bitchute.com",
234
+ "bitchute.com",
235
+ // Rumble
236
+ "www.rumble.com",
237
+ "rumble.com",
238
+ // Brighteon
239
+ "www.brighteon.com",
240
+ "brighteon.com",
241
+ // VIMM
242
+ "www.vimm.tv",
243
+ // BrandNewTube
244
+ "brandnewtube.com",
245
+ // LBRY / Odysee
246
+ "lbry.tv",
247
+ "odysee.com",
248
+ // Skatehive / Skatehype
249
+ "ipfs.skatehive.app",
250
+ "www.skatehype.com",
251
+ "skatehype.com",
252
+ // archive.org
253
+ "archive.org",
254
+ // Truvvl
255
+ "embed.truvvl.com",
256
+ // Aureal
257
+ "aureal-embed.web.app",
258
+ "www.aureal-embed.web.app"
259
+ // Dapplr (player.*.dapplr.in / *.dapplr.in) — host suffix, handled below
260
+ ]);
261
+ var ALLOWED_EMBED_HOST_SUFFIXES = [".dapplr.in"];
262
+ var EMBED_HOST_PATH_PATTERNS = {
263
+ "www.youtube.com": /^\/embed\//,
264
+ "youtube.com": /^\/embed\//,
265
+ "www.youtube-nocookie.com": /^\/embed\//,
266
+ "youtube-nocookie.com": /^\/embed\//,
267
+ "player.vimeo.com": /^\/video\//,
268
+ "player.twitch.tv": /^\/$/,
269
+ // channel/video carried in the query string
270
+ "emb.d.tube": /^\/$/,
271
+ // dtube carries the ref in the #! fragment
272
+ "play.3speak.tv": /^\/(watch|embed)/,
273
+ "open.spotify.com": /^\/embed\//,
274
+ "www.loom.com": /^\/embed\//,
275
+ "www.bitchute.com": /^\/embed\//,
276
+ "bitchute.com": /^\/embed\//,
277
+ "www.rumble.com": /^\/embed\//,
278
+ "rumble.com": /^\/embed\//,
279
+ "www.brighteon.com": /^\/embed\//,
280
+ "brighteon.com": /^\/embed\//
281
+ };
282
+ function isAllowedEmbedSrc(value) {
283
+ if (!value) return false;
284
+ let url;
285
+ try {
286
+ url = new URL(value.trim());
287
+ } catch {
288
+ return false;
289
+ }
290
+ if (url.protocol !== "https:") return false;
291
+ const host = url.hostname.toLowerCase();
292
+ const hostAllowed = ALLOWED_EMBED_HOSTS.has(host) || ALLOWED_EMBED_HOST_SUFFIXES.some((suffix) => host.endsWith(suffix));
293
+ if (!hostAllowed) return false;
294
+ const pathPattern = EMBED_HOST_PATH_PATTERNS[host];
295
+ if (pathPattern && !pathPattern.test(url.pathname)) return false;
296
+ return true;
297
+ }
208
298
  function createParser() {
209
299
  return new xmldom.DOMParser({
210
300
  onError(level, msg) {
@@ -600,6 +690,14 @@ function buildPictureSources(rawUrl) {
600
690
  }
601
691
 
602
692
  // src/methods/sanitize-html.method.ts
693
+ var EMBED_SRC_DATA_ATTRS = /* @__PURE__ */ new Set(["data-embed-src", "data-video-href"]);
694
+ var isSafeNavValue = (value) => {
695
+ const trimmed = value.trim().replace(/[\t\n\r\f\v\0]/g, "").toLowerCase();
696
+ if (!trimmed) return false;
697
+ const isSafeScheme = /^(https?|mailto|hive|tel|web\+[a-z0-9.+-]+):/i.test(trimmed);
698
+ const isRelative = /^(\/\/|\/[^/]?|#|\?|[a-z0-9._\-]+(\/|$))/i.test(trimmed);
699
+ return isSafeScheme || isRelative;
700
+ };
603
701
  var decodeEntities = (input) => input.replace(/&#(\d+);?/g, (_, dec) => String.fromCodePoint(Number(dec))).replace(/&#x([0-9a-f]+);?/gi, (_, hex) => String.fromCodePoint(parseInt(hex, 16)));
604
702
  var isProxyPSrcset = (srcset) => {
605
703
  const base = trimTrailingSlash(getProxyBase());
@@ -627,6 +725,8 @@ function sanitizeHtml(html) {
627
725
  if (tag === "video" && ["src", "poster"].includes(name) && !/^https?:\/\//.test(decodedLower)) return "";
628
726
  if (tag === "img" && ["dynsrc", "lowsrc"].includes(name)) return "";
629
727
  if (tag === "span" && name === "class" && decoded.toLowerCase().trim() === "wr") return "";
728
+ if (EMBED_SRC_DATA_ATTRS.has(name) && !isAllowedEmbedSrc(decoded)) return "";
729
+ if (name === "data-href" && !isSafeNavValue(decoded)) return "";
630
730
  if (name === "id") {
631
731
  if (!ID_WHITELIST.test(decoded)) return "";
632
732
  }
@@ -2239,6 +2339,7 @@ exports.buildSrcSet = buildSrcSet;
2239
2339
  exports.buildSrcSetForFormat = buildSrcSetForFormat;
2240
2340
  exports.catchPostImage = catchPostImage;
2241
2341
  exports.getEntryImageRawUrl = getEntryImageRawUrl;
2342
+ exports.isAllowedEmbedSrc = isAllowedEmbedSrc;
2242
2343
  exports.isPictureEligibleRawUrl = isPictureEligibleRawUrl;
2243
2344
  exports.isValidPermlink = isValidPermlink;
2244
2345
  exports.postBodySummary = getPostBodySummary;