summaryrefslogtreecommitdiff
path: root/spec/lib/gitlab/import_export/file_importer_spec.rb
blob: 870fe8bc7fc05ab371a40d4cc5855ab24220ab39 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
# frozen_string_literal: true

require 'spec_helper'

describe Gitlab::ImportExport::FileImporter do
  include ExportFileHelper

  let(:shared) { Gitlab::ImportExport::Shared.new(nil) }
  let(:storage_path) { "#{Dir.tmpdir}/file_importer_spec" }
  let(:valid_file) { "#{shared.export_path}/valid.json" }
  let(:symlink_file) { "#{shared.export_path}/invalid.json" }
  let(:hidden_symlink_file) { "#{shared.export_path}/.hidden" }
  let(:subfolder_symlink_file) { "#{shared.export_path}/subfolder/invalid.json" }
  let(:evil_symlink_file) { "#{shared.export_path}/.\nevil" }
  let(:custom_mode_symlink_file) { "#{shared.export_path}/symlink.mode" }

  before do
    stub_const('Gitlab::ImportExport::FileImporter::MAX_RETRIES', 0)
    stub_uploads_object_storage(FileUploader)

    allow_next_instance_of(Gitlab::ImportExport) do |instance|
      allow(instance).to receive(:storage_path).and_return(storage_path)
    end
    allow_next_instance_of(Gitlab::ImportExport::CommandLineUtil) do |instance|
      allow(instance).to receive(:untar_zxf).and_return(true)
    end
    allow_next_instance_of(Gitlab::ImportExport::Shared) do |instance|
      allow(instance).to receive(:relative_archive_path).and_return('test')
    end
    allow(SecureRandom).to receive(:hex).and_return('abcd')
    setup_files
  end

  after do
    FileUtils.rm_rf(storage_path)
  end

  context 'normal run' do
    before do
      described_class.import(importable: build(:project), archive_file: '', shared: shared)
    end

    it 'removes symlinks in root folder' do
      expect(File.exist?(symlink_file)).to be false
    end

    it 'removes hidden symlinks in root folder' do
      expect(File.exist?(hidden_symlink_file)).to be false
    end

    it 'removes evil symlinks in root folder' do
      expect(File.exist?(evil_symlink_file)).to be false
    end

    it 'removes symlinks in subfolders' do
      expect(File.exist?(subfolder_symlink_file)).to be false
    end

    it 'removes symlinks without any file permissions' do
      expect(File.exist?(custom_mode_symlink_file)).to be false
    end

    it 'does not remove a valid file' do
      expect(File.exist?(valid_file)).to be true
    end

    it 'does not change a valid file permissions' do
      expect(file_permissions(valid_file)).not_to eq(0000)
    end

    it 'creates the file in the right subfolder' do
      expect(shared.export_path).to include('test/abcd')
    end
  end

  context 'error' do
    before do
      allow_next_instance_of(described_class) do |instance|
        allow(instance).to receive(:wait_for_archived_file).and_raise(StandardError)
      end
      described_class.import(importable: build(:project), archive_file: '', shared: shared)
    end

    it 'removes symlinks in root folder' do
      expect(File.exist?(symlink_file)).to be false
    end

    it 'removes hidden symlinks in root folder' do
      expect(File.exist?(hidden_symlink_file)).to be false
    end

    it 'removes symlinks in subfolders' do
      expect(File.exist?(subfolder_symlink_file)).to be false
    end

    it 'does not remove a valid file' do
      expect(File.exist?(valid_file)).to be true
    end
  end

  context 'when file exceeds acceptable decompressed size' do
    let(:project) { create(:project) }
    let(:shared) { Gitlab::ImportExport::Shared.new(project) }
    let(:filepath) { File.join(Dir.tmpdir, 'file_importer_spec.gz') }

    subject { described_class.new(importable: project, archive_file: filepath, shared: shared) }

    before do
      Zlib::GzipWriter.open(filepath) do |gz|
        gz.write('Hello World!')
      end
    end

    context 'when validate_import_decompressed_archive_size feature flag is enabled' do
      before do
        stub_feature_flags(validate_import_decompressed_archive_size: true)

        allow(Gitlab::ImportExport::DecompressedArchiveSizeValidator).to receive(:max_bytes).and_return(1)
      end

      it 'returns false' do
        expect(subject.import).to eq(false)
        expect(shared.errors.join).to eq('Decompressed archive size validation failed.')
      end
    end

    context 'when validate_import_decompressed_archive_size feature flag is disabled' do
      before do
        stub_feature_flags(validate_import_decompressed_archive_size: false)
      end

      it 'skips validation' do
        expect(subject).to receive(:validate_decompressed_archive_size).never

        subject.import
      end
    end
  end

  def setup_files
    FileUtils.mkdir_p("#{shared.export_path}/subfolder/")
    FileUtils.touch(valid_file)
    FileUtils.ln_s(valid_file, symlink_file)
    FileUtils.ln_s(valid_file, subfolder_symlink_file)
    FileUtils.ln_s(valid_file, hidden_symlink_file)
    FileUtils.ln_s(valid_file, evil_symlink_file)
    FileUtils.ln_s(valid_file, custom_mode_symlink_file)
    FileUtils.chmod_R(0000, custom_mode_symlink_file)
  end
end