##// END OF EJS Templates
Merged r15431 to r15435 (#22924, #22925, #22926)....
Jean-Philippe Lang -
r15059:ee408687c61d
parent child
Show More
@@ -0,0 +1,35
1 # Redmine - project management software
2 # Copyright (C) 2006-2016 Jean-Philippe Lang
3 #
4 # This program is free software; you can redistribute it and/or
5 # modify it under the terms of the GNU General Public License
6 # as published by the Free Software Foundation; either version 2
7 # of the License, or (at your option) any later version.
8 #
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
13 #
14 # You should have received a copy of the GNU General Public License
15 # along with this program; if not, write to the Free Software
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17
18 require 'uri'
19
20 module Redmine
21 module Helpers
22 module URL
23 def uri_with_safe_scheme?(uri, schemes = ['http', 'https', 'ftp', 'mailto', nil])
24 # URLs relative to the current document or document root (without a protocol
25 # separator, should be harmless
26 return true unless uri.include? ":"
27
28 # Other URLs need to be parsed
29 schemes.include? URI.parse(uri).scheme
30 rescue URI::InvalidURIError
31 false
32 end
33 end
34 end
35 end
@@ -28,6 +28,7 module ApplicationHelper
28 28 include Redmine::SudoMode::Helper
29 29 include Redmine::Themes::Helper
30 30 include Redmine::Hook::Helper
31 include Redmine::Helpers::URL
31 32
32 33 extend Forwardable
33 34 def_delegators :wiki_helper, :wikitoolbar_for, :heads_for_wiki_formatter
@@ -262,6 +262,14 class CustomField < ActiveRecord::Base
262 262 args.include?(field_format)
263 263 end
264 264
265 def self.human_attribute_name(attribute_key_name, *args)
266 attr_name = attribute_key_name.to_s
267 if attr_name == 'url_pattern'
268 attr_name = "url"
269 end
270 super(attr_name, *args)
271 end
272
265 273 protected
266 274
267 275 # Removes multiple values for the custom field after setting the multiple attribute to false
@@ -26,7 +26,7
26 26 <% if @project.homepage.present? || @subprojects.any? || @project.visible_custom_field_values.any?(&:present?) %>
27 27 <ul>
28 28 <% unless @project.homepage.blank? %>
29 <li><span class="label"><%=l(:field_homepage)%>:</span> <%= link_to @project.homepage, @project.homepage %></li>
29 <li><span class="label"><%=l(:field_homepage)%>:</span> <%= link_to_if uri_with_safe_scheme?(@project.homepage), @project.homepage, @project.homepage %></li>
30 30 <% end %>
31 31 <% if @subprojects.any? %>
32 32 <li><span class="label"><%=l(:label_subproject_plural)%>:</span>
@@ -165,6 +165,7
165 165 # class RedCloth::Textile.new( str )
166 166
167 167 class RedCloth3 < String
168 include Redmine::Helpers::URL
168 169
169 170 VERSION = '3.0.4'
170 171 DEFAULT_RULES = [:textile, :markdown]
@@ -960,6 +961,8 class RedCloth3 < String
960 961 href, alt_title = check_refs( href ) if href
961 962 url, url_title = check_refs( url )
962 963
964 return m unless uri_with_safe_scheme?(url)
965
963 966 out = ''
964 967 out << "<a#{ shelve( " href=\"#{ href }\"" ) }>" if href
965 968 out << "<img#{ shelve( atts ) } />"
@@ -48,6 +48,7 module Redmine
48 48 class Base
49 49 include Singleton
50 50 include Redmine::I18n
51 include Redmine::Helpers::URL
51 52 include ERB::Util
52 53
53 54 class_attribute :format_name
@@ -149,7 +150,12 module Redmine
149 150 # Returns the validation errors for custom_field
150 151 # Should return an empty array if custom_field is valid
151 152 def validate_custom_field(custom_field)
152 []
153 errors = []
154 pattern = custom_field.url_pattern
155 if pattern.present? && !uri_with_safe_scheme?(url_pattern_without_tokens(pattern))
156 errors << [:url_pattern, :invalid]
157 end
158 errors
153 159 end
154 160
155 161 # Returns the validation error messages for custom_value
@@ -178,7 +184,7 module Redmine
178 184 url = url_from_pattern(custom_field, single_value, customized)
179 185 [text, url]
180 186 end
181 links = texts_and_urls.sort_by(&:first).map {|text, url| view.link_to text, url}
187 links = texts_and_urls.sort_by(&:first).map {|text, url| view.link_to_if uri_with_safe_scheme?(url), text, url}
182 188 links.join(', ').html_safe
183 189 else
184 190 casted
@@ -210,6 +216,13 module Redmine
210 216 end
211 217 protected :url_from_pattern
212 218
219 # Returns the URL pattern with substitution tokens removed,
220 # for validation purpose
221 def url_pattern_without_tokens(url_pattern)
222 url_pattern.to_s.gsub(/%(value|id|project_id|project_identifier|m\d+)%/, '')
223 end
224 protected :url_pattern_without_tokens
225
213 226 def edit_tag(view, tag_id, tag_name, custom_value, options={})
214 227 view.text_field_tag(tag_name, custom_value.value, options.merge(:id => tag_id))
215 228 end
@@ -22,8 +22,11 module Redmine
22 22 module Markdown
23 23 class HTML < Redcarpet::Render::HTML
24 24 include ActionView::Helpers::TagHelper
25 include Redmine::Helpers::URL
25 26
26 27 def link(link, title, content)
28 return nil unless uri_with_safe_scheme?(link)
29
27 30 css = nil
28 31 unless link && link.starts_with?('/')
29 32 css = 'external'
@@ -40,6 +43,12 module Redmine
40 43 "<pre>" + CGI.escapeHTML(code) + "</pre>"
41 44 end
42 45 end
46
47 def image(link, title, alt_text)
48 return unless uri_with_safe_scheme?(link)
49
50 tag('img', :src => link, :alt => alt_text || "", :title => title)
51 end
43 52 end
44 53
45 54 class Formatter
@@ -164,7 +164,7 RAW
164 164
165 165 attachment = Attachment.generate!(:filename => 'café.jpg')
166 166 with_settings :text_formatting => 'markdown' do
167 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="">),
167 assert_include %(<img src="/attachments/download/#{attachment.id}/caf%C3%A9.jpg" alt="" />),
168 168 textilizable("![](café.jpg)", :attachments => [attachment])
169 169 end
170 170 end
@@ -20,6 +20,10 require File.expand_path('../../../../../test_helper', __FILE__)
20 20 class Redmine::FieldFormatTest < ActionView::TestCase
21 21 include ApplicationHelper
22 22
23 def setup
24 set_language_if_valid 'en'
25 end
26
23 27 def test_string_field_with_text_formatting_disabled_should_not_format_text
24 28 field = IssueCustomField.new(:field_format => 'string')
25 29 custom_value = CustomValue.new(:custom_field => field, :customized => Issue.new, :value => "*foo*")
@@ -52,6 +56,17 class Redmine::FieldFormatTest < ActionView::TestCase
52 56 assert_include "<strong>foo</strong>", field.format.formatted_custom_value(self, custom_value, true)
53 57 end
54 58
59 def test_should_validate_url_pattern_with_safe_scheme
60 field = IssueCustomField.new(:field_format => 'string', :name => 'URL', :url_pattern => 'http://foo/%value%')
61 assert_save field
62 end
63
64 def test_should_not_validate_url_pattern_with_unsafe_scheme
65 field = IssueCustomField.new(:field_format => 'string', :name => 'URL', :url_pattern => 'foo://foo/%value%')
66 assert !field.save
67 assert_include "URL is invalid", field.errors.full_messages
68 end
69
55 70 def test_text_field_with_url_pattern_should_format_as_link
56 71 field = IssueCustomField.new(:field_format => 'string', :url_pattern => 'http://foo/%value%')
57 72 custom_value = CustomValue.new(:custom_field => field, :customized => Issue.new, :value => "bar")
General Comments 0
You need to be logged in to leave comments. Login now