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

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2023-06-03 00:59:19 +0300
committerGitLab Bot <gitlab-bot@gitlab.com>2023-06-03 00:59:19 +0300
commit1385478346704d03ab9d3a9bf8ae3812cea0b6b5 (patch)
treec2b68728119200c48fbfe09bb09397d4e31659b7 /spec/lib/gitlab/ci
parent361d9dae8bafae8c830d68d16ea0f76482ba9343 (diff)
Add latest changes from gitlab-org/security/gitlab@16-0-stable-ee
Diffstat (limited to 'spec/lib/gitlab/ci')
-rw-r--r--spec/lib/gitlab/ci/artifacts/decompressed_artifact_size_validator_spec.rb95
-rw-r--r--spec/lib/gitlab/ci/decompressed_gzip_size_validator_spec.rb127
2 files changed, 222 insertions, 0 deletions
diff --git a/spec/lib/gitlab/ci/artifacts/decompressed_artifact_size_validator_spec.rb b/spec/lib/gitlab/ci/artifacts/decompressed_artifact_size_validator_spec.rb
new file mode 100644
index 00000000000..ef39a431d63
--- /dev/null
+++ b/spec/lib/gitlab/ci/artifacts/decompressed_artifact_size_validator_spec.rb
@@ -0,0 +1,95 @@
+# frozen_string_literal: true
+
+require 'spec_helper'
+
+RSpec.describe Gitlab::Ci::Artifacts::DecompressedArtifactSizeValidator, feature_category: :build_artifacts do
+ include WorkhorseHelpers
+
+ let_it_be(:file_path) { File.join(Dir.tmpdir, 'decompressed_archive_size_validator_spec.gz') }
+ let(:file) { File.open(file_path) }
+ let(:file_format) { :gzip }
+ let(:max_bytes) { 20 }
+ let(:gzip_valid?) { true }
+ let(:validator) { instance_double(::Gitlab::Ci::DecompressedGzipSizeValidator, valid?: gzip_valid?) }
+
+ before(:all) do
+ Zlib::GzipWriter.open(file_path) do |gz|
+ gz.write('Hello World!')
+ end
+ end
+
+ after(:all) do
+ FileUtils.rm(file_path)
+ end
+
+ before do
+ allow(::Gitlab::Ci::DecompressedGzipSizeValidator)
+ .to receive(:new)
+ .and_return(validator)
+ end
+
+ subject { described_class.new(file: file, file_format: file_format, max_bytes: max_bytes) }
+
+ shared_examples 'when file does not exceed allowed compressed size' do
+ let(:gzip_valid?) { true }
+
+ it 'passes validation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+
+ shared_examples 'when file exceeds allowed decompressed size' do
+ let(:gzip_valid?) { false }
+
+ it 'raises an exception' do
+ expect { subject.validate! }
+ .to raise_error(Gitlab::Ci::Artifacts::DecompressedArtifactSizeValidator::FileDecompressionError)
+ end
+ end
+
+ describe '#validate!' do
+ it_behaves_like 'when file does not exceed allowed compressed size'
+
+ it_behaves_like 'when file exceeds allowed decompressed size'
+ end
+
+ context 'when file is not provided' do
+ let(:file) { nil }
+
+ it 'passes validation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+
+ context 'when the file is located in the cloud' do
+ let(:remote_path) { File.join(remote_store_path, remote_id) }
+
+ let(:file_url) { "http://s3.amazonaws.com/#{remote_path}" }
+ let(:file) do
+ instance_double(JobArtifactUploader,
+ path: file_path,
+ url: file_url,
+ object_store: ObjectStorage::Store::REMOTE)
+ end
+
+ let(:remote_id) { 'generated-remote-id-12345' }
+ let(:remote_store_path) { ObjectStorage::TMP_UPLOAD_PATH }
+
+ before do
+ stub_request(:get, %r{s3.amazonaws.com/#{remote_path}})
+ .to_return(status: 200, body: File.read('spec/fixtures/build.env.gz'))
+ end
+
+ it_behaves_like 'when file does not exceed allowed compressed size'
+
+ it_behaves_like 'when file exceeds allowed decompressed size'
+ end
+
+ context 'when file_format is not on the list' do
+ let_it_be(:file_format) { 'rar' }
+
+ it 'passes validation' do
+ expect { subject.validate! }.not_to raise_error
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/decompressed_gzip_size_validator_spec.rb b/spec/lib/gitlab/ci/decompressed_gzip_size_validator_spec.rb
new file mode 100644
index 00000000000..6ca3f4d415e
--- /dev/null
+++ b/spec/lib/gitlab/ci/decompressed_gzip_size_validator_spec.rb
@@ -0,0 +1,127 @@
+# 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 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