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.
This commit is contained in:
Sean McGivern 2018-01-15 14:49:27 +00:00
parent 1df5c74fc9
commit 82f4564fb7
5 changed files with 39 additions and 23 deletions

View File

@ -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

View File

@ -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

View File

@ -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(/^(?<ref>[^:]*):(?<filename>.*):(?<startline>\d+):/)
if matches
result.strip.each_line.each_with_index do |line, index|
prefix ||= line.match(/^(?<ref>[^:]*):(?<filename>.*)\x00(?<startline>\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(

View File

@ -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

View File

@ -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