diff --git a/app/assets/javascripts/lib/dompurify.js b/app/assets/javascripts/lib/dompurify.js
index 3861007d64640a3a72faed0ab0a8214fd234c0e8..f5038c5a62ff6c621236e780216cf9334ab728f2 100644
--- a/app/assets/javascripts/lib/dompurify.js
+++ b/app/assets/javascripts/lib/dompurify.js
@@ -125,6 +125,18 @@ addHook('beforeSanitizeAttributes', (node, _, config) => {
}
});
+// Permit "title", "data-name" and "data-unicode-version" attributes on
+// (when allowed), even when ALLOW_DATA_ATTR is false.
+addHook('uponSanitizeAttribute', (node, hookEvent) => {
+ if (
+ node.tagName === 'GL-EMOJI' &&
+ ['title', 'data-name', 'data-unicode-version'].includes(hookEvent.attrName)
+ ) {
+ // eslint-disable-next-line no-param-reassign
+ hookEvent.forceKeepAttr = true;
+ }
+});
+
addHook('afterSanitizeAttributes', (node, _, config) => {
if (node.tagName === 'A' && node.hasAttribute(TEMPORARY_ATTRIBUTE)) {
node.setAttribute('target', node.getAttribute(TEMPORARY_ATTRIBUTE));
diff --git a/lib/banzai/pipeline/gfm_pipeline.rb b/lib/banzai/pipeline/gfm_pipeline.rb
index 1933119126bd8d69100447000427589c27bb79ae..b9ef83eb498fb4ad697fc4099b742ed97111c1ab 100644
--- a/lib/banzai/pipeline/gfm_pipeline.rb
+++ b/lib/banzai/pipeline/gfm_pipeline.rb
@@ -12,12 +12,15 @@ class GfmPipeline < BasePipeline
def self.filters
@filters ||= FilterArray[
Filter::CodeLanguageFilter,
- Filter::JsonTableFilter, # process before sanitization
+ Filter::JsonTableFilter,
Filter::PlantumlFilter,
- # Must always be before the SanitizationFilter/SanitizeLinkFilter to prevent XSS attacks
Filter::SpacedLinkFilter,
+ # ======== Sanitization boundary ========
+ # Items above this point must not be moved below this point, as they depend
+ # on running before SanitizationFilter and SanitizeLinkFilter for safety.
Filter::SanitizationFilter,
Filter::SanitizeLinkFilter,
+ # =======================================
Filter::KrokiFilter,
Filter::GollumTagsFilter,
Filter::WikiLinkGollumFilter,
diff --git a/spec/frontend/lib/dompurify_spec.js b/spec/frontend/lib/dompurify_spec.js
index 9178aa65689d3ce6cbff0b740fedad30ae139854..f475c1288efa3db835f984e6d8af409458e0872e 100644
--- a/spec/frontend/lib/dompurify_spec.js
+++ b/spec/frontend/lib/dompurify_spec.js
@@ -231,4 +231,17 @@ describe('~/lib/dompurify', () => {
expect(el.hasAttribute('rel')).toBe(false);
});
});
+
+ describe('gl-emoji tags', () => {
+ it('does not remove the title, data-name, or data-unicode-version attributes', () => {
+ const html = `👍`;
+ expect(sanitize(html)).toBe(html);
+ });
+
+ it('does not remove the data-name or data-unicode-version attributes even when ALLOW_DATA_ATTR is false', () => {
+ const input = `👍`;
+ const expected = `👍`;
+ expect(sanitize(input, { ...defaultConfig, ALLOW_DATA_ATTR: false })).toBe(expected);
+ });
+ });
});
diff --git a/spec/lib/banzai/filter/json_table_filter_spec.rb b/spec/lib/banzai/filter/json_table_filter_spec.rb
index 799a3303a350c7a32af5012f7d6f9bca0a31c873..87a5245a3bece53d41113d2de0632d5a390e2fe2 100644
--- a/spec/lib/banzai/filter/json_table_filter_spec.rb
+++ b/spec/lib/banzai/filter/json_table_filter_spec.rb
@@ -23,7 +23,7 @@
],
"items": [
{
- "starts_at": "_2024-10-07_"
+ "starts_at": "_2024-10-07_ :white_check_mark: 👍"
},
{
"url": "https://example.com/page2.html"
@@ -50,7 +50,8 @@
- | 2024-10-07 |
+
+ 2024-10-07 :white_check_mark: 👍 |
|