@ecency/render-helper 2.2.8 → 2.2.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ecency/render-helper",
3
- "version": "2.2.8",
3
+ "version": "2.2.12",
4
4
  "description": "Markdown+Html Render helper",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -12,6 +12,8 @@ export const ALLOWED_ATTRIBUTES: XSSWhiteList = {
12
12
  'data-community',
13
13
  'data-filter',
14
14
  'data-embed-src',
15
+ 'data-youtube',
16
+ 'data-start-time',
15
17
  'data-video-href',
16
18
  'data-proposal',
17
19
  'class',
@@ -11,7 +11,7 @@ export const INTERNAL_TOPIC_REGEX = /^\/(trending|hot|created|promoted|muted|pay
11
11
  export const INTERNAL_POST_TAG_REGEX = /\/(.*)\/(@[\w.\d-]+)\/(.*)/i
12
12
  export const INTERNAL_POST_REGEX = /^\/(@[\w.\d-]+)\/(.*)$/i
13
13
  export const CUSTOM_COMMUNITY_REGEX = /^https?:\/\/(.*)\/c\/(hive-\d+)(.*)/i
14
- export const YOUTUBE_REGEX = /(?:https?:\/\/)?(?:www\.)?(?:youtube\.com\/watch\?v=|youtu\.be\/)([^& \n<]+)(?:[^ \n<]+)?/g
14
+ export const YOUTUBE_REGEX = /(?:youtube.com\/(?:[^\/]+\/.+\/|(?:v|e(?:mbed)?)\/|.*[?&]v=)|youtu.be\/)([^"&?\/\s]{11})/g
15
15
  export const YOUTUBE_EMBED_REGEX = /^(https?:)?\/\/www.youtube.com\/(embed|shorts)\/.*/i
16
16
  export const VIMEO_REGEX = /(https?:\/\/)?(www\.)?(?:vimeo)\.com.*(?:videos|video|channels|)\/([\d]+)/i
17
17
  export const VIMEO_EMBED_REGEX = /https:\/\/player\.vimeo\.com\/video\/([0-9]+)/
@@ -25,4 +25,5 @@ export const WHITE_LIST = [
25
25
  'hiveblockexplorer.com',
26
26
  'proofofbrain.blog',
27
27
  'liketu.com',
28
+ 'inji.com'
28
29
  ]
package/src/helper.ts CHANGED
@@ -14,3 +14,14 @@ export function makeEntryCacheKey(entry: any): string {
14
14
  return `${entry.author}-${entry.permlink}-${entry.last_update}`
15
15
  }
16
16
 
17
+ export function extractYtStartTime(url:string):string {
18
+ const urlObj = new URL(url);
19
+ const params = new URLSearchParams(urlObj.search);
20
+ if(params.has('t')){
21
+ return '' + parseInt(params.get('t')); //parsing is important as sometimes t is famated '123s';
22
+ }else if (params.has('start')){
23
+ return params.get('start');
24
+ }
25
+
26
+ return '';
27
+ }
@@ -70,7 +70,7 @@ describe('Markdown2Html', () => {
70
70
  last_update: '2019-05-10T09:15:21',
71
71
  body: 'https://www.youtube.com/watch?v=qK3d1eoH-Qs'
72
72
  }
73
- const expected = '<p><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/qK3d1eoH-Qs?autoplay=1"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eMZRVYW6mkGBWKemLWWzXbRhNG7Z3h1qjGS.png?format=match&amp;mode=fit" /><span class="markdown-video-play"></span></a></p>'
73
+ const expected = '<p><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/qK3d1eoH-Qs?autoplay=1" data-youtube="qK3d1eoH-Qs"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eMZRVYW6mkGBWKemLWWzXbRhNG7Z3h1qjGS.png?format=match&amp;mode=fit" /><span class="markdown-video-play"></span></a></p>'
74
74
 
75
75
  expect(markdown2Html(input)).toBe(expected)
76
76
  })
@@ -142,7 +142,7 @@ describe('Markdown2Html', () => {
142
142
  last_update: '2019-05-10T09:15:21',
143
143
  body: '<iframe width="560" height="315" src="https://www.youtube.com/embed/I3f9ixg59no?foo=bar&baz=000" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>'
144
144
  }
145
- const expected = '<iframe src="https://www.youtube.com/embed/I3f9ixg59no" frameborder="0" allowfullscreen="allowfullscreen"></iframe>'
145
+ const expected = '<iframe src=\"https://www.youtube.com/embed/I3f9ixg59no\" allowfullscreen=\"allowfullscreen\"></iframe>'
146
146
 
147
147
  expect(markdown2Html(input)).toBe(expected)
148
148
  })
@@ -445,9 +445,9 @@ describe('Markdown2Html', () => {
445
445
  author: 'foo329',
446
446
  permlink: 'bar329',
447
447
  last_update: '2019-05-10T09:15:21',
448
- body: 'https://youtu.be/_-6hJ8sltdI'
448
+ body: 'https://youtu.be/UuyS7YAkECA?t=295s'
449
449
  }
450
- const expected = '<p><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/_-6hJ8sltdI?autoplay=1"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eEGc5AMBA14JMjkkxrUAj3mV5QR9D6zfstr.png?format=match&amp;mode=fit" /><span class="markdown-video-play"></span></a></p>'
450
+ const expected = '<p><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&amp;mode=fit" /><span class="markdown-video-play"></span></a></p>'
451
451
 
452
452
  expect(markdown2Html(input)).toBe(expected)
453
453
  })
@@ -877,6 +877,18 @@ describe('Markdown2Html', () => {
877
877
 
878
878
  expect(markdown2Html(input, false)).toBe(expected)
879
879
  })
880
+
881
+ it('65- Should handle youtube.com/embed videos', () => {
882
+ const input = {
883
+ author: 'foo329',
884
+ permlink: 'bar329',
885
+ last_update: '2019-05-10T09:15:21',
886
+ body: 'https://www.youtube.com/embed/UuyS7YAkECA?start=295&autoplay=1'
887
+ }
888
+ const expected = '<p><a class="markdown-video-link markdown-video-link-youtube" data-embed-src="https://www.youtube.com/embed/UuyS7YAkECA?autoplay=1" data-youtube="UuyS7YAkECA" data-start-time="295"><img class="no-replace video-thumbnail" src="https://images.ecency.com/p/S5Eokt4BcQdk7EHeT1aYjzebg2hC7hkthT45eAMp88bZ44hfAQDm6BtJw2H53aq1Tpn1cu4.png?format=match&amp;mode=fit" /><span class="markdown-video-play"></span></a></p>'
889
+
890
+ expect(markdown2Html(input)).toBe(expected)
891
+ })
880
892
  })
881
893
 
882
894
  describe("Rumble support", () => {
@@ -28,6 +28,7 @@ import {
28
28
  import { getSerializedInnerHTML } from './get-inner-html.method'
29
29
  import { proxifyImageSrc } from '../proxify-image-src'
30
30
  import { removeChildNodes } from './remove-child-nodes.method'
31
+ import { extractYtStartTime } from '../helper'
31
32
 
32
33
  export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
33
34
  let href = el.getAttribute('href')
@@ -412,8 +413,15 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
412
413
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`
413
414
 
414
415
  el.textContent = ''
415
-
416
- el.setAttribute('data-embed-src', embedSrc)
416
+
417
+ el.setAttribute('data-embed-src', embedSrc);
418
+ el.setAttribute('data-youtube', vid);
419
+
420
+ //extract start time if available
421
+ const startTime = extractYtStartTime(href);
422
+ if(startTime){
423
+ el.setAttribute('data-start-time', startTime);
424
+ }
417
425
 
418
426
  const thumbImg = el.ownerDocument.createElement('img')
419
427
  thumbImg.setAttribute('class', 'no-replace video-thumbnail')
@@ -664,6 +672,20 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
664
672
 
665
673
  if (forApp) {
666
674
  el.setAttribute('data-href', href)
675
+ const match = href.match(YOUTUBE_REGEX)
676
+ if (match) {
677
+ const e = YOUTUBE_REGEX.exec(href)
678
+ if (e[1]) {
679
+ const vid = e[1]
680
+ el.setAttribute('data-youtube', vid);
681
+
682
+ //extract start time if available
683
+ const startTime = extractYtStartTime(href);
684
+ if(startTime){
685
+ el.setAttribute('data-start-time', startTime);
686
+ }
687
+ }
688
+ }
667
689
  el.removeAttribute('href')
668
690
  } else {
669
691
  const externalRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
@@ -675,3 +697,6 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
675
697
  }
676
698
  }
677
699
  }
700
+
701
+
702
+
@@ -44,7 +44,26 @@ export function markdownToHTML(input: string, forApp: boolean, webp: boolean): s
44
44
  return ''
45
45
  }
46
46
 
47
- let output
47
+ let output = '';
48
+
49
+ //encrypt entities
50
+ const entities = input.match(/&(.*?);/g);
51
+ const encEntities:string[] = [];
52
+
53
+ try{
54
+ if(entities && forApp){
55
+ entities.forEach((entity)=>{
56
+ const CryptoJS = require("react-native-crypto-js");
57
+ const encData = CryptoJS.AES.encrypt(entity, 'key').toString();
58
+ const encyptedEntity = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encData));
59
+ encEntities.push(encyptedEntity);
60
+ input = input.replace(entity, encyptedEntity);
61
+ })
62
+ }
63
+ } catch (err){
64
+ console.log("failed to encrypt entities, ignore if not using mobile");
65
+ }
66
+
48
67
 
49
68
  try {
50
69
  output = md.render(input)
@@ -57,6 +76,16 @@ export function markdownToHTML(input: string, forApp: boolean, webp: boolean): s
57
76
  output = ''
58
77
  }
59
78
 
79
+ //decrypt and put back entiteis
80
+ if(forApp && output){
81
+ encEntities.forEach((encEntity)=>{
82
+ const CryptoJS = require("react-native-crypto-js");
83
+ const decData = CryptoJS.enc.Base64.parse(encEntity).toString(CryptoJS.enc.Utf8);
84
+ const entity = CryptoJS.AES.decrypt(decData, 'key').toString(CryptoJS.enc.Utf8);
85
+ output = output.replace(encEntity, entity);
86
+ })
87
+ }
88
+
60
89
  output = output.replace(/ xmlns="http:\/\/www.w3.org\/1999\/xhtml"/g, '')
61
90
  .replace('<body id="root">', '')
62
91
  .replace('</body>', '')
@@ -1,4 +1,5 @@
1
1
  import { IMG_REGEX, YOUTUBE_REGEX, WHITE_LIST, DOMParser, POST_REGEX, } from '../consts'
2
+ import { extractYtStartTime } from '../helper'
2
3
  import { proxifyImageSrc } from '../proxify-image-src'
3
4
  import { linkify } from './linkify.method'
4
5
 
@@ -34,7 +35,12 @@ export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
34
35
  const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split('?')[0]}/hqdefault.jpg`, 0, 0, webp ? 'webp' : 'match')
35
36
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`
36
37
 
37
- const attrs = `class="markdown-video-link markdown-video-link-youtube" data-embed-src="${embedSrc}"`
38
+ let attrs = `class="markdown-video-link markdown-video-link-youtube" data-embed-src="${embedSrc}" data-youtube="${vid}"`
39
+ //extract start time if available
40
+ const startTime = extractYtStartTime(node.nodeValue);
41
+ if(startTime){
42
+ attrs = attrs.concat(` data-start-time="${startTime}"`);
43
+ }
38
44
 
39
45
  const thumbImg = node.ownerDocument.createElement('img')
40
46
  thumbImg.setAttribute('class', 'no-replace video-thumbnail')
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "id": 22,
3
3
  "input": "<IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert(&quot;XSS&quot;)>\n<IMG SRC=`javascript:alert(\"RSnake says, 'XSS'\")`>\n<a onmouseover=\"alert(document.cookie)\">xxs link</a><a onmouseover=alert(document.cookie)>xxs link</a>\n<IMG \"\"\"><SCRIPT>alert(\"XSS\")</SCRIPT>\"><IMG SRC=javascript:alert(String.fromCharCode(88,83,83))>\n <IMG SRC= onmouseover=\"alert('xxs')\">",
4
- "result": "<p>&lt;IMG SRC=JaVaScRiPt:alert('XSS')&gt; <img /><br />\n&lt;IMG SRC=<code>javascript:alert(\"RSnake says, 'XSS'\")</code>&gt;<br />\n<a>xxs link</a><a>xxs link</a><br />\n&lt;IMG \"\"\"&gt;alert(&quot;XSS&quot;)\"&gt;<img /><br />\n&lt;IMG SRC= onmouseover=\"alert('xxs')\"&gt;</p>"
4
+ "result": "<p>&lt;IMG SRC=JaVaScRiPt:alert('XSS')&gt; &lt;IMG SRC=javascript:alert(&quot;XSS&quot;)&gt;<br />\n&lt;IMG SRC=<code>javascript:alert(\"RSnake says, 'XSS'\")</code>&gt;<br />\n<a>xxs link</a><a>xxs link</a><br />\n&lt;IMG \"\"\"&gt;alert(&quot;XSS&quot;)\"&gt;<img /><br />\n&lt;IMG SRC= onmouseover=\"alert('xxs')\"&gt;</p>"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "id": 23,
3
3
  "input": "<IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"></img> <img src=x onerror=\"&#0000106&#0000097&#0000118&#0000097&#0000115&#0000099&#0000114&#0000105&#0000112&#0000116&#0000058&#0000097&#0000108&#0000101&#0000114&#0000116&#0000040&#0000039&#0000088&#0000083&#0000083&#0000039&#0000041\"> <IMG SRC=&#106;&#97;&#118;&#97;&#115;&#99;&#114;&#105;&#112;&#116;&#58;&#97;&#108;&#101;&#114;&#116;&#40;&#39;&#88;&#83;&#83;&#39;&#41;> <IMG SRC=&#x6A&#x61&#x76&#x61&#x73&#x63&#x72&#x69&#x70&#x74&#x3A&#x61&#x6C&#x65&#x72&#x74&#x28&#x27&#x58&#x53&#x53&#x27&#x29> <IMG SRC=\" &#14; javascript:alert('XSS');\">",
4
- "result": "<p>&lt;IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"&gt; <img src=\"https://images.ecency.com/p/35.png?format=match&amp;mode=fit\" /> <img /> <img /> <img /></p>"
4
+ "result": "<p>&lt;IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"&gt; <img src=\"https://images.ecency.com/p/35.png?format=match&amp;mode=fit\"> <img> <img> <img /></p>"
5
5
  }
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "id": 27,
3
3
  "input": "<BR SIZE=\"&{alert('XSS')}\"> <LINK REL=\"stylesheet\" HREF=\"javascript:alert('XSS');\"> <STYLE>body{}</STYLE> <META HTTP-EQUIV=\"Link\" Content=\"<http://xss.rocks/xss.css>; REL=stylesheet\"> <IFRAME SRC=\"javascript:alert('XSS');\"></IFRAME> foo <IFRAME SRC=# onmouseover=\"alert(document.cookie)\"></IFRAME> bar <FRAMESET><FRAME SRC=\"javascript:alert('XSS');\"></FRAMESET> baz ",
4
- "result": "<p><br /> foo bar baz\n</p>"
4
+ "result": "<p><br> foo bar baz\n</p>"
5
5
  }