Welcome to mirror list, hosted at ThFree Co, Russian Federation.

decompressed_gzip_size_validator_spec.rb « ci « gitlab « lib « spec - gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: dad5bd2548b82753c9e7782923ea81688a5a32e8 (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
# frozen_string_literal: true

require 'spec_helper'

RSpec.describe Gitlab::Ci::DecompressedGzipSizeValidator, feature_category: :importers do
  let_it_be(:filepath) { File.join(Dir.tmpdir, 'decompressed_gzip_size_validator_spec.gz') }

  before(:all) do
    create_compressed_file
  end

  after(:all) do
    FileUtils.rm(filepath)
  end

  subject { described_class.new(archive_path: filepath, max_bytes: max_bytes) }

  describe '#valid?' do
    let(:max_bytes) { 20 }

    context 'when file does not exceed allowed decompressed size' do
      it 'returns true' do
        expect(subject.valid?).to eq(true)
      end

      context 'when the waiter thread no longer exists due to being terminated or crashing' do
        it 'gracefully handles the absence of the waiter without raising exception' do
          allow(Process).to receive(:getpgid).and_raise(Errno::ESRCH)

          expect(subject.valid?).to eq(true)
        end
      end
    end

    context 'when file exceeds allowed decompressed size' do
      let(:max_bytes) { 1 }

      it 'returns false' do
        expect(subject.valid?).to eq(false)
      end
    end

    context 'when exception occurs during header readings' do
      shared_examples 'raises exception and terminates validator process group' do
        let(:std) { instance_double(IO, close: nil) }
        let(:wait_thr) { double }
        let(:wait_threads) { [wait_thr, wait_thr] }

        before do
          allow(Process).to receive(:getpgid).and_return(2)
          allow(Open3).to receive(:pipeline_r).and_return([std, wait_threads])
          allow(wait_thr).to receive(:[]).with(:pid).and_return(1)
          allow(wait_thr).to receive(:value).and_raise(exception)
        end

        it 'terminates validator process group' do
          expect(Process).to receive(:kill).with(-1, 2).twice
          expect(subject.valid?).to eq(false)
        end
      end

      context 'when timeout occurs' do
        let(:exception) { Timeout::Error }

        include_examples 'raises exception and terminates validator process group'
      end

      context 'when exception occurs' do
        let(:error_message) { 'Error!' }
        let(:exception) { StandardError.new(error_message) }

        include_examples 'raises exception and terminates validator process group'
      end
    end

    describe 'archive path validation' do
      let(:filesize) { nil }

      context 'when archive path is traversed' do
        let(:filepath) { '/foo/../bar' }

        it 'does not pass validation' do
          expect(subject.valid?).to eq(false)
        end
      end
    end

    context 'when archive path is not a string' do
      let(:filepath) { 123 }

      it 'returns false' do
        expect(subject.valid?).to eq(false)
      end
    end

    context 'when archive path is a symlink' do
      let(:filepath) { File.join(Dir.tmpdir, 'symlink') }

      before do
        FileUtils.ln_s(filepath, filepath, force: true)
      end

      it 'returns false' do
        expect(subject.valid?).to eq(false)
      end
    end

    context 'when archive path has multiple hard links' do
      before do
        FileUtils.link(filepath, File.join(Dir.mktmpdir, 'hard_link'))
      end

      it 'returns false' do
        expect(subject).not_to be_valid
      end
    end

    context 'when archive path is not a file' do
      let(:filepath) { Dir.mktmpdir }
      let(:filesize) { File.size(filepath) }

      after do
        FileUtils.rm_rf(filepath)
      end

      it 'returns false' do
        expect(subject.valid?).to eq(false)
      end
    end
  end

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