소스 검색

Fixes #2814. Links are mangled on open/copy

JC Brand 3 년 전
부모
커밋
9a2424fa26
6개의 변경된 파일37개의 추가작업 그리고 27개의 파일을 삭제
  1. 1 0
      CHANGES.md
  2. 18 2
      src/plugins/chatview/tests/messages.js
  3. 2 2
      src/plugins/chatview/tests/styling.js
  4. 10 8
      src/plugins/chatview/tests/xss.js
  5. 1 1
      src/plugins/muc-views/tests/mep.js
  6. 5 14
      src/templates/hyperlink.js

+ 1 - 0
CHANGES.md

@@ -14,6 +14,7 @@
 - #2786: Fix webpack configuration not working on Windows OS
 - #2788: `TypeError` when trying to use `@converse/headless`
 - #2789: Implement new hook `parseMessageForCommands` for plugins to add custom commands
+- #2814: Links are mangled on open/copy
 
 
 ## 9.0.0 (2021-11-26)

+ 18 - 2
src/plugins/chatview/tests/messages.js

@@ -521,7 +521,7 @@ describe("A Chat Message", function () {
         const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
         expect(msg.textContent).toEqual(message);
         await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
-            'This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
+        'This message contains a hyperlink: <a target="_blank" rel="noopener" href="http://www.opkode.com">www.opkode.com</a>');
     }));
 
     it("will remove url query parameters from hyperlinks as set",
@@ -552,6 +552,22 @@ describe("A Chat Message", function () {
             '<a target="_blank" rel="noopener" href="https://www.opkode.com/?id=0&amp;utm_content=1&amp;s=1">https://www.opkode.com/?id=0&amp;utm_content=1&amp;s=1</a>');
     }));
 
+    it("properly renders URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+
+        await mock.waitForRoster(_converse, 'current');
+        await mock.openControlBox(_converse);
+        const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
+        await mock.openChatBoxFor(_converse, contact_jid);
+        const view = _converse.chatboxviews.get(contact_jid);
+        const message = 'https://mov.im/?node/pubsub.movim.eu/Dino/urn-uuid-979bd24f-0bf3-5099-9fa7-510b9ce9a884';
+        await mock.sendMessage(view, message);
+        await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length);
+        const msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
+        const anchor = await u.waitUntil(() => msg.querySelector('a'));
+        expect(anchor.innerHTML.replace(/<!-.*?->/g, '')).toBe(message);
+        expect(anchor.getAttribute('href')).toBe(message);
+    }));
+
     it("will render newlines", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
         await mock.waitForRoster(_converse, 'current');
         const contact_jid = mock.cur_names[0].replace(/ /g,'.').toLowerCase() + '@montague.lit';
@@ -595,7 +611,7 @@ describe("A Chat Message", function () {
         await u.waitUntil(() => view.querySelectorAll('.chat-msg__text').length === 4);
         await u.waitUntil(() => {
             const text = view.querySelector('converse-chat-message:last-child .chat-msg__text').innerHTML.replace(/<!-.*?->/g, '');
-            return text === 'Hey\nHave you heard\n\u200B\nthe news?\n<a target="_blank" rel="noopener" href="https://conversejs.org/">https://conversejs.org</a>';
+            return text === 'Hey\nHave you heard\n\u200B\nthe news?\n<a target="_blank" rel="noopener" href="https://conversejs.org">https://conversejs.org</a>';
         });
     }));
 

+ 2 - 2
src/plugins/chatview/tests/styling.js

@@ -106,7 +106,7 @@ describe("An incoming chat Message", function () {
         expect(msg_el.innerText).toBe(msg_text);
         await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
             '<span class="styling-directive">~</span>'+
-            '<del>Check out this site: <a target="_blank" rel="noopener" href="https://conversejs.org/">https://conversejs.org</a></del>'+
+            '<del>Check out this site: <a target="_blank" rel="noopener" href="https://conversejs.org">https://conversejs.org</a></del>'+
             '<span class="styling-directive">~</span>');
 
         // Images inside directives aren't shown inline
@@ -182,7 +182,7 @@ describe("An incoming chat Message", function () {
         expect(msg_el.innerText).toBe(msg_text);
         await u.waitUntil(() => msg_el.innerHTML.replace(/<!-.*?->/g, '') ===
             'Go to <span class="styling-directive">_</span>'+
-            '<i><a target="_blank" rel="noopener" href="https://converse_js.org/">https://converse_js.org</a></i>'+
+            '<i><a target="_blank" rel="noopener" href="https://converse_js.org">https://converse_js.org</a></i>'+
             '<span class="styling-directive">_</span> <span class="styling-directive">_</span><i>please</i><span class="styling-directive">_</span>');
 
     }));

+ 10 - 8
src/plugins/chatview/tests/xss.js

@@ -115,8 +115,7 @@ describe("XSS", function () {
             expect(window.alert).not.toHaveBeenCalled();
         }));
 
-        it("will have properly escaped URLs",
-                mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
+        it("will have properly escaped URLs", mock.initConverse(['chatBoxesFetched'], {}, async function (_converse) {
 
             await mock.waitForRoster(_converse, 'current');
             await mock.openControlBox(_converse);
@@ -132,21 +131,24 @@ describe("XSS", function () {
             expect(msg.textContent).toEqual(message);
             expect(msg.innerHTML.replace(/<!-.*?->/g, ''))
                 .toEqual('http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever');
+
+
             await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
-                '<a target="_blank" rel="noopener" href="http://www.opkode.com/%27onmouseover=%27alert%281%29%27whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>');
+                `<a target="_blank" rel="noopener" href="http://www.opkode.com/'onmouseover='alert(1)'whatever">http://www.opkode.com/\'onmouseover=\'alert(1)\'whatever</a>`);
 
             message = 'http://www.opkode.com/"onmouseover="alert(1)"whatever';
             await mock.sendMessage(view, message);
             msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
             expect(msg.textContent).toEqual(message);
             await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
-                '<a target="_blank" rel="noopener" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>');
+                `<a target="_blank" rel="noopener" href="http://www.opkode.com/&quot;onmouseover=&quot;alert(1)&quot;whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>`);
 
             message = "https://en.wikipedia.org/wiki/Ender's_Game";
             await mock.sendMessage(view, message);
             msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
             expect(msg.textContent).toEqual(message);
-            await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') === '<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender%27s_Game">'+message+'</a>');
+            await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
+                `<a target="_blank" rel="noopener" href="https://en.wikipedia.org/wiki/Ender's_Game">https://en.wikipedia.org/wiki/Ender's_Game</a>`);
 
             message = "<https://bugs.documentfoundation.org/show_bug.cgi?id=123737>";
             await mock.sendMessage(view, message);
@@ -160,14 +162,14 @@ describe("XSS", function () {
             msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
             expect(msg.textContent).toEqual(message);
             await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
-                '&lt;<a target="_blank" rel="noopener" href="http://www.opkode.com/%22onmouseover=%22alert%281%29%22whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>&gt;');
+                `&lt;<a target="_blank" rel="noopener" href="http://www.opkode.com/&quot;onmouseover=&quot;alert(1)&quot;whatever">http://www.opkode.com/"onmouseover="alert(1)"whatever</a>&gt;`);
 
             message = `https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2`
             await mock.sendMessage(view, message);
             msg = sizzle('.chat-content .chat-msg:last .chat-msg__text', view).pop();
             expect(msg.textContent).toEqual(message);
             await u.waitUntil(() => msg.innerHTML.replace(/<!-.*?->/g, '') ===
-                `<a target="_blank" rel="noopener" href="https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=%213m6%211e1%213m4%211sQ7SdHo_bPLPlLlU8GSGWaQ%212e0%217i13312%218i6656%214m5%213m4%211s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08%218m2%213d52.3773668%214d4.5489388%215m1%211e2">https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2</a>`);
+                `<a target="_blank" rel="noopener" href="https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2">https://www.google.com/maps/place/Kochstraat+6,+2041+CE+Zandvoort/@52.3775999,4.548971,3a,15y,170.85h,88.39t/data=!3m6!1e1!3m4!1sQ7SdHo_bPLPlLlU8GSGWaQ!2e0!7i13312!8i6656!4m5!3m4!1s0x47c5ec1e56f845ad:0x1de0bc4a5771fb08!8m2!3d52.3773668!4d4.5489388!5m1!1e2</a>`);
         }));
 
         it("will avoid malformed and unsafe urls urls from rendering as anchors",
@@ -187,7 +189,7 @@ describe("XSS", function () {
 
             const good_urls =[{
                 entered: 'http://www.google.com',
-                href: 'http://www.google.com/'
+                href: 'http://www.google.com'
             }, {
                 entered: 'https://www.google.com/',
                 href: 'https://www.google.com/'

+ 1 - 1
src/plugins/muc-views/tests/mep.js

@@ -163,7 +163,7 @@ describe("A XEP-0316 MEP notification", function () {
         await u.waitUntil(() => view.querySelectorAll('.chat-info').length === 1, 1000);
         expect(view.querySelector('.chat-info__message converse-rich-text').textContent.trim()).toBe(msg);
         expect(view.querySelector('.reason converse-rich-text').innerHTML.replace(/<!-.*?->/g, '').trim()).toBe(
-            'Check out <a target="_blank" rel="noopener" href="https://conversejs.org/">https://conversejs.org</a>');
+            'Check out <a target="_blank" rel="noopener" href="https://conversejs.org">https://conversejs.org</a>');
     }));
 
     it("can be retracted by a moderator",

+ 5 - 14
src/templates/hyperlink.js

@@ -1,4 +1,3 @@
-import log from '@converse/headless/log';
 import { api } from  "@converse/headless/core";
 import { html } from "lit";
 
@@ -8,24 +7,16 @@ function onClickXMPPURI (ev) {
 }
 
 export default (uri, url_text) => {
-    let normalized_url;
-    try {
-        normalized_url = uri.normalize()._string;
-    } catch (e) {
-        log.error(e);
-        return url_text;
-    }
-    const pretty_url = uri._parts.urn ? normalized_url : uri.readable();
-    const visible_url = url_text || pretty_url;
-    if (!uri._parts.protocol && !normalized_url.startsWith('http://') && !normalized_url.startsWith('https://')) {
-        normalized_url = 'http://' + normalized_url;
+    let href_text = url_text;
+    if (!uri._parts.protocol && !url_text.startsWith('http://') && !url_text.startsWith('https://')) {
+        href_text = 'http://' + url_text;
     }
     if (uri._parts.protocol === 'xmpp' && uri._parts.query === 'join') {
         return html`
             <a target="_blank"
                rel="noopener"
                @click=${onClickXMPPURI}
-               href="${normalized_url}">${visible_url}</a>`;
+               href="${href_text}">${url_text}</a>`;
     }
-    return html`<a target="_blank" rel="noopener" href="${normalized_url}">${visible_url}</a>`;
+    return html`<a target="_blank" rel="noopener" href="${href_text}">${url_text}</a>`;
 }