@ecency/render-helper 2.2.7 → 2.2.11

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.7",
3
+ "version": "2.2.11",
4
4
  "description": "Markdown+Html Render helper",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",
@@ -63,6 +63,7 @@
63
63
  "multihashes": "0.4.13",
64
64
  "path": "^0.12.7",
65
65
  "querystring": "^0.2.0",
66
+ "react-native-crypto-js": "^1.0.0",
66
67
  "remarkable": "^2.0.1",
67
68
  "url": "^0.11.0",
68
69
  "xmldom": "^0.5.0",
@@ -12,6 +12,7 @@ export const ALLOWED_ATTRIBUTES: XSSWhiteList = {
12
12
  'data-community',
13
13
  'data-filter',
14
14
  'data-embed-src',
15
+ 'data-youtube',
15
16
  'data-video-href',
16
17
  'data-proposal',
17
18
  'class',
@@ -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
  ]
@@ -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
  })
@@ -447,7 +447,7 @@ describe('Markdown2Html', () => {
447
447
  last_update: '2019-05-10T09:15:21',
448
448
  body: 'https://youtu.be/_-6hJ8sltdI'
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/_-6hJ8sltdI?autoplay=1" data-youtube="_-6hJ8sltdI"><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>'
451
451
 
452
452
  expect(markdown2Html(input)).toBe(expected)
453
453
  })
@@ -476,14 +476,14 @@ describe('Markdown2Html', () => {
476
476
  expect(markdown2Html(input)).toBe(expected)
477
477
  })
478
478
 
479
- it('32- Should handle whitelisted post links', () => {
479
+ it('32 - Should handle whitelisted post links', () => {
480
480
  const input = {
481
481
  author: 'foo33435',
482
482
  permlink: 'bar32435',
483
483
  last_update: '2019-05-10T09:15:21',
484
484
  body: 'https://peakd.com/tag/@demo/tests and https://steemit.com/test/@demo/post'
485
485
  }
486
- const expected = '<p><a class="markdown-post-link" data-tag="tag" data-author="demo" data-permlink="tests">/@demo/tests</a> and <a class="markdown-post-link" data-tag="test" data-author="demo" data-permlink="post">/@demo/post</a></p>'
486
+ const expected = '<p><a class=\"markdown-post-link\" data-tag=\"tag\" data-author=\"demo\" data-permlink=\"tests\">@demo/tests</a> and <a class=\"markdown-post-link\" data-tag=\"test\" data-author=\"demo\" data-permlink=\"post\">@demo/post</a></p>'
487
487
 
488
488
  expect(markdown2Html(input)).toBe(expected)
489
489
  })
@@ -644,14 +644,14 @@ describe('Markdown2Html', () => {
644
644
  expect(markdown2Html(input)).toBe(expected)
645
645
  })
646
646
 
647
- it('45- Should handle collection linking', () => {
647
+ it('45 - Should handle collection linking', () => {
648
648
  const input = {
649
649
  author: 'foo345',
650
650
  permlink: 'bar345',
651
651
  last_update: '2019-05-10T09:15:21',
652
652
  body: 'this is link https://peakd.com/ccc/jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more'
653
653
  }
654
- const expected = '<p>this is link <a class=\"markdown-post-link\" data-tag=\"ccc\" data-author=\"jarvie\" data-permlink=\"one-week-roadtrip-to-all-5-utah-national-parks-and-more\">/@jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more</a></p>'
654
+ const expected = '<p>this is link <a class=\"markdown-post-link\" data-tag=\"ccc\" data-author=\"jarvie\" data-permlink=\"one-week-roadtrip-to-all-5-utah-national-parks-and-more\">@jarvie/one-week-roadtrip-to-all-5-utah-national-parks-and-more</a></p>'
655
655
 
656
656
  expect(markdown2Html(input)).toBe(expected)
657
657
  })
@@ -99,7 +99,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
99
99
  const author = postMatch[3].replace('@', '')
100
100
  const permlink = postMatch[4]
101
101
  if (el.textContent === href) {
102
- el.textContent = `/@${author}/${permlink}`
102
+ el.textContent = `@${author}/${permlink}`
103
103
  }
104
104
  if (forApp) {
105
105
  el.removeAttribute('href')
@@ -152,7 +152,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
152
152
  const ha = `https://ecency.com/@${author}/${section}`
153
153
  el.setAttribute('href', ha)
154
154
  } else {
155
- const h = `@${author}/${section}`
155
+ const h = `/@${author}/${section}`
156
156
  el.setAttribute('href', h)
157
157
  }
158
158
  return
@@ -215,7 +215,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
215
215
  const section = cpostMatch[2]
216
216
 
217
217
  if (el.textContent === href) {
218
- el.textContent = `/@${author}/${section}`
218
+ el.textContent = `@${author}/${section}`
219
219
  }
220
220
  if (forApp) {
221
221
  const ha = `https://ecency.com/@${author}/${section}`
@@ -233,7 +233,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
233
233
  const author = cpostMatch[1].replace('@', '')
234
234
  const permlink = cpostMatch[2]
235
235
  if (el.textContent === href) {
236
- el.textContent = `/@${author}/${permlink}`
236
+ el.textContent = `@${author}/${permlink}`
237
237
  }
238
238
  if (forApp) {
239
239
  el.removeAttribute('href')
@@ -329,7 +329,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
329
329
  const author = cccMatch[2].replace('@', '')
330
330
  const permlink = cccMatch[3]
331
331
  if (el.textContent === href) {
332
- el.textContent = `/@${author}/${permlink}`
332
+ el.textContent = `@${author}/${permlink}`
333
333
  }
334
334
  if (forApp) {
335
335
  el.removeAttribute('href')
@@ -412,8 +412,9 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
412
412
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`
413
413
 
414
414
  el.textContent = ''
415
-
416
- el.setAttribute('data-embed-src', embedSrc)
415
+
416
+ el.setAttribute('data-embed-src', embedSrc);
417
+ el.setAttribute('data-youtube', vid);
417
418
 
418
419
  const thumbImg = el.ownerDocument.createElement('img')
419
420
  thumbImg.setAttribute('class', 'no-replace video-thumbnail')
@@ -664,6 +665,14 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
664
665
 
665
666
  if (forApp) {
666
667
  el.setAttribute('data-href', href)
668
+ const match = href.match(YOUTUBE_REGEX)
669
+ if (match) {
670
+ const e = YOUTUBE_REGEX.exec(href)
671
+ if (e[1]) {
672
+ const vid = e[1]
673
+ el.setAttribute('data-youtube', vid);
674
+ }
675
+ }
667
676
  el.removeAttribute('href')
668
677
  } else {
669
678
  const externalRegex =/(\b(https?|ftp|file):\/\/[-A-Z0-9+&@#\/%?=~_|!:,.;]*[-A-Z0-9+&@#\/%=~_|])/ig;
@@ -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>', '')
@@ -34,7 +34,7 @@ export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
34
34
  const thumbnail = proxifyImageSrc(`https://img.youtube.com/vi/${vid.split('?')[0]}/hqdefault.jpg`, 0, 0, webp ? 'webp' : 'match')
35
35
  const embedSrc = `https://www.youtube.com/embed/${vid}?autoplay=1`
36
36
 
37
- const attrs = `class="markdown-video-link markdown-video-link-youtube" data-embed-src="${embedSrc}"`
37
+ const attrs = `class="markdown-video-link markdown-video-link-youtube" data-embed-src="${embedSrc}" data-youtube="${vid}"`
38
38
 
39
39
  const thumbImg = node.ownerDocument.createElement('img')
40
40
  thumbImg.setAttribute('class', 'no-replace video-thumbnail')
@@ -140,4 +140,11 @@ So, how can you qualify to get one?`
140
140
  const expected = 'Lorem Ipsum Dolor'
141
141
  expect(getPostBodySummary(input)).toBe(expected)
142
142
  })
143
+
144
+
145
+ it('12- Test entity parsing', () => {
146
+ const input = 'http://lorem.com Lorem &lt; Ipsum &amp; Dolor &euro; '
147
+ const expected = 'Lorem < Ipsum & Dolor €'
148
+ expect(getPostBodySummary(input)).toBe(expected)
149
+ })
143
150
  })
@@ -31,7 +31,7 @@ const joint = (arr: string[], limit = 200) => {
31
31
  return result.trim();
32
32
  };
33
33
 
34
- function postBodySummary(entryBody: string, length?: number): string {
34
+ function postBodySummary(entryBody: string, length?: number, platform:'ios'|'android'|'web' = 'web'): string {
35
35
  if (!entryBody) {
36
36
  return ''
37
37
  }
@@ -57,6 +57,19 @@ function postBodySummary(entryBody: string, length?: number): string {
57
57
  'sup'
58
58
  ]);
59
59
 
60
+ //encrypt entities
61
+ const entities = entryBody.match(/&(.*?);/g);
62
+ const encEntities:string[] = [];
63
+ if(entities && platform !== 'web'){
64
+ entities.forEach((entity)=>{
65
+ var CryptoJS = require("react-native-crypto-js");
66
+ const encData = CryptoJS.AES.encrypt(entity, 'key').toString();
67
+ let encyptedEntity = CryptoJS.enc.Base64.stringify(CryptoJS.enc.Utf8.parse(encData));
68
+ encEntities.push(encyptedEntity);
69
+ entryBody = entryBody.replace(entity, encyptedEntity);
70
+ })
71
+ }
72
+
60
73
  // Convert markdown to html
61
74
  let text = '';
62
75
  try {
@@ -65,6 +78,18 @@ function postBodySummary(entryBody: string, length?: number): string {
65
78
  console.log(err)
66
79
  }
67
80
 
81
+
82
+ //decrypt and put back entiteis
83
+ if(platform !== 'web'){
84
+ encEntities.forEach((encEntity)=>{
85
+ var CryptoJS = require("react-native-crypto-js");
86
+ let decData = CryptoJS.enc.Base64.parse(encEntity).toString(CryptoJS.enc.Utf8);
87
+ let entity = CryptoJS.AES.decrypt(decData, 'key').toString(CryptoJS.enc.Utf8);
88
+ text = text.replace(encEntity, entity);
89
+ })
90
+ }
91
+
92
+
68
93
  text = text
69
94
  .replace(/(<([^>]+)>)/gi, '') // Remove html tags
70
95
  .replace(/\r?\n|\r/g, ' ') // Remove new lines
@@ -84,9 +109,9 @@ function postBodySummary(entryBody: string, length?: number): string {
84
109
  return text
85
110
  }
86
111
 
87
- export function getPostBodySummary(obj: Entry | string, length?: number): any {
112
+ export function getPostBodySummary(obj: Entry | string, length?: number, platform?:'ios'|'android'|'web'): any {
88
113
  if (typeof obj === 'string') {
89
- return postBodySummary(obj as string, length)
114
+ return postBodySummary(obj as string, length, platform)
90
115
  }
91
116
 
92
117
  const key = `${makeEntryCacheKey(obj)}-sum-${length}`
@@ -96,7 +121,7 @@ export function getPostBodySummary(obj: Entry | string, length?: number): any {
96
121
  return item
97
122
  }
98
123
 
99
- const res = postBodySummary(obj.body, length)
124
+ const res = postBodySummary(obj.body, length, platform)
100
125
  cacheSet(key, res)
101
126
 
102
127
  return res
@@ -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
  }