diff --git a/CHANGELOG b/CHANGELOG
index 99dd1d3ff4a43bfd41f736ee9b90b14871714279..364690286e11ccba1028e637ad059e8b4b142ef0 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -13,6 +13,7 @@ v 8.9.0 (unreleased)
   - Fix issue todo not remove when leave project !4150 (Long Nguyen)
   - Allow customisable text on the 'nearly there' page after a user signs up
   - Bump recaptcha gem to 3.0.0 to remove deprecated stoken support
+  - Fix SVG sanitizer to allow more elements
   - Allow forking projects with restricted visibility level
   - Added descriptions to notification settings dropdown
   - Improve note validation to prevent errors when creating invalid note via API
diff --git a/lib/gitlab/sanitizers/svg.rb b/lib/gitlab/sanitizers/svg.rb
index 5e95f6c05290ec35788eb8bc5674c84304072dd1..8304b9a482c36b40c9dd841abdd73cc13e9bcf0a 100644
--- a/lib/gitlab/sanitizers/svg.rb
+++ b/lib/gitlab/sanitizers/svg.rb
@@ -12,23 +12,45 @@ module Gitlab
         def scrub(node)
           unless Whitelist::ALLOWED_ELEMENTS.include?(node.name)
             node.unlink
-          else
-            node.attributes.each do |attr_name, attr|
-              valid_attributes = Whitelist::ALLOWED_ATTRIBUTES[node.name]
-
-              unless valid_attributes && valid_attributes.include?(attr_name)
-                if Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name) &&
-                    attr_name.start_with?('data-')
-                  # Arbitrary data attributes are allowed. Verify that the attribute
-                  # is a valid data attribute.
-                  attr.unlink unless attr_name =~ DATA_ATTR_PATTERN
-                else
-                  attr.unlink
-                end
+            return
+          end
+
+          valid_attributes = Whitelist::ALLOWED_ATTRIBUTES[node.name]
+          return unless valid_attributes
+
+          node.attribute_nodes.each do |attr|
+            attr_name = attribute_name_with_namespace(attr)
+
+            if valid_attributes.include?(attr_name)
+              attr.unlink if unsafe_href?(attr)
+            else
+              # Arbitrary data attributes are allowed.
+              unless allows_data_attribute?(node) && data_attribute?(attr)
+                attr.unlink
               end
             end
           end
         end
+
+        def attribute_name_with_namespace(attr)
+          if attr.namespace
+            "#{attr.namespace.prefix}:#{attr.name}"
+          else
+            attr.name
+          end
+        end
+
+        def allows_data_attribute?(node)
+          Whitelist::ALLOWED_DATA_ATTRIBUTES_IN_ELEMENTS.include?(node.name)
+        end
+
+        def unsafe_href?(attr)
+          attribute_name_with_namespace(attr) == 'xlink:href' && !attr.value.start_with?('#')
+        end
+
+        def data_attribute?(attr)
+          attr.name.start_with?('data-') && attr.name =~ DATA_ATTR_PATTERN && attr.namespace.nil?
+        end
       end
     end
   end
diff --git a/spec/lib/gitlab/sanitizers/svg_spec.rb b/spec/lib/gitlab/sanitizers/svg_spec.rb
new file mode 100644
index 0000000000000000000000000000000000000000..030c2063ab20e729e2f868f2a661bc25b5d30cc5
--- /dev/null
+++ b/spec/lib/gitlab/sanitizers/svg_spec.rb
@@ -0,0 +1,94 @@
+require 'spec_helper'
+
+describe Gitlab::Sanitizers::SVG do
+  let(:scrubber) { Gitlab::Sanitizers::SVG::Scrubber.new }
+  let(:namespace) { double(Nokogiri::XML::Namespace, prefix: 'xlink', href: 'http://www.w3.org/1999/xlink') }
+  let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: '#awesome_id') }
+
+  describe '.clean' do
+    let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') }
+    let(:data) { open(input_svg_path).read }
+    let(:sanitized_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') }
+    let(:sanitized) { open(sanitized_svg_path).read }
+
+    it 'delegates sanitization to scrubber' do
+      expect_any_instance_of(Gitlab::Sanitizers::SVG::Scrubber).to receive(:scrub).at_least(:once)
+      described_class.clean(data)
+    end
+
+    it 'returns sanitized data' do
+      expect(described_class.clean(data)).to eq(sanitized)
+    end
+  end
+
+  context 'scrubber' do
+    describe '#scrub' do
+      let(:invalid_element) { double(Nokogiri::XML::Node, name: 'invalid', value: 'invalid') }
+      let(:invalid_attribute) { double(Nokogiri::XML::Attr, name: 'invalid', namespace: nil) }
+      let(:valid_element) { double(Nokogiri::XML::Node, name: 'use') }
+
+      it 'removes an invalid element' do
+        expect(invalid_element).to receive(:unlink)
+
+        scrubber.scrub(invalid_element)
+      end
+
+      it 'removes an invalid attribute' do
+        allow(valid_element).to receive(:attribute_nodes) { [invalid_attribute] }
+        expect(invalid_attribute).to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+
+      it 'accepts valid element' do
+        allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
+        expect(valid_element).not_to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+
+      it 'accepts valid namespaced attributes' do
+        allow(valid_element).to receive(:attribute_nodes) { [namespaced_attr] }
+        expect(namespaced_attr).not_to receive(:unlink)
+
+        scrubber.scrub(valid_element)
+      end
+    end
+
+    describe '#attribute_name_with_namespace' do
+      it 'returns name with prefix when attribute is namespaced' do
+        expect(scrubber.attribute_name_with_namespace(namespaced_attr)).to eq('xlink:href')
+      end
+    end
+
+    describe '#unsafe_href?' do
+      let(:unsafe_attr) { double(Nokogiri::XML::Attr, name: 'href', namespace: namespace, value: 'http://evilsite.example.com/random.svg') }
+
+      it 'returns true if href attribute is an external url' do
+        expect(scrubber.unsafe_href?(unsafe_attr)).to be_truthy
+      end
+
+      it 'returns false if href atttribute is an internal reference' do
+        expect(scrubber.unsafe_href?(namespaced_attr)).to be_falsey
+      end
+    end
+
+    describe '#data_attribute?' do
+      let(:data_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: nil, value: 'gitlab is awesome') }
+      let(:namespaced_attr) { double(Nokogiri::XML::Attr, name: 'data-gitlab', namespace: namespace, value: 'gitlab is awesome') }
+      let(:other_attr) { double(Nokogiri::XML::Attr, name: 'something', namespace: nil, value: 'content') }
+
+      it 'returns true if is a valid data attribute' do
+        expect(scrubber.data_attribute?(data_attr)).to be_truthy
+      end
+
+      it 'returns false if attribute is namespaced' do
+        expect(scrubber.data_attribute?(namespaced_attr)).to be_falsey
+      end
+
+      it 'returns false if not a data attribute' do
+        expect(scrubber.data_attribute?(other_attr)).to be_falsey
+      end
+    end
+  end
+end