about summary refs log tree commit diff
diff options
context:
space:
mode:
authorClaire <claire.github-309c@sitedethib.com>2022-11-14 22:43:56 +0100
committerGitHub <noreply@github.com>2022-11-14 22:43:56 +0100
commite589afa0ef520747919935956d2d87746000adc9 (patch)
treeb4df6d9329686bebd09cbfcc107b8fe32c413af3
parent7a8cd0cb0a61d5091071d682bfb263247a324d3a (diff)
parent8aa56aedfd5cb5a2caa4dd070fb6a233e960dbec (diff)
Merge pull request #1929 from ClearlyClaire/glitch-soc/merge-upstream
Merge upstream changes
-rw-r--r--CHANGELOG.md11
-rw-r--r--app/javascript/flavours/glitch/features/emoji/emoji.js15
-rw-r--r--app/javascript/mastodon/features/emoji/__tests__/emoji-test.js42
-rw-r--r--app/javascript/mastodon/features/emoji/emoji.js15
-rw-r--r--lib/mastodon/version.rb4
5 files changed, 55 insertions, 32 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 20da71f80..4392cc658 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,7 +3,12 @@ Changelog
 
 All notable changes to this project will be documented in this file.
 
-## [Unreleased]
+## [4.0.1] - 2022-11-14
+### Fixed
+
+- Fix nodes order being sometimes mangled when rewriting emoji ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20677))
+
+## [4.0.0] - 2022-11-14
 
 Some of the features in this release have been funded through the [NGI0 Discovery](https://nlnet.nl/discovery) Fund, a fund established by [NLnet](https://nlnet.nl/) with financial support from the European Commission's [Next Generation Internet](https://ngi.eu/) programme, under the aegis of DG Communications Networks, Content and Technology under grant agreement No 825322.
 
@@ -196,6 +201,10 @@ Some of the features in this release have been funded through the [NGI0 Discover
 ### Security
 
 - Fix being able to spoof link verification ([Gargron](https://github.com/mastodon/mastodon/pull/20217))
+- Fix emoji substitution not applying only to text nodes in backend code ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20641))
+- Fix emoji substitution not applying only to text nodes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/20640))
+- Fix rate limiting for paths with formats ([Gargron](https://github.com/mastodon/mastodon/pull/20675))
+- Fix out-of-bound reads in blurhash transcoder ([delroth](https://github.com/mastodon/mastodon/pull/20388))
 
 ## [3.5.3] - 2022-05-26
 ### Added
diff --git a/app/javascript/flavours/glitch/features/emoji/emoji.js b/app/javascript/flavours/glitch/features/emoji/emoji.js
index 6138164e0..880f9401d 100644
--- a/app/javascript/flavours/glitch/features/emoji/emoji.js
+++ b/app/javascript/flavours/glitch/features/emoji/emoji.js
@@ -19,10 +19,13 @@ const emojiFilename = (filename) => {
   return borderedEmoji.includes(filename) ? (filename + '_border') : filename;
 };
 
+const domParser = new DOMParser();
+
 const emojifyTextNode = (node, customEmojis) => {
-  const parentElement = node.parentElement;
   let str = node.textContent;
 
+  const fragment = new DocumentFragment();
+
   for (;;) {
     let match, i = 0;
 
@@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => {
       }
     }
 
+    fragment.append(document.createTextNode(str.slice(0, i)));
+    if (replacement) {
+      fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]);
+    }
     node.textContent = str.slice(0, i);
-    parentElement.insertAdjacentHTML('beforeend', replacement);
     str = str.slice(rend);
-    node = document.createTextNode(str);
-    parentElement.append(node);
   }
+
+  fragment.append(document.createTextNode(str));
+  node.parentElement.replaceChild(fragment, node);
 };
 
 const emojifyNode = (node, customEmojis) => {
diff --git a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
index 07b3d8c53..2f19aab7e 100644
--- a/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
+++ b/app/javascript/mastodon/features/emoji/__tests__/emoji-test.js
@@ -11,8 +11,8 @@ describe('emoji', () => {
     });
 
     it('works with unclosed tags', () => {
-      expect(emojify('hello>')).toEqual('hello>');
-      expect(emojify('<hello')).toEqual('<hello');
+      expect(emojify('hello>')).toEqual('hello&gt;');
+      expect(emojify('<hello')).toEqual('');
     });
 
     it('works with unclosed shortcodes', () => {
@@ -22,23 +22,23 @@ describe('emoji', () => {
 
     it('does unicode', () => {
       expect(emojify('\uD83D\uDC69\u200D\uD83D\uDC69\u200D\uD83D\uDC66\u200D\uD83D\uDC66')).toEqual(
-        '<img draggable="false" class="emojione" alt="๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg" />');
+        '<img draggable="false" class="emojione" alt="๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆโ€๐Ÿ‘ฆ" title=":woman-woman-boy-boy:" src="/emoji/1f469-200d-1f469-200d-1f466-200d-1f466.svg">');
       expect(emojify('๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง')).toEqual(
-        '<img draggable="false" class="emojione" alt="๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg" />');
-      expect(emojify('๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg" />');
+        '<img draggable="false" class="emojione" alt="๐Ÿ‘จโ€๐Ÿ‘ฉโ€๐Ÿ‘งโ€๐Ÿ‘ง" title=":man-woman-girl-girl:" src="/emoji/1f468-200d-1f469-200d-1f467-200d-1f467.svg">');
+      expect(emojify('๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘ฉโ€๐Ÿ‘ฉโ€๐Ÿ‘ฆ" title=":woman-woman-boy:" src="/emoji/1f469-200d-1f469-200d-1f466.svg">');
       expect(emojify('\u2757')).toEqual(
-        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" />');
+        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg">');
     });
 
     it('does multiple unicode', () => {
       expect(emojify('\u2757 #\uFE0F\u20E3')).toEqual(
-        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg" />');
+        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg">');
       expect(emojify('\u2757#\uFE0F\u20E3')).toEqual(
-        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" /><img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg" />');
+        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg"><img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg">');
       expect(emojify('\u2757 #\uFE0F\u20E3 \u2757')).toEqual(
-        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg" /> <img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" />');
+        '<img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg"> <img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg">');
       expect(emojify('foo \u2757 #\uFE0F\u20E3 bar')).toEqual(
-        'foo <img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg" /> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg" /> bar');
+        'foo <img draggable="false" class="emojione" alt="โ—" title=":exclamation:" src="/emoji/2757.svg"> <img draggable="false" class="emojione" alt="#๏ธโƒฃ" title=":hash:" src="/emoji/23-20e3.svg"> bar');
     });
 
     it('ignores unicode inside of tags', () => {
@@ -46,16 +46,16 @@ describe('emoji', () => {
     });
 
     it('does multiple emoji properly (issue 5188)', () => {
-      expect(emojify('๐Ÿ‘Œ๐ŸŒˆ๐Ÿ’•')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘Œ" title=":ok_hand:" src="/emoji/1f44c.svg" /><img draggable="false" class="emojione" alt="๐ŸŒˆ" title=":rainbow:" src="/emoji/1f308.svg" /><img draggable="false" class="emojione" alt="๐Ÿ’•" title=":two_hearts:" src="/emoji/1f495.svg" />');
-      expect(emojify('๐Ÿ‘Œ ๐ŸŒˆ ๐Ÿ’•')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘Œ" title=":ok_hand:" src="/emoji/1f44c.svg" /> <img draggable="false" class="emojione" alt="๐ŸŒˆ" title=":rainbow:" src="/emoji/1f308.svg" /> <img draggable="false" class="emojione" alt="๐Ÿ’•" title=":two_hearts:" src="/emoji/1f495.svg" />');
+      expect(emojify('๐Ÿ‘Œ๐ŸŒˆ๐Ÿ’•')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘Œ" title=":ok_hand:" src="/emoji/1f44c.svg"><img draggable="false" class="emojione" alt="๐ŸŒˆ" title=":rainbow:" src="/emoji/1f308.svg"><img draggable="false" class="emojione" alt="๐Ÿ’•" title=":two_hearts:" src="/emoji/1f495.svg">');
+      expect(emojify('๐Ÿ‘Œ ๐ŸŒˆ ๐Ÿ’•')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘Œ" title=":ok_hand:" src="/emoji/1f44c.svg"> <img draggable="false" class="emojione" alt="๐ŸŒˆ" title=":rainbow:" src="/emoji/1f308.svg"> <img draggable="false" class="emojione" alt="๐Ÿ’•" title=":two_hearts:" src="/emoji/1f495.svg">');
     });
 
     it('does an emoji that has no shortcode', () => {
-      expect(emojify('๐Ÿ‘โ€๐Ÿ—จ')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘โ€๐Ÿ—จ" title="" src="/emoji/1f441-200d-1f5e8.svg" />');
+      expect(emojify('๐Ÿ‘โ€๐Ÿ—จ')).toEqual('<img draggable="false" class="emojione" alt="๐Ÿ‘โ€๐Ÿ—จ" title="" src="/emoji/1f441-200d-1f5e8.svg">');
     });
 
     it('does an emoji whose filename is irregular', () => {
-      expect(emojify('โ†™๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ†™๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg" />');
+      expect(emojify('โ†™๏ธ')).toEqual('<img draggable="false" class="emojione" alt="โ†™๏ธ" title=":arrow_lower_left:" src="/emoji/2199.svg">');
     });
 
     it('avoid emojifying on invisible text', () => {
@@ -67,26 +67,26 @@ describe('emoji', () => {
 
     it('avoid emojifying on invisible text with nested tags', () => {
       expect(emojify('<span class="invisible">๐Ÿ˜„<span class="foo">bar</span>๐Ÿ˜ด</span>๐Ÿ˜‡'))
-        .toEqual('<span class="invisible">๐Ÿ˜„<span class="foo">bar</span>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg" />');
+        .toEqual('<span class="invisible">๐Ÿ˜„<span class="foo">bar</span>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg">');
       expect(emojify('<span class="invisible">๐Ÿ˜„<span class="invisible">๐Ÿ˜•</span>๐Ÿ˜ด</span>๐Ÿ˜‡'))
-        .toEqual('<span class="invisible">๐Ÿ˜„<span class="invisible">๐Ÿ˜•</span>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg" />');
-      expect(emojify('<span class="invisible">๐Ÿ˜„<br/>๐Ÿ˜ด</span>๐Ÿ˜‡'))
-        .toEqual('<span class="invisible">๐Ÿ˜„<br/>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg" />');
+        .toEqual('<span class="invisible">๐Ÿ˜„<span class="invisible">๐Ÿ˜•</span>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg">');
+      expect(emojify('<span class="invisible">๐Ÿ˜„<br>๐Ÿ˜ด</span>๐Ÿ˜‡'))
+        .toEqual('<span class="invisible">๐Ÿ˜„<br>๐Ÿ˜ด</span><img draggable="false" class="emojione" alt="๐Ÿ˜‡" title=":innocent:" src="/emoji/1f607.svg">');
     });
 
     it('skips the textual presentation VS15 character', () => {
       expect(emojify('โœด๏ธŽ')) // This is U+2734 EIGHT POINTED BLACK STAR then U+FE0E VARIATION SELECTOR-15
-        .toEqual('<img draggable="false" class="emojione" alt="โœด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg" />');
+        .toEqual('<img draggable="false" class="emojione" alt="โœด" title=":eight_pointed_black_star:" src="/emoji/2734_border.svg">');
     });
 
     it('does an simple emoji properly', () => {
       expect(emojify('โ™€โ™‚'))
-        .toEqual('<img draggable="false" class="emojione" alt="โ™€" title=":female_sign:" src="/emoji/2640.svg" /><img draggable="false" class="emojione" alt="โ™‚" title=":male_sign:" src="/emoji/2642.svg" />');
+        .toEqual('<img draggable="false" class="emojione" alt="โ™€" title=":female_sign:" src="/emoji/2640.svg"><img draggable="false" class="emojione" alt="โ™‚" title=":male_sign:" src="/emoji/2642.svg">');
     });
 
     it('does an emoji containing ZWJ properly', () => {
       expect(emojify('๐Ÿ’‚โ€โ™€๏ธ๐Ÿ’‚โ€โ™‚๏ธ'))
-        .toEqual('<img draggable="false" class="emojione" alt="๐Ÿ’‚\u200Dโ™€๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg" /><img draggable="false" class="emojione" alt="๐Ÿ’‚\u200Dโ™‚๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg" />');
+        .toEqual('<img draggable="false" class="emojione" alt="๐Ÿ’‚\u200Dโ™€๏ธ" title=":female-guard:" src="/emoji/1f482-200d-2640-fe0f_border.svg"><img draggable="false" class="emojione" alt="๐Ÿ’‚\u200Dโ™‚๏ธ" title=":male-guard:" src="/emoji/1f482-200d-2642-fe0f_border.svg">');
     });
   });
 });
diff --git a/app/javascript/mastodon/features/emoji/emoji.js b/app/javascript/mastodon/features/emoji/emoji.js
index 0ab32767a..52a8458fb 100644
--- a/app/javascript/mastodon/features/emoji/emoji.js
+++ b/app/javascript/mastodon/features/emoji/emoji.js
@@ -19,10 +19,13 @@ const emojiFilename = (filename) => {
   return borderedEmoji.includes(filename) ? (filename + '_border') : filename;
 };
 
+const domParser = new DOMParser();
+
 const emojifyTextNode = (node, customEmojis) => {
-  const parentElement = node.parentElement;
   let str = node.textContent;
 
+  const fragment = new DocumentFragment();
+
   for (;;) {
     let match, i = 0;
 
@@ -64,12 +67,16 @@ const emojifyTextNode = (node, customEmojis) => {
       }
     }
 
+    fragment.append(document.createTextNode(str.slice(0, i)));
+    if (replacement) {
+      fragment.append(domParser.parseFromString(replacement, 'text/html').documentElement.getElementsByTagName('img')[0]);
+    }
     node.textContent = str.slice(0, i);
-    parentElement.insertAdjacentHTML('beforeend', replacement);
     str = str.slice(rend);
-    node = document.createTextNode(str);
-    parentElement.append(node);
   }
+
+  fragment.append(document.createTextNode(str));
+  node.parentElement.replaceChild(fragment, node);
 };
 
 const emojifyNode = (node, customEmojis) => {
diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb
index 86be34944..b41188814 100644
--- a/lib/mastodon/version.rb
+++ b/lib/mastodon/version.rb
@@ -13,11 +13,11 @@ module Mastodon
     end
 
     def patch
-      0
+      1
     end
 
     def flags
-      'rc4'
+      ''
     end
 
     def suffix