Skip to content

Commit 291ca09

Browse files
committed
Address command line injection in Git::Lib#fetch
Signed-off-by: James Couball <jcouball@yahoo.com>
1 parent 521b8e7 commit 291ca09

File tree

2 files changed

+54
-34
lines changed

2 files changed

+54
-34
lines changed

lib/git/lib.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -875,14 +875,15 @@ def tag(name, *opts)
875875
command('tag', arr_opts)
876876
end
877877

878-
879878
def fetch(remote, opts)
880-
arr_opts = [remote]
881-
arr_opts << opts[:ref] if opts[:ref]
879+
arr_opts = []
882880
arr_opts << '--tags' if opts[:t] || opts[:tags]
883881
arr_opts << '--prune' if opts[:p] || opts[:prune]
884882
arr_opts << '--unshallow' if opts[:unshallow]
885883
arr_opts << '--depth' << opts[:depth] if opts[:depth]
884+
arr_opts << '--'
885+
arr_opts << remote
886+
arr_opts << opts[:ref] if opts[:ref]
886887

887888
command('fetch', arr_opts)
888889
end

tests/units/test_remotes.rb

Lines changed: 50 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -23,29 +23,29 @@ def test_add_remote
2323
assert(local.remotes.map{|b| b.name}.include?('testremote2'))
2424

2525
local.add_remote('testremote3', remote, :track => 'master')
26-
27-
assert(local.branches.map{|b| b.full}.include?('master')) #We actually a new branch ('test_track') on the remote and track that one intead.
26+
27+
assert(local.branches.map{|b| b.full}.include?('master')) #We actually a new branch ('test_track') on the remote and track that one intead.
2828
assert(local.remotes.map{|b| b.name}.include?('testremote3'))
29-
end
29+
end
3030
end
3131

3232
def test_remove_remote_remove
3333
in_temp_dir do |path|
3434
local = Git.clone(@wbare, 'local')
3535
remote = Git.clone(@wbare, 'remote')
36-
36+
3737
local.add_remote('testremote', remote)
3838
local.remove_remote('testremote')
39-
39+
4040
assert(!local.remotes.map{|b| b.name}.include?('testremote'))
4141

4242
local.add_remote('testremote', remote)
4343
local.remote('testremote').remove
44-
44+
4545
assert(!local.remotes.map{|b| b.name}.include?('testremote'))
4646
end
4747
end
48-
48+
4949
def test_set_remote_url
5050
in_temp_dir do |path|
5151
local = Git.clone(@wbare, 'local')
@@ -65,33 +65,33 @@ def test_remote_fun
6565
in_temp_dir do |path|
6666
loc = Git.clone(@wbare, 'local')
6767
rem = Git.clone(@wbare, 'remote')
68-
68+
6969
r = loc.add_remote('testrem', rem)
7070

7171
Dir.chdir('remote') do
7272
new_file('test-file1', 'blahblahblah1')
7373
rem.add
7474
rem.commit('master commit')
75-
75+
7676
rem.branch('testbranch').in_branch('tb commit') do
7777
new_file('test-file3', 'blahblahblah3')
7878
rem.add
79-
true
79+
true
8080
end
8181
end
8282
assert(!loc.status['test-file1'])
8383
assert(!loc.status['test-file3'])
84-
84+
8585
r.fetch
86-
r.merge
86+
r.merge
8787
assert(loc.status['test-file1'])
88-
88+
8989
loc.merge(loc.remote('testrem').branch('testbranch'))
90-
assert(loc.status['test-file3'])
91-
90+
assert(loc.status['test-file3'])
91+
9292
#puts loc.remotes.map { |r| r.to_s }.inspect
93-
94-
#r.remove
93+
94+
#r.remove
9595
#puts loc.remotes.inspect
9696
end
9797
end
@@ -123,18 +123,37 @@ def test_fetch
123123
end
124124
end
125125

126+
def test_fetch_command_injection
127+
test_file = 'VULNERABILITY_EXISTS'
128+
vulnerability_exists = false
129+
in_temp_dir do |_path|
130+
git = Git.init('test_project')
131+
origin = "--upload-pack=touch #{test_file};"
132+
begin
133+
git.fetch(origin, { ref: 'some/ref/head' })
134+
rescue Git::GitExecuteError
135+
# This is expected
136+
else
137+
raise 'Expected Git::GitExecuteError to be raised'
138+
end
139+
140+
vulnerability_exists = File.exist?(test_file)
141+
end
142+
assert(!vulnerability_exists)
143+
end
144+
126145
def test_fetch_ref_adds_ref_option
127146
in_temp_dir do |path|
128147
loc = Git.clone(@wbare, 'local')
129148
rem = Git.clone(@wbare, 'remote', :config => 'receive.denyCurrentBranch=ignore')
130149
loc.add_remote('testrem', rem)
131-
150+
132151
loc.chdir do
133152
new_file('test-file1', 'gonnaCommitYou')
134153
loc.add
135154
loc.commit('master commit 1')
136155
first_commit_sha = loc.log.first.sha
137-
156+
138157
new_file('test-file2', 'gonnaCommitYouToo')
139158
loc.add
140159
loc.commit('master commit 2')
@@ -146,46 +165,46 @@ def test_fetch_ref_adds_ref_option
146165

147166
# Make sure fetch message only has the second commit when we fetch the second commit
148167
assert(loc.fetch('origin', {:ref => second_commit_sha}).include?(second_commit_sha))
149-
assert(!loc.fetch('origin', {:ref => second_commit_sha}).include?(first_commit_sha))
150-
end
168+
assert(!loc.fetch('origin', {:ref => second_commit_sha}).include?(first_commit_sha))
169+
end
151170
end
152171
end
153-
172+
154173
def test_push
155174
in_temp_dir do |path|
156175
loc = Git.clone(@wbare, 'local')
157176
rem = Git.clone(@wbare, 'remote', :config => 'receive.denyCurrentBranch=ignore')
158-
177+
159178
loc.add_remote('testrem', rem)
160179

161180
loc.chdir do
162181
new_file('test-file1', 'blahblahblah1')
163182
loc.add
164183
loc.commit('master commit')
165184
loc.add_tag('test-tag')
166-
185+
167186
loc.branch('testbranch').in_branch('tb commit') do
168187
new_file('test-file3', 'blahblahblah3')
169188
loc.add
170-
true
189+
true
171190
end
172191
end
173192
assert(!rem.status['test-file1'])
174193
assert(!rem.status['test-file3'])
175-
194+
176195
loc.push('testrem')
177196

178-
assert(rem.status['test-file1'])
179-
assert(!rem.status['test-file3'])
197+
assert(rem.status['test-file1'])
198+
assert(!rem.status['test-file3'])
180199
assert_raise Git::GitTagNameDoesNotExist do
181200
rem.tag('test-tag')
182201
end
183-
202+
184203
loc.push('testrem', 'testbranch', true)
185204

186205
rem.checkout('testbranch')
187-
assert(rem.status['test-file1'])
188-
assert(rem.status['test-file3'])
206+
assert(rem.status['test-file1'])
207+
assert(rem.status['test-file3'])
189208
assert(rem.tag('test-tag'))
190209
end
191210
end

0 commit comments

Comments
 (0)