From 82f4564fb7dc57a9a7bb6a052926ee219bb29b13 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 15 Jan 2018 14:49:27 +0000 Subject: [PATCH 1/2] Fix project search results for digits surrounded by colons A file containing /:\d+:/ in its contents would break the search results if those contents were part of the results, because we were splitting on colons, which can't work with untrusted input. Changing to use the null byte as a separator is much safer. --- app/models/repository.rb | 2 +- ...41666-cannot-search-with-keyword-merge.yml | 6 +++ lib/gitlab/project_search_results.rb | 13 ++----- .../lib/gitlab/project_search_results_spec.rb | 39 +++++++++++++------ spec/models/repository_spec.rb | 2 +- 5 files changed, 39 insertions(+), 23 deletions(-) create mode 100644 changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml diff --git a/app/models/repository.rb b/app/models/repository.rb index 2ffd9558ebc..66869d0539b 100644 --- a/app/models/repository.rb +++ b/app/models/repository.rb @@ -932,7 +932,7 @@ class Repository return [] if empty? || query.blank? offset = 2 - args = %W(grep -i -I -n --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) + args = %W(grep -i -I -n -z --before-context #{offset} --after-context #{offset} -E -e #{Regexp.escape(query)} #{ref || root_ref}) run_git(args).first.scrub.split(/^--$/) end diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml new file mode 100644 index 00000000000..3a6fa425c9c --- /dev/null +++ b/changelogs/unreleased/41666-cannot-search-with-keyword-merge.yml @@ -0,0 +1,6 @@ +--- +title: Fix file search results when they match file contents with a number between + two colons +merge_request: 16462 +author: +type: fixed diff --git a/lib/gitlab/project_search_results.rb b/lib/gitlab/project_search_results.rb index e2662fc362b..7771b15069b 100644 --- a/lib/gitlab/project_search_results.rb +++ b/lib/gitlab/project_search_results.rb @@ -44,25 +44,20 @@ module Gitlab ref = nil filename = nil basename = nil + data = "" startline = 0 - result.each_line.each_with_index do |line, index| - matches = line.match(/^(?[^:]*):(?.*):(?\d+):/) - if matches + result.strip.each_line.each_with_index do |line, index| + prefix ||= line.match(/^(?[^:]*):(?.*)\x00(?\d+)\x00/)&.tap do |matches| ref = matches[:ref] filename = matches[:filename] startline = matches[:startline] startline = startline.to_i - index extname = Regexp.escape(File.extname(filename)) basename = filename.sub(/#{extname}$/, '') - break end - end - data = "" - - result.each_line do |line| - data << line.sub(ref, '').sub(filename, '').sub(/^:-\d+-/, '').sub(/^::\d+:/, '') + data << line.sub(prefix.to_s, '') end FoundBlob.new( diff --git a/spec/lib/gitlab/project_search_results_spec.rb b/spec/lib/gitlab/project_search_results_spec.rb index 17937726f2c..1ebb0105cf5 100644 --- a/spec/lib/gitlab/project_search_results_spec.rb +++ b/spec/lib/gitlab/project_search_results_spec.rb @@ -70,15 +70,6 @@ describe Gitlab::ProjectSearchResults do subject { described_class.parse_search_result(search_result) } - it 'can correctly parse filenames including ":"' do - special_char_result = "\nmaster:testdata/project::function1.yaml-1----\nmaster:testdata/project::function1.yaml:2:test: data1\n" - - blob = described_class.parse_search_result(special_char_result) - - expect(blob.ref).to eq('master') - expect(blob.filename).to eq('testdata/project::function1.yaml') - end - it "returns a valid FoundBlob" do is_expected.to be_an Gitlab::SearchResults::FoundBlob expect(subject.id).to be_nil @@ -90,8 +81,32 @@ describe Gitlab::ProjectSearchResults do expect(subject.data.lines[2]).to eq(" - Feature: Replace teams with group membership\n") end + context 'when the matching filename contains a colon' do + let(:search_result) { "\nmaster:testdata/project::function1.yaml\x001\x00---\n" } + + it 'returns a valid FoundBlob' do + expect(subject.filename).to eq('testdata/project::function1.yaml') + expect(subject.basename).to eq('testdata/project::function1') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(1) + expect(subject.data).to eq('---') + end + end + + context 'when the matching content contains a number surrounded by colons' do + let(:search_result) { "\nmaster:testdata/foo.txt\x001\x00blah:9:blah" } + + it 'returns a valid FoundBlob' do + expect(subject.filename).to eq('testdata/foo.txt') + expect(subject.basename).to eq('testdata/foo') + expect(subject.ref).to eq('master') + expect(subject.startline).to eq(1) + expect(subject.data).to eq('blah:9:blah') + end + end + context "when filename has extension" do - let(:search_result) { "master:CONTRIBUTE.md:5:- [Contribute to GitLab](#contribute-to-gitlab)\n" } + let(:search_result) { "master:CONTRIBUTE.md\x005\x00- [Contribute to GitLab](#contribute-to-gitlab)\n" } it { expect(subject.path).to eq('CONTRIBUTE.md') } it { expect(subject.filename).to eq('CONTRIBUTE.md') } @@ -99,7 +114,7 @@ describe Gitlab::ProjectSearchResults do end context "when file under directory" do - let(:search_result) { "master:a/b/c.md:5:a b c\n" } + let(:search_result) { "master:a/b/c.md\x005\x00a b c\n" } it { expect(subject.path).to eq('a/b/c.md') } it { expect(subject.filename).to eq('a/b/c.md') } @@ -144,7 +159,7 @@ describe Gitlab::ProjectSearchResults do end it 'finds by content' do - expect(results).to include("master:Title.md:1:Content\n") + expect(results).to include("master:Title.md\x001\x00Content\n") end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index f3456e5b354..7e215e37de3 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -657,7 +657,7 @@ describe Repository do subject { results.first } it { is_expected.to be_an String } - it { expect(subject.lines[2]).to eq("master:CHANGELOG:190: - Feature: Replace teams with group membership\n") } + it { expect(subject.lines[2]).to eq("master:CHANGELOG\x00190\x00 - Feature: Replace teams with group membership\n") } end end From 91939161aace35823f7a60b25647d5e23285c556 Mon Sep 17 00:00:00 2001 From: Sean McGivern Date: Mon, 15 Jan 2018 15:00:58 +0000 Subject: [PATCH 2/2] Only highlight search results under the highlighting size limit We should use this limit everywhere, but especially in project search results, where we could be highlighting very long single lines. (Typical examples: minified JavaScript, and JSON data files.) --- app/helpers/blob_helper.rb | 2 ++ .../41666-cannot-search-with-keyword-merge-2.yml | 5 +++++ spec/helpers/blob_helper_spec.rb | 7 +++++++ 3 files changed, 14 insertions(+) create mode 100644 changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml diff --git a/app/helpers/blob_helper.rb b/app/helpers/blob_helper.rb index 5e3b2e5581c..a6e1de6ffdc 100644 --- a/app/helpers/blob_helper.rb +++ b/app/helpers/blob_helper.rb @@ -1,6 +1,8 @@ module BlobHelper def highlight(blob_name, blob_content, repository: nil, plain: false) + plain ||= blob_content.length > Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE highlighted = Gitlab::Highlight.highlight(blob_name, blob_content, plain: plain, repository: repository) + raw %(
#{highlighted}
) end diff --git a/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml b/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml new file mode 100644 index 00000000000..48893862071 --- /dev/null +++ b/changelogs/unreleased/41666-cannot-search-with-keyword-merge-2.yml @@ -0,0 +1,5 @@ +--- +title: Only highlight search results under the highlighting size limit +merge_request: 16462 +author: +type: performance diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 04620f6d88c..a030796c54e 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -22,6 +22,13 @@ describe BlobHelper do expect(result).to eq(%[
:type "assem"))
]) end + it 'returns plaintext for long blobs' do + stub_const('Blob::MAXIMUM_TEXT_HIGHLIGHT_SIZE', 1) + result = helper.highlight(blob_name, blob_content) + + expect(result).to eq(%[
(make-pathname :defaults name\n:type "assem"))
]) + end + it 'highlights single block' do expected = %Q[
(make-pathname :defaults name
 :type "assem"))
]