mirror of
https://github.com/meineerde/redmine.git
synced 2026-01-31 19:47:14 +00:00
Relax allowed protocols in links by denying specific protocols for CommonMark text formatting (#32424).
Patch by Martin Cizek. git-svn-id: http://svn.redmine.org/redmine/trunk@21161 e93f8b46-1217-0410-a6f0-8f06a7374b81
This commit is contained in:
parent
46ecdcec4d
commit
e8c911577f
@ -22,6 +22,7 @@ require 'uri'
|
||||
module Redmine
|
||||
module Helpers
|
||||
module URL
|
||||
# safe for resources fetched without user interaction?
|
||||
def uri_with_safe_scheme?(uri, schemes = ['http', 'https', 'ftp', 'mailto', nil])
|
||||
# URLs relative to the current document or document root (without a protocol
|
||||
# separator, should be harmless
|
||||
@ -32,6 +33,18 @@ module Redmine
|
||||
rescue URI::Error
|
||||
false
|
||||
end
|
||||
|
||||
# safe to render links to given uri?
|
||||
def uri_with_link_safe_scheme?(uri)
|
||||
# regexp adapted from Sanitize (we need to catch even invalid protocol specs)
|
||||
return true unless uri =~ /\A\s*([^\/#]*?)(?:\:|�*58|�*3a)/i
|
||||
|
||||
# absolute scheme
|
||||
scheme = $1.downcase
|
||||
return false unless /\A[a-z][a-z0-9\+\.\-]*\z/.match?(scheme) # RFC 3986
|
||||
|
||||
%w(data javascript vbscript).none?(scheme)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -22,6 +22,11 @@ module Redmine
|
||||
module CommonMark
|
||||
# sanitizes rendered HTML using the Sanitize gem
|
||||
class SanitizationFilter < HTML::Pipeline::SanitizationFilter
|
||||
include Redmine::Helpers::URL
|
||||
RELAXED_PROTOCOL_ATTRS = {
|
||||
"a" => %w(href).freeze,
|
||||
}.freeze
|
||||
|
||||
def whitelist
|
||||
@@whitelist ||= customize_whitelist(super.deep_dup)
|
||||
end
|
||||
@ -72,11 +77,24 @@ module Redmine
|
||||
node.remove_attribute("id")
|
||||
}
|
||||
|
||||
# allow the same set of URL schemes for links as is the default in
|
||||
# Redmine::Helpers::URL#uri_with_safe_scheme?
|
||||
whitelist[:protocols]["a"]["href"] = [
|
||||
'http', 'https', 'ftp', 'mailto', :relative
|
||||
]
|
||||
# https://github.com/rgrove/sanitize/issues/209
|
||||
whitelist[:protocols].delete("a")
|
||||
whitelist[:transformers].push lambda{|env|
|
||||
node = env[:node]
|
||||
return if node.type != Nokogiri::XML::Node::ELEMENT_NODE
|
||||
|
||||
name = env[:node_name]
|
||||
return unless RELAXED_PROTOCOL_ATTRS.include?(name)
|
||||
|
||||
RELAXED_PROTOCOL_ATTRS[name].each do |attr|
|
||||
next unless node.has_attribute?(attr)
|
||||
|
||||
node[attr] = node[attr].strip
|
||||
unless !node[attr].empty? && uri_with_link_safe_scheme?(node[attr])
|
||||
node.remove_attribute(attr)
|
||||
end
|
||||
end
|
||||
}
|
||||
|
||||
whitelist
|
||||
end
|
||||
|
||||
@ -33,4 +33,45 @@ class URLTest < ActiveSupport::TestCase
|
||||
assert_not uri_with_safe_scheme?("httpx://example.com/")
|
||||
assert_not uri_with_safe_scheme?("mailto:root@")
|
||||
end
|
||||
|
||||
LINK_SAFE_URIS = [
|
||||
"http://example.com/",
|
||||
"https://example.com/",
|
||||
"ftp://example.com/",
|
||||
"foo://example.org",
|
||||
"mailto:foo@example.org",
|
||||
" http://example.com/",
|
||||
"",
|
||||
"/javascript:alert(\'filename\')",
|
||||
]
|
||||
|
||||
def test_uri_with_link_safe_scheme_should_recognize_safe_uris
|
||||
LINK_SAFE_URIS.each do |uri|
|
||||
assert uri_with_link_safe_scheme?(uri), "'#{uri}' should be safe"
|
||||
end
|
||||
end
|
||||
|
||||
LINK_UNSAFE_URIS = [
|
||||
"javascript:alert(\'XSS\');",
|
||||
"javascript :alert(\'XSS\');",
|
||||
"javascript: alert(\'XSS\');",
|
||||
"javascript : alert(\'XSS\');",
|
||||
":javascript:alert(\'XSS\');",
|
||||
"javascript:",
|
||||
"javascript:",
|
||||
"javascript:",
|
||||
"javascript:",
|
||||
"java\0script:alert(\"XSS\")",
|
||||
"java\script:alert(\"XSS\")",
|
||||
" \x0e javascript:alert(\'XSS\');",
|
||||
"data:image/png;base64,foobar",
|
||||
"vbscript:foobar",
|
||||
"data:text/html;base64,foobar",
|
||||
]
|
||||
|
||||
def test_uri_with_link_safe_scheme_should_recognize_unsafe_uris
|
||||
LINK_UNSAFE_URIS.each do |uri|
|
||||
assert_not uri_with_link_safe_scheme?(uri), "'#{uri}' should not be safe"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -71,6 +71,25 @@ if Object.const_defined?(:CommonMarker)
|
||||
assert_equal %(<code>foo</code>), filter(input)
|
||||
end
|
||||
|
||||
def test_should_allow_links_with_safe_url_schemes
|
||||
%w(http https ftp ssh foo).each do |scheme|
|
||||
input = %(<a href="#{scheme}://example.org/">foo</a>)
|
||||
assert_equal input, filter(input)
|
||||
end
|
||||
end
|
||||
|
||||
def test_should_allow_mailto_links
|
||||
input = %(<a href="mailto:foo@example.org">bar</a>)
|
||||
assert_equal input, filter(input)
|
||||
end
|
||||
|
||||
def test_should_remove_empty_link
|
||||
input = %(<a href="">bar</a>)
|
||||
assert_equal %(<a>bar</a>), filter(input)
|
||||
input = %(<a href=" ">bar</a>)
|
||||
assert_equal %(<a>bar</a>), filter(input)
|
||||
end
|
||||
|
||||
# samples taken from the Sanitize test suite
|
||||
# rubocop:disable Layout/LineLength
|
||||
STRINGS = [
|
||||
@ -194,11 +213,6 @@ if Object.const_defined?(:CommonMarker)
|
||||
'<a href="vbscript:foobar">XSS</a>',
|
||||
'<a>XSS</a>'
|
||||
],
|
||||
|
||||
'invalid URIs' => [
|
||||
'<a href="foo://example.org">link</a>',
|
||||
'<a>link</a>'
|
||||
],
|
||||
}
|
||||
|
||||
PROTOCOLS.each do |name, strings|
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user