Skip to content

Commit 532a0b6

Browse files
bluegodstanhu
authored andcommitted
Merge branch 'fix/import-rce-10-3' into 'security-10-3'
[10.3] Fix RCE via project import mechanism See merge request gitlab/gitlabhq!2294 (cherry picked from commit dcfec507d6f9ee119d65a832393e7c593af1d3b2) 86d75812 Fix RCE via project import mechanism
1 parent 791ca43 commit 532a0b6

File tree

5 files changed

+68
-16
lines changed

5 files changed

+68
-16
lines changed
+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
title: Fix RCE via project import mechanism
3+
merge_request:
4+
author:
5+
type: security

lib/gitlab/import_export/file_importer.rb

+5-1
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,16 @@ def initialize(archive_file:, shared:)
1717
def import
1818
mkdir_p(@shared.export_path)
1919

20+
remove_symlinks!
21+
2022
wait_for_archived_file do
2123
decompress_archive
2224
end
2325
rescue => e
2426
@shared.error(e)
2527
false
28+
ensure
29+
remove_symlinks!
2630
end
2731

2832
private
@@ -43,7 +47,7 @@ def decompress_archive
4347

4448
raise Projects::ImportService::Error.new("Unable to decompress #{@archive_file} into #{@shared.export_path}") unless result
4549

46-
remove_symlinks!
50+
result
4751
end
4852

4953
def remove_symlinks!

lib/gitlab/import_export/saver.rb

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def remove_export_path
3737
end
3838

3939
def archive_file
40-
@archive_file ||= File.join(@shared.export_path, '..', Gitlab::ImportExport.export_filename(project: @project))
40+
@archive_file ||= File.join(@shared.archive_path, Gitlab::ImportExport.export_filename(project: @project))
4141
end
4242
end
4343
end

lib/gitlab/import_export/shared.rb

+13-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ def initialize(opts)
99
end
1010

1111
def export_path
12-
@export_path ||= Gitlab::ImportExport.export_path(relative_path: opts[:relative_path])
12+
@export_path ||= Gitlab::ImportExport.export_path(relative_path: relative_path)
13+
end
14+
15+
def archive_path
16+
@archive_path ||= Gitlab::ImportExport.export_path(relative_path: relative_archive_path)
1317
end
1418

1519
def error(error)
@@ -21,6 +25,14 @@ def error(error)
2125

2226
private
2327

28+
def relative_path
29+
File.join(opts[:relative_path], SecureRandom.hex)
30+
end
31+
32+
def relative_archive_path
33+
File.join(opts[:relative_path], '..')
34+
end
35+
2436
def error_out(message, caller)
2537
Rails.logger.error("Import/Export error raised on #{caller}: #{message}")
2638
end

spec/lib/gitlab/import_export/file_importer_spec.rb

+44-13
Original file line numberDiff line numberDiff line change
@@ -12,30 +12,61 @@
1212
stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
1313
allow_any_instance_of(Gitlab::ImportExport).to receive(:storage_path).and_return(export_path)
1414
allow_any_instance_of(Gitlab::ImportExport::CommandLineUtil).to receive(:untar_zxf).and_return(true)
15-
15+
allow(SecureRandom).to receive(:hex).and_return('abcd')
1616
setup_files
17-
18-
described_class.import(archive_file: '', shared: shared)
1917
end
2018

2119
after do
2220
FileUtils.rm_rf(export_path)
2321
end
2422

25-
it 'removes symlinks in root folder' do
26-
expect(File.exist?(symlink_file)).to be false
27-
end
23+
context 'normal run' do
24+
before do
25+
described_class.import(archive_file: '', shared: shared)
26+
end
2827

29-
it 'removes hidden symlinks in root folder' do
30-
expect(File.exist?(hidden_symlink_file)).to be false
31-
end
28+
it 'removes symlinks in root folder' do
29+
expect(File.exist?(symlink_file)).to be false
30+
end
31+
32+
it 'removes hidden symlinks in root folder' do
33+
expect(File.exist?(hidden_symlink_file)).to be false
34+
end
35+
36+
it 'removes symlinks in subfolders' do
37+
expect(File.exist?(subfolder_symlink_file)).to be false
38+
end
3239

33-
it 'removes symlinks in subfolders' do
34-
expect(File.exist?(subfolder_symlink_file)).to be false
40+
it 'does not remove a valid file' do
41+
expect(File.exist?(valid_file)).to be true
42+
end
43+
44+
it 'creates the file in the right subfolder' do
45+
expect(shared.export_path).to include('test/abcd')
46+
end
3547
end
3648

37-
it 'does not remove a valid file' do
38-
expect(File.exist?(valid_file)).to be true
49+
context 'error' do
50+
before do
51+
allow_any_instance_of(described_class).to receive(:wait_for_archived_file).and_raise(StandardError)
52+
described_class.import(archive_file: '', shared: shared)
53+
end
54+
55+
it 'removes symlinks in root folder' do
56+
expect(File.exist?(symlink_file)).to be false
57+
end
58+
59+
it 'removes hidden symlinks in root folder' do
60+
expect(File.exist?(hidden_symlink_file)).to be false
61+
end
62+
63+
it 'removes symlinks in subfolders' do
64+
expect(File.exist?(subfolder_symlink_file)).to be false
65+
end
66+
67+
it 'does not remove a valid file' do
68+
expect(File.exist?(valid_file)).to be true
69+
end
3970
end
4071

4172
def setup_files

0 commit comments

Comments
 (0)