From 3c29f9003cdbcf3700df97c5d6b6ca42e9152dcd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Oliver=20G=C3=BCnther?= Date: Thu, 17 Jan 2019 08:30:45 +0100 Subject: [PATCH] Correctly escape highlighted diff sections --- lib/redmine/unified_diff.rb | 86 ++++++++++++--------------- spec/lib/redmine/unified_diff_spec.rb | 29 +++++++++ 2 files changed, 66 insertions(+), 49 deletions(-) diff --git a/lib/redmine/unified_diff.rb b/lib/redmine/unified_diff.rb index 08079d2bc0d..206db2c118a 100644 --- a/lib/redmine/unified_diff.rb +++ b/lib/redmine/unified_diff.rb @@ -191,42 +191,26 @@ module Redmine def offsets(line_left, line_right) if line_left.present? && line_right.present? && line_left != line_right - line_left = escapeHTML(line_left) - line_right = escapeHTML(line_right) max = [line_left.size, line_right.size].min - starting = starting(line_left, line_right, max) - ending = ending(line_left, line_right, max, starting) + starting = 0 + while starting < max && line_left[starting] == line_right[starting] + starting += 1 + end + ending = -1 + while ending >= -(max - starting) && (line_left[ending] == line_right[ending]) + ending -= 1 + end unless starting == 0 && ending == -1 [starting, ending] end end end - - def starting(line_left, line_right, max) - starting = 0 - while starting < max && line_left[starting] == line_right[starting] - starting += 1 - end - if starting.positive? && line_left[starting - 1] == '&' - starting -= 1 - end - starting - end - - def ending(line_left, line_right, max, starting) - ending = -1 - while ending >= -(max - starting) && line_left[ending] == line_right[ending] - ending -= 1 - end - if ending < -1 && line_left[ending + 1] == ';' && line_left[starting] == '&' - ending += 1 - end - ending - end end # A line of diff class Diff + include ActionView::Helpers::TagHelper + attr_accessor :nb_line_left attr_accessor :line_left attr_accessor :nb_line_right @@ -253,35 +237,15 @@ module Redmine end def html_line_left - if offsets - l = escapeHTML(line_left) - l.insert(offsets.first, '').insert(offsets.last, '').html_safe - else - line_left - end + line_to_html(line_left, offsets) end def html_line_right - if offsets - l = escapeHTML(line_right) - l.insert(offsets.first, '').insert(offsets.last, '').html_safe - else - line_right - end - end - - # Escape the HTML for the diff - def escapeHTML(line) - CGI.escapeHTML(line) + line_to_html(line_right, offsets) end def html_line - if offsets - l = escapeHTML(line) - l.insert(offsets.first, '').insert(offsets.last, '').html_safe - else - line - end + line_to_html(line, offsets) end def inspect @@ -291,5 +255,29 @@ module Redmine puts nb_line_right puts line_right end + + private + + def line_to_html(line, offsets) + line_to_html_raw(line, offsets).tap do |html_str| + html_str.force_encoding('UTF-8') + end + end + + def line_to_html_raw(line, offsets) + return line unless offsets + + ActiveSupport::SafeBuffer.new.tap do |output| + if offsets.first != 0 + output << line[0..offsets.first-1] + end + + output << content_tag(:span, line[offsets.first..offsets.last]) + + unless offsets.last == -1 + output << line[offsets.last+1..-1] + end + end.to_s + end end end diff --git a/spec/lib/redmine/unified_diff_spec.rb b/spec/lib/redmine/unified_diff_spec.rb index 75dc1241867..846eeabb005 100644 --- a/spec/lib/redmine/unified_diff_spec.rb +++ b/spec/lib/redmine/unified_diff_spec.rb @@ -55,4 +55,33 @@ module Redmine expect(@diff.first.first.line_right).to eq('') end end + + describe 'unified diff html eescape' do + let(:diff) do + Redmine::UnifiedDiff.new(<<~DIFF + diff --git a/asdf b/asdf + index 7f6361d..3c52e50 100644 + --- a/asdf + +++ b/asdf + @@ -1,4 +1,4 @@ + Test 1 + -Test 2 <_> pouet + +Test 2 >_> pouet + Test 3 + Test 4 + DIFF + ) + end + + subject do + [].tap do |lines| + diff.first.each_line { |_,l| lines << [l.html_line_left, l.html_line_right] } + end + end + + it 'should correctly escape elements' do + expect(subject[1]).to eq(["Test 2 <_> pouet", ""]) + expect(subject[2]).to eq(["", "Test 2 >_> pouet"]) + end + end end