From af59ed027df3fec6caba85f48247a148b5b57225 Mon Sep 17 00:00:00 2001
From: Jay Swain <jswain@gitlab.com>
Date: Mon, 24 May 2021 17:49:42 -0700
Subject: [PATCH] What's New content link opens in new tab

What's new items have body content which is written in markdown. This
commit makes each link within the body content to open in a new tab.

part of:
https://gitlab.com/gitlab-org/growth/engineering/-/issues/5434

Changelog: changed
---
 .../javascripts/whats_new/components/feature.vue     |  6 +++++-
 app/models/release_highlight.rb                      |  2 +-
 spec/fixtures/whats_new/20201225_01_05.yml           |  2 +-
 spec/frontend/whats_new/components/feature_spec.js   | 12 +++++++++++-
 spec/models/release_highlight_spec.rb                |  6 +++---
 5 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/app/assets/javascripts/whats_new/components/feature.vue b/app/assets/javascripts/whats_new/components/feature.vue
index 5444e77a4d2cd..11096b0803232 100644
--- a/app/assets/javascripts/whats_new/components/feature.vue
+++ b/app/assets/javascripts/whats_new/components/feature.vue
@@ -30,6 +30,7 @@ export default {
       return dateInWords(date);
     },
   },
+  safeHtmlConfig: { ADD_ATTR: ['target'] },
 };
 </script>
 
@@ -71,7 +72,10 @@ export default {
         <gl-icon name="license" />{{ packageName }}
       </gl-badge>
     </div>
-    <div v-safe-html="feature.body" class="gl-pt-3 gl-line-height-20"></div>
+    <div
+      v-safe-html:[$options.safeHtmlConfig]="feature.body"
+      class="gl-pt-3 gl-line-height-20"
+    ></div>
     <gl-button
       :href="feature.url"
       target="_blank"
diff --git a/app/models/release_highlight.rb b/app/models/release_highlight.rb
index 9c30d0611e6d5..84e0a43670b78 100644
--- a/app/models/release_highlight.rb
+++ b/app/models/release_highlight.rb
@@ -33,7 +33,7 @@ def self.load_items(page:)
       next unless include_item?(item)
 
       begin
-        item.tap {|i| i['body'] = Kramdown::Document.new(i['body']).to_html }
+        item.tap {|i| i['body'] = Banzai.render(i['body'], { project: nil }) }
       rescue StandardError => e
         Gitlab::ErrorTracking.track_exception(e, file_path: file_path)
 
diff --git a/spec/fixtures/whats_new/20201225_01_05.yml b/spec/fixtures/whats_new/20201225_01_05.yml
index 27c8f989b085e..d707502af54df 100644
--- a/spec/fixtures/whats_new/20201225_01_05.yml
+++ b/spec/fixtures/whats_new/20201225_01_05.yml
@@ -1,7 +1,7 @@
 ---
 - title: bright and sunshinin' day
   body: |
-    ## bright and sunshinin' day
+    bright and sunshinin' [day](https://en.wikipedia.org/wiki/Day)
   self-managed: true
   gitlab-com: false
   packages: ["Premium", "Ultimate"]
diff --git a/spec/frontend/whats_new/components/feature_spec.js b/spec/frontend/whats_new/components/feature_spec.js
index 9e9cb59c0d687..8f4b4b08f5067 100644
--- a/spec/frontend/whats_new/components/feature_spec.js
+++ b/spec/frontend/whats_new/components/feature_spec.js
@@ -8,7 +8,7 @@ describe("What's new single feature", () => {
   const exampleFeature = {
     title: 'Compliance pipeline configurations',
     body:
-      '<p>We are thrilled to announce that it is now possible to define enforceable pipelines that will run for any project assigned a corresponding compliance framework.</p>',
+      '<p data-testid="body-content">We are thrilled to announce that it is now possible to define enforceable pipelines that will run for any project assigned a corresponding <a href="https://en.wikipedia.org/wiki/Compliance_(psychology)" target="_blank" rel="noopener noreferrer" onload="alert(xss)">compliance</a> framework.</p>',
     stage: 'Manage',
     'self-managed': true,
     'gitlab-com': true,
@@ -20,6 +20,7 @@ describe("What's new single feature", () => {
   };
 
   const findReleaseDate = () => wrapper.find('[data-testid="release-date"]');
+  const findBodyAnchor = () => wrapper.find('[data-testid="body-content"] a');
 
   const createWrapper = ({ feature } = {}) => {
     wrapper = shallowMount(Feature, {
@@ -43,4 +44,13 @@ describe("What's new single feature", () => {
       expect(findReleaseDate().exists()).toBe(false);
     });
   });
+
+  it('safe-html config allows target attribute on elements', () => {
+    createWrapper({ feature: exampleFeature });
+    expect(findBodyAnchor().attributes()).toEqual({
+      href: expect.any(String),
+      rel: 'noopener noreferrer',
+      target: '_blank',
+    });
+  });
 });
diff --git a/spec/models/release_highlight_spec.rb b/spec/models/release_highlight_spec.rb
index b4dff4c33ff61..a5441e2f47bd4 100644
--- a/spec/models/release_highlight_spec.rb
+++ b/spec/models/release_highlight_spec.rb
@@ -67,12 +67,12 @@
         expect(subject[:next_page]).to eq(2)
       end
 
-      it 'parses the body as markdown and returns html' do
-        expect(subject[:items].first['body']).to match("<h2 id=\"bright-and-sunshinin-day\">bright and sunshinin’ day</h2>")
+      it 'parses the body as markdown and returns html, and links are target="_blank"' do
+        expect(subject[:items].first['body']).to match('<p data-sourcepos="1:1-1:62" dir="auto">bright and sunshinin\' <a href="https://en.wikipedia.org/wiki/Day" rel="nofollow noreferrer noopener" target="_blank">day</a></p>')
       end
 
       it 'logs an error if theres an error parsing markdown for an item, and skips it' do
-        allow(Kramdown::Document).to receive(:new).and_raise
+        allow(Banzai).to receive(:render).and_raise
 
         expect(Gitlab::ErrorTracking).to receive(:track_exception)
         expect(subject[:items]).to be_empty
-- 
GitLab