@ecency/render-helper 2.3.1 → 2.3.3
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/lib/helper.d.ts +1 -0
- package/lib/helper.js +9 -1
- package/lib/helper.js.map +1 -1
- package/lib/methods/a.method.js +6 -8
- package/lib/methods/a.method.js.map +1 -1
- package/lib/methods/img.method.js +4 -6
- package/lib/methods/img.method.js.map +1 -1
- package/lib/methods/linkify.method.js +8 -5
- package/lib/methods/linkify.method.js.map +1 -1
- package/lib/methods/text.method.js +2 -0
- package/lib/methods/text.method.js.map +1 -1
- package/lib/render-helper.js +1 -1
- package/package.json +1 -1
- package/src/helper.ts +11 -1
- package/src/markdown-2-html.spec.ts +2 -2
- package/src/methods/a.method.ts +2 -5
- package/src/methods/img.method.ts +5 -7
- package/src/methods/linkify.method.ts +8 -5
- package/src/methods/text.method.ts +2 -1
- package/src/test/data/legacy/10.json +1 -1
- package/src/test/data/legacy/21.json +1 -1
- package/src/test/data/legacy/22.json +1 -1
- package/src/test/data/legacy/23.json +1 -1
- package/src/test/data/legacy/26.json +1 -1
package/package.json
CHANGED
package/src/helper.ts
CHANGED
|
@@ -22,8 +22,18 @@ export function extractYtStartTime(url:string):string {
|
|
|
22
22
|
return '' + parseInt(params.get('t')); //parsing is important as sometimes t is famated '123s';
|
|
23
23
|
}else if (params.has('start')){
|
|
24
24
|
return params.get('start');
|
|
25
|
-
}
|
|
25
|
+
}
|
|
26
26
|
} catch (error) {
|
|
27
27
|
return '';
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
|
+
export function isValidPermlink(permlink: string): boolean {
|
|
31
|
+
// Should not contain image extensions, query params, or fragments
|
|
32
|
+
const isImage = /\.(jpg|jpeg|png|webp|gif|svg)$/i.test(permlink);
|
|
33
|
+
const hasSpecialChars = /[?#]/.test(permlink);
|
|
34
|
+
const isCleanFormat = /^[a-z0-9-]+$/.test(permlink); // Hive standard
|
|
35
|
+
|
|
36
|
+
return isCleanFormat && !isImage && !hasSpecialChars;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
|
|
@@ -910,7 +910,7 @@ describe('Markdown2Html', () => {
|
|
|
910
910
|
last_update: '2019-05-10T09:15:21',
|
|
911
911
|
body: 'direct link https://ecency.com/@ecency/faq?history'
|
|
912
912
|
}
|
|
913
|
-
const expected = '<p dir=\"auto\">direct link <a
|
|
913
|
+
const expected = '<p dir=\"auto\">direct link <a href=\"https://ecency.com/@ecency/faq?history\" class=\"markdown-post-link\">https://ecency.com/@ecency/faq?history</a></p>'
|
|
914
914
|
|
|
915
915
|
expect(markdown2Html(input)).toBe(expected)
|
|
916
916
|
})
|
|
@@ -922,7 +922,7 @@ describe('Markdown2Html', () => {
|
|
|
922
922
|
last_update: '2019-05-10T09:15:21',
|
|
923
923
|
body: 'direct link https://ecency.com/@ecency/posts?q=games'
|
|
924
924
|
}
|
|
925
|
-
const expected = '<p dir=\"auto\">direct link <a href=\"https://ecency.com/@ecency/posts?q=games\" class=\"markdown-profile-link\"
|
|
925
|
+
const expected = '<p dir=\"auto\">direct link <a href=\"https://ecency.com/@ecency/posts?q=games\" class=\"markdown-profile-link\">https://ecency.com/@ecency/posts?q=games</a></p>'
|
|
926
926
|
|
|
927
927
|
expect(markdown2Html(input)).toBe(expected)
|
|
928
928
|
})
|
package/src/methods/a.method.ts
CHANGED
|
@@ -30,12 +30,8 @@ import {
|
|
|
30
30
|
import { getSerializedInnerHTML } from './get-inner-html.method'
|
|
31
31
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
32
32
|
import { removeChildNodes } from './remove-child-nodes.method'
|
|
33
|
-
import { extractYtStartTime } from '../helper'
|
|
33
|
+
import { extractYtStartTime, isValidPermlink } from '../helper'
|
|
34
34
|
|
|
35
|
-
function isValidPermlink(permlink: string): boolean {
|
|
36
|
-
// Reject anything that looks like a file (ends with .jpg/.png/.webp/.jpeg etc)
|
|
37
|
-
return !/\.(jpg|jpeg|png|webp|gif|svg)$/i.test(permlink);
|
|
38
|
-
}
|
|
39
35
|
|
|
40
36
|
export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
41
37
|
let href = el.getAttribute('href')
|
|
@@ -162,6 +158,7 @@ export function a(el: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
162
158
|
const author = tpostMatch[2].replace('@', '').toLowerCase()
|
|
163
159
|
const section = tpostMatch[3]
|
|
164
160
|
|
|
161
|
+
if (!isValidPermlink(section)) return;
|
|
165
162
|
if (el.textContent === href) {
|
|
166
163
|
el.textContent = `@${author}/${section}`
|
|
167
164
|
}
|
|
@@ -4,7 +4,7 @@ export function img(el: HTMLElement, webp: boolean): void {
|
|
|
4
4
|
el.removeAttribute("width");
|
|
5
5
|
el.removeAttribute("height");
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
let src = el.getAttribute("src") || "";
|
|
8
8
|
|
|
9
9
|
// Normalize encoded characters
|
|
10
10
|
const decodedSrc = decodeURIComponent(
|
|
@@ -15,16 +15,14 @@ export function img(el: HTMLElement, webp: boolean): void {
|
|
|
15
15
|
// ❌ Remove if javascript or empty/invalid
|
|
16
16
|
const isInvalid = !src || decodedSrc.startsWith("javascript") || decodedSrc.startsWith("vbscript") || decodedSrc === "x";
|
|
17
17
|
if (isInvalid) {
|
|
18
|
-
|
|
19
|
-
return;
|
|
18
|
+
src = ""
|
|
20
19
|
}
|
|
21
20
|
|
|
22
|
-
// ❌ Skip relative paths (e.g., `photo.jpg`)
|
|
21
|
+
// ❌ Skip relative paths (e.g., `photo.jpg`, `./photo.png`, `assets/pic.jpeg`)
|
|
23
22
|
const isRelative = !/^https?:\/\//i.test(src) && !src.startsWith("/");
|
|
24
23
|
if (isRelative) {
|
|
25
|
-
console.warn("Skipped relative image:", src);
|
|
26
|
-
|
|
27
|
-
return;
|
|
24
|
+
//console.warn("Skipped relative image:", src);
|
|
25
|
+
src = ""
|
|
28
26
|
}
|
|
29
27
|
|
|
30
28
|
// Sanitize any dynamic or low-res src-like attributes
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { IMG_REGEX, SECTION_LIST } from '../consts'
|
|
2
2
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
3
|
+
import {isValidPermlink} from "../helper";
|
|
3
4
|
|
|
4
5
|
export function linkify(content: string, forApp: boolean, webp: boolean): string {
|
|
5
6
|
// Tags
|
|
@@ -33,13 +34,15 @@ export function linkify(content: string, forApp: boolean, webp: boolean): string
|
|
|
33
34
|
content = content.replace(
|
|
34
35
|
/((^|\s)(\/|)@[\w.\d-]+)\/(\S+)/gi, (match, u, p1, p2, p3) => {
|
|
35
36
|
const uu = u.trim().toLowerCase().replace('/@', '').replace('@', '');
|
|
36
|
-
const
|
|
37
|
+
const permlink = p3;
|
|
38
|
+
if (!isValidPermlink(permlink)) return match;
|
|
39
|
+
|
|
37
40
|
if (SECTION_LIST.some(v => p3.includes(v))) {
|
|
38
|
-
const attrs = forApp ? `https://ecency.com/@${uu}/${
|
|
39
|
-
return ` <a class="markdown-profile-link" ${attrs}>@${uu}/${
|
|
41
|
+
const attrs = forApp ? `https://ecency.com/@${uu}/${permlink}` : `href="/@${uu}/${permlink}"`
|
|
42
|
+
return ` <a class="markdown-profile-link" ${attrs}>@${uu}/${permlink}</a>`
|
|
40
43
|
} else {
|
|
41
|
-
const attrs = forApp ? `data-author="${uu}" data-tag="post" data-permlink="${
|
|
42
|
-
return ` <a class="markdown-post-link" ${attrs}>@${uu}/${
|
|
44
|
+
const attrs = forApp ? `data-author="${uu}" data-tag="post" data-permlink="${permlink}"` : `href="/post/@${uu}/${permlink}"`
|
|
45
|
+
return ` <a class="markdown-post-link" ${attrs}>@${uu}/${permlink}</a>`
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { IMG_REGEX, YOUTUBE_REGEX, WHITE_LIST, DOMParser, POST_REGEX, } from '../consts'
|
|
2
|
-
import { extractYtStartTime } from '../helper'
|
|
2
|
+
import { extractYtStartTime, isValidPermlink } from '../helper'
|
|
3
3
|
import { proxifyImageSrc } from '../proxify-image-src'
|
|
4
4
|
import { linkify } from './linkify.method'
|
|
5
5
|
|
|
@@ -59,6 +59,7 @@ export function text(node: HTMLElement, forApp: boolean, webp: boolean): void {
|
|
|
59
59
|
const tag = postMatch[2]
|
|
60
60
|
const author = postMatch[3].replace('@', '')
|
|
61
61
|
const permlink = postMatch[4]
|
|
62
|
+
if (!isValidPermlink(permlink)) return;
|
|
62
63
|
|
|
63
64
|
const attrs = forApp ? `data-tag="${tag}" data-author="${author}" data-permlink="${permlink}" class="markdown-post-link"` : `class="markdown-post-link" href="/${tag}/@${author}/${permlink}"`
|
|
64
65
|
const replaceNode = DOMParser.parseFromString(
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 10,
|
|
3
3
|
"input": "Lorem ipsum dolor <img src=x onerror=alert(x)> sit amet.\n\n<a href=\"javascript:void(0)\">etiam ut sollicitudin neque</a>\n\n<a onclick=\"console.log('ss')\">Vivamus pulvinar semper porttitor</a>",
|
|
4
|
-
"result": ""
|
|
4
|
+
"result": "<p dir=\"auto\">Lorem ipsum dolor <img /> sit amet.</p>\n<p dir=\"auto\"><a>etiam ut sollicitudin neque</a></p>\n<p dir=\"auto\"><a>Vivamus pulvinar semper porttitor</a></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 21,
|
|
3
3
|
"input": "javascript:/*--></title></style></textarea></script></xmp><svg/onload='+/\"/+/onmouseover=1/+/[*/[]/+alert(1)//'>\n\n<IMG SRC=\"javascript:alert('XSS');\"><img src=\"javascript:alert('XSS');\">\n\n<IMG SRC=javascript:alert('XSS')> <img src=javascript:alert('XSS')>",
|
|
4
|
-
"result": ""
|
|
4
|
+
"result": "<p dir=\"auto\">javascript:/<em>--><svg/onload='+/\"/+/onmouseover=1/+/[</em>/[]/+alert(1)//'></p>\n<p dir=\"auto\"><img /><img /></p>\n<p dir=\"auto\"><IMG SRC=javascript:alert('XSS')> <img src=javascript:alert('XSS')></p>"
|
|
5
5
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"id": 22,
|
|
3
3
|
"input": "<IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert("XSS")>\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": ""
|
|
4
|
+
"result": "<p dir=\"auto\"><IMG SRC=JaVaScRiPt:alert('XSS')> <IMG SRC=javascript:alert("XSS")><br />\n<IMG SRC=<code>javascript:alert(\"RSnake says, 'XSS'\")</code>><br />\n<a>xxs link</a><a>xxs link</a><br />\n<IMG \"\"\">alert("XSS")\"><img /><br />\n<IMG SRC= onmouseover=\"alert('xxs')\"></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=\"javascript:alert('XSS')\"> <IMG SRC=javascript:alert('XSS')> <IMG SRC=javascript:alert('XSS')> <IMG SRC=\"  javascript:alert('XSS');\">",
|
|
4
|
-
"result": ""
|
|
4
|
+
"result": "<p dir=\"auto\"><IMG SRC=/ onerror=\"alert(String.fromCharCode(88,83,83))\"> <img /> <IMG SRC=javascript:alert('XSS')> <img /> <img /></p>"
|
|
5
5
|
}
|