diff options
Diffstat (limited to 'spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb')
-rw-r--r-- | spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb b/spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb new file mode 100644 index 00000000000..af551861d47 --- /dev/null +++ b/spec/lib/gitlab/background_migration/fix_vulnerability_occurrences_with_hashes_as_raw_metadata_spec.rb @@ -0,0 +1,232 @@ +# frozen_string_literal: true + +require 'spec_helper' + +RSpec.describe Gitlab::BackgroundMigration::FixVulnerabilityOccurrencesWithHashesAsRawMetadata, schema: 20211209203821 do + let(:users) { table(:users) } + let(:namespaces) { table(:namespaces) } + let(:projects) { table(:projects) } + let(:scanners) { table(:vulnerability_scanners) } + let(:identifiers) { table(:vulnerability_identifiers) } + let(:findings) { table(:vulnerability_occurrences) } + + let(:user) { users.create!(name: 'Test User', projects_limit: 10, username: 'test-user', email: '1') } + + let(:namespace) do + namespaces.create!( + owner_id: user.id, + name: user.name, + path: user.username + ) + end + + let(:project) do + projects.create!(namespace_id: namespace.id, name: 'Test Project') + end + + let(:scanner) do + scanners.create!( + project_id: project.id, + external_id: 'test-scanner', + name: 'Test Scanner', + vendor: 'GitLab' + ) + end + + let(:primary_identifier) do + identifiers.create!( + project_id: project.id, + external_type: 'cve', + name: 'CVE-2021-1234', + external_id: 'CVE-2021-1234', + fingerprint: '4c0fe491999f94701ee437588554ef56322ae276' + ) + end + + let(:finding) do + findings.create!( + raw_metadata: raw_metadata, + project_id: project.id, + scanner_id: scanner.id, + primary_identifier_id: primary_identifier.id, + uuid: '4deb090a-bedf-5ccc-aa9a-ac8055a1ea81', + project_fingerprint: '1caa750a6dad769a18ad6f40b413b3b6ab1c8d77', + location_fingerprint: '6d1f35f53b065238abfcadc01336ce65d112a2bd', + name: 'name', + report_type: 7, + severity: 0, + confidence: 0, + detection_method: 'gitlab_security_report', + metadata_version: 'cluster_image_scanning:1.0', + created_at: "2021-12-10 14:27:42 -0600", + updated_at: "2021-12-10 14:27:42 -0600" + ) + end + + subject(:perform) { described_class.new.perform(finding.id, finding.id) } + + context 'with stringified hash as raw_metadata' do + let(:raw_metadata) do + '{:location=>{"image"=>"index.docker.io/library/nginx:latest", "kubernetes_resource"=>{"namespace"=>"production", "kind"=>"deployment", "name"=>"nginx", "container_name"=>"nginx", "agent_id"=>"2"}, "dependency"=>{"package"=>{"name"=>"libc"}, "version"=>"v1.2.3"}}}' + end + + it 'converts stringified hash to JSON' do + expect { perform }.not_to raise_error + + result = finding.reload.raw_metadata + metadata = Oj.load(result) + expect(metadata).to eq( + { + 'location' => { + 'image' => 'index.docker.io/library/nginx:latest', + 'kubernetes_resource' => { + 'namespace' => 'production', + 'kind' => 'deployment', + 'name' => 'nginx', + 'container_name' => 'nginx', + 'agent_id' => '2' + }, + 'dependency' => { + 'package' => { 'name' => 'libc' }, + 'version' => 'v1.2.3' + } + } + } + ) + end + end + + context 'with valid raw_metadata' do + where(:raw_metadata) do + [ + '{}', + '{"location":null}', + '{"location":{"image":"index.docker.io/library/nginx:latest","kubernetes_resource":{"namespace":"production","kind":"deployment","name":"nginx","container_name":"nginx","agent_id":"2"},"dependency":{"package":{"name":"libc"},"version":"v1.2.3"}}}' + ] + end + + with_them do + it 'does not change the raw_metadata' do + expect { perform }.not_to raise_error + + result = finding.reload.raw_metadata + expect(result).to eq(raw_metadata) + end + end + end + + context 'when raw_metadata contains forbidden types' do + using RSpec::Parameterized::TableSyntax + + where(:raw_metadata, :type) do + 'def foo; "bar"; end' | :def + '`cat somefile`' | :xstr + 'exec("cat /etc/passwd")' | :send + end + + with_them do + it 'does not change the raw_metadata' do + expect(Gitlab::AppLogger).to receive(:error).with(message: "expected raw_metadata to be a hash", type: type) + + expect { perform }.not_to raise_error + + result = finding.reload.raw_metadata + expect(result).to eq(raw_metadata) + end + end + end + + context 'when forbidden types are nested inside a hash' do + using RSpec::Parameterized::TableSyntax + + where(:raw_metadata, :type) do + '{:location=>Env.fetch("SOME_VAR")}' | :send + '{:location=>{:image=>Env.fetch("SOME_VAR")}}' | :send + # rubocop:disable Lint/InterpolationCheck + '{"key"=>"value: #{send}"}' | :dstr + # rubocop:enable Lint/InterpolationCheck + end + + with_them do + it 'does not change the raw_metadata' do + expect(Gitlab::AppLogger).to receive(:error).with( + message: "error parsing raw_metadata", + error: "value of a pair was an unexpected type", + type: type + ) + + expect { perform }.not_to raise_error + + result = finding.reload.raw_metadata + expect(result).to eq(raw_metadata) + end + end + end + + context 'when key is an unexpected type' do + let(:raw_metadata) { "{nil=>nil}" } + + it 'logs error' do + expect(Gitlab::AppLogger).to receive(:error).with( + message: "error parsing raw_metadata", + error: "expected key to be either symbol, string, or integer", + type: :nil + ) + + expect { perform }.not_to raise_error + end + end + + context 'when raw_metadata cannot be parsed' do + let(:raw_metadata) { "{" } + + it 'logs error' do + expect(Gitlab::AppLogger).to receive(:error).with(message: "error parsing raw_metadata", error: "unexpected token $end") + + expect { perform }.not_to raise_error + end + end + + describe '#hash_from_s' do + subject { described_class.new.hash_from_s(input) } + + context 'with valid input' do + let(:input) { '{:location=>{"image"=>"index.docker.io/library/nginx:latest", "kubernetes_resource"=>{"namespace"=>"production", "kind"=>"deployment", "name"=>"nginx", "container_name"=>"nginx", "agent_id"=>2}, "dependency"=>{"package"=>{"name"=>"libc"}, "version"=>"v1.2.3"}}}' } + + it 'converts string to a hash' do + expect(subject).to eq({ + location: { + 'image' => 'index.docker.io/library/nginx:latest', + 'kubernetes_resource' => { + 'namespace' => 'production', + 'kind' => 'deployment', + 'name' => 'nginx', + 'container_name' => 'nginx', + 'agent_id' => 2 + }, + 'dependency' => { + 'package' => { 'name' => 'libc' }, + 'version' => 'v1.2.3' + } + } + }) + end + end + + using RSpec::Parameterized::TableSyntax + + where(:input, :expected) do + '{}' | {} + '{"bool"=>true}' | { 'bool' => true } + '{"bool"=>false}' | { 'bool' => false } + '{"nil"=>nil}' | { 'nil' => nil } + '{"array"=>[1, "foo", nil]}' | { 'array' => [1, "foo", nil] } + '{foo: :bar}' | { foo: :bar } + '{foo: {bar: "bin"}}' | { foo: { bar: "bin" } } + end + + with_them do + specify { expect(subject).to eq(expected) } + end + end +end |