Merge branch 'bug/svg_sanitizer' into 'master'
Improve SVG sanitizer to handle namespaced attributes * Small refactor in the SVG sanitizer * Enable already whitelisted namespaced attributes to be allowed * Disable `xlink:href`to reference any external resource Fixes #18100 See merge request !4427
This commit is contained in:
commit
06a99cf7ea
3 changed files with 129 additions and 12 deletions
|
@ -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
|
||||
|
|
|
@ -12,22 +12,44 @@ 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]
|
||||
return
|
||||
end
|
||||
|
||||
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
|
||||
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
|
||||
|
|
94
spec/lib/gitlab/sanitizers/svg_spec.rb
Normal file
94
spec/lib/gitlab/sanitizers/svg_spec.rb
Normal file
|
@ -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
|
Loading…
Reference in a new issue