Commit d4ef47a2 authored by Olivier Gonzalez's avatar Olivier Gonzalez Committed by Kamil Trzciński

Use POROs for security report vulnerabilities

Generate POROS instead of hashes when parsing security reports
parent adfb33b8
...@@ -38,7 +38,7 @@ module Security ...@@ -38,7 +38,7 @@ module Security
def create_vulnerability(occurrence) def create_vulnerability(occurrence)
vulnerability = create_or_find_vulnerability_object(occurrence) vulnerability = create_or_find_vulnerability_object(occurrence)
occurrence[:identifiers].map do |identifier| occurrence.identifiers.map do |identifier|
create_vulnerability_identifier_object(vulnerability, identifier) create_vulnerability_identifier_object(vulnerability, identifier)
end end
...@@ -48,14 +48,13 @@ module Security ...@@ -48,14 +48,13 @@ module Security
# rubocop: disable CodeReuse/ActiveRecord # rubocop: disable CodeReuse/ActiveRecord
def create_or_find_vulnerability_object(occurrence) def create_or_find_vulnerability_object(occurrence)
find_params = { find_params = {
scanner: scanners_objects[occurrence[:scanner]], scanner: scanners_objects[occurrence.scanner.key],
primary_identifier: identifiers_objects[occurrence[:primary_identifier]], primary_identifier: identifiers_objects[occurrence.primary_identifier.key],
location_fingerprint: occurrence[:location_fingerprint] location_fingerprint: occurrence.location_fingerprint
} }
create_params = occurrence.except( create_params = occurrence.to_hash
:scanner, :primary_identifier, .except(:compare_key, :identifiers, :scanner) # rubocop: disable CodeReuse/ActiveRecord
:location_fingerprint, :identifiers)
begin begin
project.vulnerabilities project.vulnerabilities
...@@ -69,7 +68,7 @@ module Security ...@@ -69,7 +68,7 @@ module Security
def create_vulnerability_identifier_object(vulnerability, identifier) def create_vulnerability_identifier_object(vulnerability, identifier)
vulnerability.occurrence_identifiers.find_or_create_by!( # rubocop: disable CodeReuse/ActiveRecord vulnerability.occurrence_identifiers.find_or_create_by!( # rubocop: disable CodeReuse/ActiveRecord
identifier: identifiers_objects[identifier]) identifier: identifiers_objects[identifier.key])
rescue ActiveRecord::RecordNotUnique rescue ActiveRecord::RecordNotUnique
end end
...@@ -81,13 +80,13 @@ module Security ...@@ -81,13 +80,13 @@ module Security
def scanners_objects def scanners_objects
strong_memoize(:scanners_objects) do strong_memoize(:scanners_objects) do
@report.scanners.map do |key, scanner| @report.scanners.map do |key, scanner|
[key, existing_scanner_objects[key] || project.vulnerability_scanners.build(scanner)] [key, existing_scanner_objects[key] || project.vulnerability_scanners.build(scanner.to_hash)]
end.to_h end.to_h
end end
end end
def all_scanners_external_ids def all_scanners_external_ids
@report.scanners.values.map { |scanner| scanner[:external_id] } @report.scanners.values.map(&:external_id)
end end
def existing_scanner_objects def existing_scanner_objects
...@@ -101,13 +100,13 @@ module Security ...@@ -101,13 +100,13 @@ module Security
def identifiers_objects def identifiers_objects
strong_memoize(:identifiers_objects) do strong_memoize(:identifiers_objects) do
@report.identifiers.map do |key, identifier| @report.identifiers.map do |key, identifier|
[key, existing_identifiers_objects[key] || project.vulnerability_identifiers.build(identifier)] [key, existing_identifiers_objects[key] || project.vulnerability_identifiers.build(identifier.to_hash)]
end.to_h end.to_h
end end
end end
def all_identifiers_fingerprints def all_identifiers_fingerprints
@report.identifiers.values.map { |identifier| identifier[:fingerprint] } @report.identifiers.values.map(&:fingerprint)
end end
def existing_identifiers_objects def existing_identifiers_objects
......
...@@ -44,29 +44,28 @@ module Gitlab ...@@ -44,29 +44,28 @@ module Gitlab
def create_vulnerability(report, data, version) def create_vulnerability(report, data, version)
scanner = create_scanner(report, data['scanner'] || mutate_scanner_tool(data['tool'])) scanner = create_scanner(report, data['scanner'] || mutate_scanner_tool(data['tool']))
identifiers = create_identifiers(report, data['identifiers']) identifiers = create_identifiers(report, data['identifiers'])
report.add_occurrence( report.add_occurrence(
::Gitlab::Ci::Reports::Security::Occurrence.new(
uuid: SecureRandom.uuid, uuid: SecureRandom.uuid,
report_type: report.type, report_type: report.type,
name: data['message'], name: data['message'],
primary_identifier: identifiers.first, compare_key: data['cve'],
project_fingerprint: generate_project_fingerprint(data['cve']),
location_fingerprint: generate_location_fingerprint(data['location']), location_fingerprint: generate_location_fingerprint(data['location']),
severity: parse_level(data['severity']), severity: parse_level(data['severity']),
confidence: parse_level(data['confidence']), confidence: parse_level(data['confidence']),
scanner: scanner, scanner: scanner,
identifiers: identifiers, identifiers: identifiers,
raw_metadata: data.to_json, raw_metadata: data.to_json,
metadata_version: version metadata_version: version))
)
end end
def create_scanner(report, scanner) def create_scanner(report, scanner)
return unless scanner.is_a?(Hash) return unless scanner.is_a?(Hash)
report.add_scanner( report.add_scanner(
::Gitlab::Ci::Reports::Security::Scanner.new(
external_id: scanner['id'], external_id: scanner['id'],
name: scanner['name']) name: scanner['name']))
end end
def create_identifiers(report, identifiers) def create_identifiers(report, identifiers)
...@@ -81,13 +80,14 @@ module Gitlab ...@@ -81,13 +80,14 @@ module Gitlab
return unless identifier.is_a?(Hash) return unless identifier.is_a?(Hash)
report.add_identifier( report.add_identifier(
::Gitlab::Ci::Reports::Security::Identifier.new(
external_type: identifier['type'], external_type: identifier['type'],
external_id: identifier['value'], external_id: identifier['value'],
name: identifier['name'], name: identifier['name'],
fingerprint: generate_identifier_fingerprint(identifier), url: identifier['url']))
url: identifier['url'])
end end
# TODO: this can be removed as of `12.0`
def mutate_scanner_tool(tool) def mutate_scanner_tool(tool)
{ 'id' => tool, 'name' => tool.capitalize } if tool { 'id' => tool, 'name' => tool.capitalize } if tool
end end
...@@ -96,14 +96,6 @@ module Gitlab ...@@ -96,14 +96,6 @@ module Gitlab
input.blank? ? 'undefined' : input.downcase input.blank? ? 'undefined' : input.downcase
end end
def generate_project_fingerprint(compare_key)
Digest::SHA1.hexdigest(compare_key)
end
def generate_identifier_fingerprint(identifier)
Digest::SHA1.hexdigest("#{identifier['type']}:#{identifier['value']}")
end
def generate_location_fingerprint(location) def generate_location_fingerprint(location)
raise NotImplementedError raise NotImplementedError
end end
......
# frozen_string_literal: true
module Gitlab
module Ci
module Reports
module Security
class Identifier
attr_reader :external_id
attr_reader :external_type
attr_reader :fingerprint
attr_reader :name
attr_reader :url
def initialize(external_id:, external_type:, name:, url: nil)
@external_id = external_id
@external_type = external_type
@name = name
@url = url
@fingerprint = generate_fingerprint
end
def key
fingerprint
end
def to_hash
%i[
external_id
external_type
fingerprint
name
url
].each_with_object({}) do |key, hash|
hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
def ==(other)
other.external_type == external_type &&
other.external_id == external_id
end
private
def generate_fingerprint
Digest::SHA1.hexdigest("#{external_type}:#{external_id}")
end
end
end
end
end
end
# frozen_string_literal: true
module Gitlab
module Ci
module Reports
module Security
class Occurrence
attr_reader :compare_key
attr_reader :confidence
attr_reader :identifiers
attr_reader :location_fingerprint
attr_reader :metadata_version
attr_reader :name
attr_reader :project_fingerprint
attr_reader :raw_metadata
attr_reader :report_type
attr_reader :scanner
attr_reader :severity
attr_reader :uuid
def initialize(compare_key:, identifiers:, location_fingerprint:, metadata_version:, name:, raw_metadata:, report_type:, scanner:, uuid:, confidence: nil, severity: nil) # rubocop:disable Metrics/ParameterLists
@compare_key = compare_key
@confidence = confidence
@identifiers = identifiers
@location_fingerprint = location_fingerprint
@metadata_version = metadata_version
@name = name
@raw_metadata = raw_metadata
@report_type = report_type
@scanner = scanner
@severity = severity
@uuid = uuid
@project_fingerprint = generate_project_fingerprint
end
def to_hash
%i[
compare_key
confidence
identifiers
location_fingerprint
metadata_version
name
project_fingerprint
raw_metadata
report_type
scanner
severity
uuid
].each_with_object({}) do |key, hash|
hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
def primary_identifier
identifiers.first
end
def ==(other)
other.report_type == report_type &&
other.location_fingerprint == location_fingerprint &&
other.primary_identifier == primary_identifier
end
private
def generate_project_fingerprint
Digest::SHA1.hexdigest(compare_key)
end
end
end
end
end
end
...@@ -22,34 +22,18 @@ module Gitlab ...@@ -22,34 +22,18 @@ module Gitlab
error.present? error.present?
end end
def add_scanner(params) def add_scanner(scanner)
scanner_key(params).tap do |key| scanners[scanner.key] ||= scanner
scanners[key] ||= params
end
end end
def add_identifier(params) def add_identifier(identifier)
identifier_key(params).tap do |key| identifiers[identifier.key] ||= identifier
identifiers[key] ||= params
end
end end
def add_occurrence(params) def add_occurrence(occurrence)
params.tap do |occurrence|
occurrences << occurrence occurrences << occurrence
end end
end end
private
def scanner_key(params)
params.fetch(:external_id)
end
def identifier_key(params)
params.fetch(:fingerprint)
end
end
end end
end end
end end
......
# frozen_string_literal: true
module Gitlab
module Ci
module Reports
module Security
class Scanner
attr_accessor :external_id, :name
def initialize(external_id:, name:)
@external_id = external_id
@name = name
end
def key
external_id
end
def to_hash
%i[
external_id
name
].each_with_object({}) do |key, hash|
hash[key] = public_send(key) # rubocop:disable GitlabSecurity/PublicSend
end
end
def ==(other)
other.external_id == external_id
end
end
end
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :ci_reports_security_identifier, class: ::Gitlab::Ci::Reports::Security::Identifier do
external_id 'PREDICTABLE_RANDOM'
external_type 'find_sec_bugs_type'
name { "#{external_type}-#{external_id}" }
skip_create
initialize_with do
::Gitlab::Ci::Reports::Security::Identifier.new(attributes)
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :ci_reports_security_occurrence, class: ::Gitlab::Ci::Reports::Security::Occurrence do
compare_key 'this_is_supposed_to_be_a_unique_value'
confidence :medium
identifiers { Array.new(1) { FactoryBot.build(:ci_reports_security_identifier) } }
location_fingerprint '4e5b6966dd100170b4b1ad599c7058cce91b57b4'
metadata_version 'sast:1.0'
name 'Cipher with no integrity'
report_type :sast
raw_metadata do
{
description: "The cipher does not provide data integrity update 1",
solution: "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
location: {
file: "maven/src/main/java/com/gitlab/security_products/tests/App.java",
start_line: 29,
end_line: 29,
class: "com.gitlab.security_products.tests.App",
method: "insecureCypher"
},
links: [
{
name: "Cipher does not check for integrity first?",
url: "https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first"
}
]
}.to_json
end
scanner factory: :ci_reports_security_scanner
severity :high
sequence(:uuid) { generate(:vulnerability_occurrence_uuid) }
skip_create
initialize_with do
::Gitlab::Ci::Reports::Security::Occurrence.new(attributes)
end
end
end
# frozen_string_literal: true
FactoryBot.define do
factory :ci_reports_security_scanner, class: ::Gitlab::Ci::Reports::Security::Scanner do
external_id 'find_sec_bugs'
name 'Find Security Bugs'
skip_create
initialize_with do
::Gitlab::Ci::Reports::Security::Scanner.new(attributes)
end
end
end
...@@ -34,15 +34,15 @@ describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -34,15 +34,15 @@ describe Gitlab::Ci::Parsers::Security::ContainerScanning do
it "generates expected location fingerprint" do it "generates expected location fingerprint" do
expected = Digest::SHA1.hexdigest('debian:9:glibc') expected = Digest::SHA1.hexdigest('debian:9:glibc')
expect(report.occurrences.first[:location_fingerprint]).to eq(expected) expect(report.occurrences.first.location_fingerprint).to eq(expected)
end end
it "generates expected metadata_version" do it "generates expected metadata_version" do
expect(report.occurrences.first[:metadata_version]).to eq('1.3') expect(report.occurrences.first.metadata_version).to eq('1.3')
end end
it "adds report image's name to raw_metadata" do it "adds report image's name to raw_metadata" do
expect(JSON.parse(report.occurrences.first[:raw_metadata]).dig('location', 'image')) expect(JSON.parse(report.occurrences.first.raw_metadata).dig('location', 'image'))
.to eq('registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff') .to eq('registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff')
end end
end end
......
...@@ -27,8 +27,8 @@ describe Gitlab::Ci::Parsers::Security::Dast do ...@@ -27,8 +27,8 @@ describe Gitlab::Ci::Parsers::Security::Dast do
expected1 = Digest::SHA1.hexdigest(':GET:X-Content-Type-Options') expected1 = Digest::SHA1.hexdigest(':GET:X-Content-Type-Options')
expected2 = Digest::SHA1.hexdigest('/:GET:X-Content-Type-Options') expected2 = Digest::SHA1.hexdigest('/:GET:X-Content-Type-Options')
expect(report.occurrences.first[:location_fingerprint]).to eq(expected1) expect(report.occurrences.first.location_fingerprint).to eq(expected1)
expect(report.occurrences.last[:location_fingerprint]).to eq(expected2) expect(report.occurrences.last.location_fingerprint).to eq(expected2)
end end
describe 'occurrence properties' do describe 'occurrence properties' do
...@@ -44,7 +44,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do ...@@ -44,7 +44,7 @@ describe Gitlab::Ci::Parsers::Security::Dast do
it 'saves properly occurrence' do it 'saves properly occurrence' do
occurrence = report.occurrences.last occurrence = report.occurrences.last
expect(occurrence[attribute]).to eq(value) expect(occurrence.public_send(attribute)).to eq(value)
end end
end end
end end
......
...@@ -34,11 +34,11 @@ describe Gitlab::Ci::Parsers::Security::DependencyScanning do ...@@ -34,11 +34,11 @@ describe Gitlab::Ci::Parsers::Security::DependencyScanning do
end end
it "generates expected location fingerprint" do it "generates expected location fingerprint" do
expect(report.occurrences.first[:location_fingerprint]).to eq(fingerprint) expect(report.occurrences.first.location_fingerprint).to eq(fingerprint)
end end
it "generates expected metadata_version" do it "generates expected metadata_version" do
expect(report.occurrences.first[:metadata_version]).to eq(version) expect(report.occurrences.first.metadata_version).to eq(version)
end end
end end
...@@ -53,9 +53,9 @@ describe Gitlab::Ci::Parsers::Security::DependencyScanning do ...@@ -53,9 +53,9 @@ describe Gitlab::Ci::Parsers::Security::DependencyScanning do
it "generates occurrence with expected remediation" do it "generates occurrence with expected remediation" do
occurrence = report.occurrences.last occurrence = report.occurrences.last
raw_metadata = JSON.parse!(occurrence[:raw_metadata]) raw_metadata = JSON.parse!(occurrence.raw_metadata)
expect(occurrence[:name]).to eq("Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js") expect(occurrence.name).to eq("Authentication bypass via incorrect DOM traversal and canonicalization in saml2-js")
expect(raw_metadata["remediations"].first["summary"]).to eq("Upgrade saml2-js") expect(raw_metadata["remediations"].first["summary"]).to eq("Upgrade saml2-js")
expect(raw_metadata["remediations"].first["diff"]).to start_with("ZGlmZiAtLWdpdCBhL3lhcm4") expect(raw_metadata["remediations"].first["diff"]).to start_with("ZGlmZiAtLWdpdCBhL3lhcm4")
end end
......
...@@ -27,11 +27,11 @@ describe Gitlab::Ci::Parsers::Security::Sast do ...@@ -27,11 +27,11 @@ describe Gitlab::Ci::Parsers::Security::Sast do
end end
it "generates expected location fingerprint" do it "generates expected location fingerprint" do
expect(report.occurrences.first[:location_fingerprint]).to eq('d869ba3f0b3347eb2749135a437dc07c8ae0f420') expect(report.occurrences.first.location_fingerprint).to eq('d869ba3f0b3347eb2749135a437dc07c8ae0f420')
end end
it "generates expected metadata_version" do it "generates expected metadata_version" do
expect(report.occurrences.first[:metadata_version]).to eq('1.2') expect(report.occurrences.first.metadata_version).to eq('1.2')
end end
end end
end end
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Ci::Reports::Security::Identifier do
describe '#initialize' do
subject { described_class.new(**params) }
let(:params) do
{
external_type: 'brakeman_warning_code',
external_id: '107',
name: 'Brakeman Warning Code 107',
url: 'https://brakemanscanner.org/docs/warning_types/cross_site_scripting/'
}
end
context 'when all params are given' do
it 'initializes an instance' do
expect { subject }.not_to raise_error
expect(subject).to have_attributes(
external_type: 'brakeman_warning_code',
external_id: '107',
fingerprint: 'aa2254904a69148ad14b6ac5db25b355da9c987f',
name: 'Brakeman Warning Code 107',
url: 'https://brakemanscanner.org/docs/warning_types/cross_site_scripting/'
)
end
end
%i[external_type external_id name].each do |attribute|
context "when attribute #{attribute} is missing" do
before do
params.delete(attribute)
end
it 'raises an error' do
expect { subject }.to raise_error(ArgumentError)
end
end
end
end
describe '#key' do
let(:identifier) { create(:ci_reports_security_identifier) }
subject { identifier.key }
it 'returns fingerprint' do
is_expected.to eq(identifier.fingerprint)
end
end
describe '#to_hash' do
let(:identifier) { create(:ci_reports_security_identifier) }
subject { identifier.to_hash }
it 'returns expected hash' do
is_expected.to eq({
external_type: identifier.external_type,
external_id: identifier.external_id,
fingerprint: identifier.fingerprint,
name: identifier.name,
url: identifier.url
})
end
end
describe '#==' do
using RSpec::Parameterized::TableSyntax
where(:type_1, :id_1, :type_2, :id_2, :equal, :case_name) do
'CVE' | '2018-1234' | 'CVE' | '2018-1234' | true | 'when external_type and external_id are equal'
'CVE' | '2018-1234' | 'brakeman_code' | '2018-1234' | false | 'when external_type is different'
'CVE' | '2018-1234' | 'CVE' | '2019-6789' | false | 'when external_id is different'
end
with_them do
let(:identifier_1) { create(:ci_reports_security_identifier, external_type: type_1, external_id: id_1) }
let(:identifier_2) { create(:ci_reports_security_identifier, external_type: type_2, external_id: id_2) }
it "returns #{params[:equal]}" do
expect(identifier_1 == identifier_2).to eq(equal)
end
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Ci::Reports::Security::Occurrence do
describe '#initialize' do
subject { described_class.new(**params) }
let(:primary_identifier) { create(:ci_reports_security_identifier) }
let(:other_identifier) { create(:ci_reports_security_identifier) }
let(:scanner) { create(:ci_reports_security_scanner) }
let(:params) do
{
compare_key: 'this_is_supposed_to_be_a_unique_value',
confidence: :medium,
identifiers: [primary_identifier, other_identifier],
location_fingerprint: '4e5b6966dd100170b4b1ad599c7058cce91b57b4',
metadata_version: 'sast:1.0',
name: 'Cipher with no integrity',
raw_metadata: 'I am a stringified json object',
report_type: :sast,
scanner: scanner,
severity: :high,
uuid: 'cadf8cf0a8228fa92a0f4897a0314083bb38'
}
end
context 'when both all params are given' do
it 'initializes an instance' do
expect { subject }.not_to raise_error
expect(subject).to have_attributes(
compare_key: 'this_is_supposed_to_be_a_unique_value',
confidence: :medium,
project_fingerprint: '9a73f32d58d87d94e3dc61c4c1a94803f6014258',
identifiers: [primary_identifier, other_identifier],
location_fingerprint: '4e5b6966dd100170b4b1ad599c7058cce91b57b4',
metadata_version: 'sast:1.0',
name: 'Cipher with no integrity',
raw_metadata: 'I am a stringified json object',
report_type: :sast,
scanner: scanner,
severity: :high,
uuid: 'cadf8cf0a8228fa92a0f4897a0314083bb38'
)
end
end
%i[compare_key identifiers location_fingerprint metadata_version name raw_metadata report_type scanner uuid].each do |attribute|
context "when attribute #{attribute} is missing" do
before do
params.delete(attribute)
end
it 'raises an error' do
expect { subject }.to raise_error(ArgumentError)
end
end
end
end
describe '#to_hash' do
let(:occurrence) { create(:ci_reports_security_occurrence) }
subject { occurrence.to_hash }
it 'returns expected hash' do
is_expected.to eq({
compare_key: occurrence.compare_key,
confidence: occurrence.confidence,
identifiers: occurrence.identifiers,
location_fingerprint: occurrence.location_fingerprint,
metadata_version: occurrence.metadata_version,
name: occurrence.name,
project_fingerprint: occurrence.project_fingerprint,
raw_metadata: occurrence.raw_metadata,
report_type: occurrence.report_type,
scanner: occurrence.scanner,
severity: occurrence.severity,
uuid: occurrence.uuid
})
end
end
describe '#primary_identifier' do
let(:primary_identifier) { create(:ci_reports_security_identifier) }
let(:other_identifier) { create(:ci_reports_security_identifier) }
let(:occurrence) { create(:ci_reports_security_occurrence, identifiers: [primary_identifier, other_identifier]) }
subject { occurrence.primary_identifier }
it 'returns the first identifier' do
is_expected.to eq(primary_identifier)
end
end
describe '#==' do
using RSpec::Parameterized::TableSyntax
let(:identifier) { create(:ci_reports_security_identifier) }
let(:other_identifier) { create(:ci_reports_security_identifier, external_type: 'other_identifier') }
report_type = 'sast'
fingerprint = '4e5b6966dd100170b4b1ad599c7058cce91b57b4'
other_report_type = 'dependency_scanning'
other_fingerprint = '368d8604fb8c0g455d129274f5773aa2f31d4f7q'
where(:report_type_1, :location_fingerprint_1, :identifier_1, :report_type_2, :location_fingerprint_2, :identifier_2, :equal, :case_name) do
report_type | fingerprint | -> { identifier } | report_type | fingerprint | -> { identifier } | true | 'when report_type, location_fingerprint and primary identifier are equal'
report_type | fingerprint | -> { identifier } | other_report_type | fingerprint | -> { identifier } | false | 'when report_type is different'
report_type | fingerprint | -> { identifier } | report_type | other_fingerprint | -> { identifier } | false | 'when location_fingerprint is different'
report_type | fingerprint | -> { identifier } | report_type | fingerprint | -> { other_identifier } | false | 'when primary identifier is different'
end
with_them do
let(:occurrence_1) { create(:ci_reports_security_occurrence, report_type: report_type_1, location_fingerprint: location_fingerprint_1, identifiers: [identifier_1.call]) }
let(:occurrence_2) { create(:ci_reports_security_occurrence, report_type: report_type_2, location_fingerprint: location_fingerprint_2, identifiers: [identifier_2.call]) }
it "returns #{params[:equal]}" do
expect(occurrence_1 == occurrence_2).to eq(equal)
end
end
end
end
...@@ -9,7 +9,7 @@ describe Gitlab::Ci::Reports::Security::Report do ...@@ -9,7 +9,7 @@ describe Gitlab::Ci::Reports::Security::Report do
it { expect(report.type).to eq('sast') } it { expect(report.type).to eq('sast') }
describe '#add_scanner' do describe '#add_scanner' do
let(:scanner) { { external_id: 'find_sec_bugs' } } let(:scanner) { create(:ci_reports_security_scanner, external_id: 'find_sec_bugs') }
subject { report.add_scanner(scanner) } subject { report.add_scanner(scanner) }
...@@ -19,29 +19,29 @@ describe Gitlab::Ci::Reports::Security::Report do ...@@ -19,29 +19,29 @@ describe Gitlab::Ci::Reports::Security::Report do
expect(report.scanners).to eq({ 'find_sec_bugs' => scanner }) expect(report.scanners).to eq({ 'find_sec_bugs' => scanner })
end end
it 'returns the map keyap' do it 'returns the added scanner' do
expect(subject).to eq('find_sec_bugs') expect(subject).to eq(scanner)
end end
end end
describe '#add_identifier' do describe '#add_identifier' do
let(:identifier) { { fingerprint: '4e5b6966dd100170b4b1ad599c7058cce91b57b4' } } let(:identifier) { create(:ci_reports_security_identifier) }
subject { report.add_identifier(identifier) } subject { report.add_identifier(identifier) }
it 'stores given identifier params in the map' do it 'stores given identifier params in the map' do
subject subject
expect(report.identifiers).to eq({ '4e5b6966dd100170b4b1ad599c7058cce91b57b4' => identifier }) expect(report.identifiers).to eq({ identifier.fingerprint => identifier })
end end
it 'returns the map keyap' do it 'returns the added identifier' do
expect(subject).to eq('4e5b6966dd100170b4b1ad599c7058cce91b57b4') expect(subject).to eq(identifier)
end end
end end
describe '#add_occurrence' do describe '#add_occurrence' do
let(:occurrence) { { foo: :bar } } let(:occurrence) { create(:ci_reports_security_occurrence) }
it 'enriches given occurrence and stores it in the collection' do it 'enriches given occurrence and stores it in the collection' do
report.add_occurrence(occurrence) report.add_occurrence(occurrence)
......
# frozen_string_literal: true
require 'spec_helper'
describe Gitlab::Ci::Reports::Security::Scanner do
describe '#initialize' do
subject { described_class.new(**params) }
let(:params) do
{
external_id: 'brakeman',
name: 'Brakeman'
}
end
context 'when all params are given' do
it 'initializes an instance' do
expect { subject }.not_to raise_error
expect(subject).to have_attributes(
external_id: 'brakeman',
name: 'Brakeman'
)
end
end
%i[external_id name].each do |attribute|
context "when attribute #{attribute} is missing" do
before do
params.delete(attribute)
end
it 'raises an error' do
expect { subject }.to raise_error(ArgumentError)
end
end
end
end
describe '#key' do
let(:scanner) { create(:ci_reports_security_scanner) }
subject { scanner.key }
it 'returns external_id' do
is_expected.to eq(scanner.external_id)
end
end
describe '#to_hash' do
let(:scanner) { create(:ci_reports_security_scanner) }
subject { scanner.to_hash }
it 'returns expected hash' do
is_expected.to eq({
external_id: scanner.external_id,
name: scanner.name
})
end
end
describe '#==' do
using RSpec::Parameterized::TableSyntax
where(:id_1, :id_2, :equal, :case_name) do
'brakeman' | 'brakeman' | true | 'when external_id is equal'
'brakeman' | 'bandit' | false | 'when external_id is different'
end
with_them do
let(:scanner_1) { create(:ci_reports_security_scanner, external_id: id_1) }
let(:scanner_2) { create(:ci_reports_security_scanner, external_id: id_2) }
it "returns #{params[:equal]}" do
expect(scanner_1 == scanner_2).to eq(equal)
end
end
end
end
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment