summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lib/redmine/helpers/url.rb15
-rw-r--r--test/unit/lib/redmine/helpers/url_test.rb41
2 files changed, 56 insertions, 0 deletions
diff --git a/lib/redmine/helpers/url.rb b/lib/redmine/helpers/url.rb
index 54fd3cb61..f56920b48 100644
--- a/lib/redmine/helpers/url.rb
+++ b/lib/redmine/helpers/url.rb
@@ -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,20 @@ 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*([^\/#]*?)(?:\:|&#0*58|&#x0*3a)/i
+
+ # absolute scheme
+ scheme = $1.downcase
+ return false unless /\A[a-z][a-z0-9\+\.\-]*\z/.match?(scheme) # RFC 3986
+
+ # To support Ruby 2.4, we use `none? {|obj| ... }` instead of
+ # `none?(pattern)` in 4.2-stable branch.
+ %w(data javascript vbscript).none? {|v| v == scheme}
+ end
end
end
end
diff --git a/test/unit/lib/redmine/helpers/url_test.rb b/test/unit/lib/redmine/helpers/url_test.rb
index fe143393b..93a797a10 100644
--- a/test/unit/lib/redmine/helpers/url_test.rb
+++ b/test/unit/lib/redmine/helpers/url_test.rb
@@ -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&#58;",
+ "javascript&#0058;",
+ "javascript&#x3A;",
+ "javascript&#x003A;",
+ "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