Commit f1e18d56 authored by Mikołaj Wawrzyniak's avatar Mikołaj Wawrzyniak

Merge branch '343280-evidence-metadata' into 'master'

Use finding_evidence over raw_metadata

See merge request gitlab-org/gitlab!75140
parents 57aa5386 adcf3d51
...@@ -39,7 +39,7 @@ module Vulnerabilities ...@@ -39,7 +39,7 @@ module Vulnerabilities
has_many :vulnerability_flags, class_name: 'Vulnerabilities::Flag', inverse_of: :finding, foreign_key: 'vulnerability_occurrence_id' has_many :vulnerability_flags, class_name: 'Vulnerabilities::Flag', inverse_of: :finding, foreign_key: 'vulnerability_occurrence_id'
has_one :evidence, class_name: 'Vulnerabilities::Finding::Evidence', inverse_of: :finding, foreign_key: 'vulnerability_occurrence_id' has_one :finding_evidence, class_name: 'Vulnerabilities::Finding::Evidence', inverse_of: :finding, foreign_key: 'vulnerability_occurrence_id'
serialize :config_options, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize serialize :config_options, Serializers::Json # rubocop:disable Cop/ActiveRecordSerialize
...@@ -285,12 +285,14 @@ module Vulnerabilities ...@@ -285,12 +285,14 @@ module Vulnerabilities
end end
def evidence def evidence
evidence_data = finding_evidence.present? ? finding_evidence.data : metadata.dig('evidence')
{ {
summary: metadata.dig('evidence', 'summary'), summary: evidence_data&.dig('summary'),
request: build_evidence_request(metadata.dig('evidence', 'request')), request: build_evidence_request(evidence_data&.dig('request')),
response: build_evidence_response(metadata.dig('evidence', 'response')), response: build_evidence_response(evidence_data&.dig('response')),
source: build_evidence_source(metadata.dig('evidence', 'source')), source: build_evidence_source(evidence_data&.dig('source')),
supporting_messages: build_evidence_supporting_messages(metadata.dig('evidence', 'supporting_messages')) supporting_messages: build_evidence_supporting_messages(evidence_data&.dig('supporting_messages'))
} }
end end
......
...@@ -7,7 +7,7 @@ module Vulnerabilities ...@@ -7,7 +7,7 @@ module Vulnerabilities
belongs_to :finding, belongs_to :finding,
class_name: 'Vulnerabilities::Finding', class_name: 'Vulnerabilities::Finding',
inverse_of: :evidence, inverse_of: :finding_evidence,
foreign_key: 'vulnerability_occurrence_id', foreign_key: 'vulnerability_occurrence_id',
optional: false optional: false
......
...@@ -2,6 +2,55 @@ ...@@ -2,6 +2,55 @@
FactoryBot.define do FactoryBot.define do
factory :vulnerabilties_finding_evidence, class: 'Vulnerabilities::Finding::Evidence' do factory :vulnerabilties_finding_evidence, class: 'Vulnerabilities::Finding::Evidence' do
finding { association :vulnerabilities_finding }
summary { 'Evidence summary' } summary { 'Evidence summary' }
data do
{
summary: 'Credit card detected',
request: {
headers: [{ name: 'Accept', value: '*/*' }],
method: 'GET',
url: 'http://goat:8080/WebGoat/logout',
body: nil
},
response: {
headers: [{ name: 'Content-Length', value: '0' }],
reason_phrase: 'OK',
status_code: 200,
body: nil
},
source: {
id: 'assert:Response Body Analysis',
name: 'Response Body Analysis',
url: 'htpp://hostname/documentation'
},
supporting_messages: [
{
name: 'Origional',
request: {
headers: [{ name: 'Accept', value: '*/*' }],
method: 'GET',
url: 'http://goat:8080/WebGoat/logout',
body: ''
}
},
{
name: 'Recorded',
request: {
headers: [{ name: 'Accept', value: '*/*' }],
method: 'GET',
url: 'http://goat:8080/WebGoat/logout',
body: ''
},
response: {
headers: [{ name: 'Content-Length', value: '0' }],
reason_phrase: 'OK',
status_code: 200,
body: ''
}
}
]
}
end
end end
end end
...@@ -25,7 +25,7 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -25,7 +25,7 @@ RSpec.describe Vulnerabilities::Finding do
it { is_expected.to have_many(:finding_remediations).class_name('Vulnerabilities::FindingRemediation').with_foreign_key('vulnerability_occurrence_id') } it { is_expected.to have_many(:finding_remediations).class_name('Vulnerabilities::FindingRemediation').with_foreign_key('vulnerability_occurrence_id') }
it { is_expected.to have_many(:vulnerability_flags).class_name('Vulnerabilities::Flag').with_foreign_key('vulnerability_occurrence_id') } it { is_expected.to have_many(:vulnerability_flags).class_name('Vulnerabilities::Flag').with_foreign_key('vulnerability_occurrence_id') }
it { is_expected.to have_many(:remediations).through(:finding_remediations) } it { is_expected.to have_many(:remediations).through(:finding_remediations) }
it { is_expected.to have_one(:evidence).class_name('Vulnerabilities::Finding::Evidence').with_foreign_key('vulnerability_occurrence_id') } it { is_expected.to have_one(:finding_evidence).class_name('Vulnerabilities::Finding::Evidence').with_foreign_key('vulnerability_occurrence_id') }
end end
describe 'validations' do describe 'validations' do
...@@ -775,79 +775,87 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -775,79 +775,87 @@ RSpec.describe Vulnerabilities::Finding do
describe '#evidence' do describe '#evidence' do
subject { finding.evidence } subject { finding.evidence }
context 'has an evidence fields' do shared_examples 'evidence schema' do
let(:finding) { create(:vulnerabilities_finding) } it 'matches evidence schema' do
let(:evidence) { finding.metadata['evidence'] } example_evidence = evidence.with_indifferent_access
is_expected.to match a_hash_including(
summary: example_evidence['summary']
)
it do
is_expected.to match a_hash_including( is_expected.to match a_hash_including(
summary: evidence['summary'],
request: { request: {
headers: [ headers: [
{ {
name: evidence['request']['headers'][0]['name'], name: example_evidence['request']['headers'][0]['name'],
value: evidence['request']['headers'][0]['value'] value: example_evidence['request']['headers'][0]['value']
} }
], ],
url: evidence['request']['url'], url: example_evidence['request']['url'],
method: evidence['request']['method'], method: example_evidence['request']['method'],
body: evidence['request']['body'] body: example_evidence['request']['body']
}, }
)
is_expected.to match a_hash_including(
response: { response: {
headers: [ headers: [
{ {
name: evidence['response']['headers'][0]['name'], name: example_evidence['response']['headers'][0]['name'],
value: evidence['response']['headers'][0]['value'] value: example_evidence['response']['headers'][0]['value']
} }
], ],
reason_phrase: evidence['response']['reason_phrase'], reason_phrase: example_evidence['response']['reason_phrase'],
status_code: evidence['response']['status_code'], status_code: example_evidence['response']['status_code'],
body: evidence['request']['body'] body: example_evidence['request']['body']
}, },
source: { source: {
id: evidence.dig('source', 'id'), id: example_evidence.dig('source', 'id'),
name: evidence.dig('source', 'name'), name: example_evidence.dig('source', 'name'),
url: evidence.dig('source', 'url') url: example_evidence.dig('source', 'url')
}, }
)
is_expected.to match a_hash_including(
supporting_messages: [ supporting_messages: [
{ {
name: evidence.dig('supporting_messages')[0].dig('name'), name: example_evidence.dig('supporting_messages')[0].dig('name'),
request: { request: {
headers: [ headers: [
{ {
name: evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('name'), name: example_evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('value') value: example_evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('value')
} }
], ],
url: evidence.dig('supporting_messages')[0].dig('request', 'url'), url: example_evidence.dig('supporting_messages')[0].dig('request', 'url'),
method: evidence.dig('supporting_messages')[0].dig('request', 'method'), method: example_evidence.dig('supporting_messages')[0].dig('request', 'method'),
body: evidence.dig('supporting_messages')[0].dig('request', 'body') body: example_evidence.dig('supporting_messages')[0].dig('request', 'body')
}, },
response: evidence.dig('supporting_messages')[0].dig('response') response: example_evidence.dig('supporting_messages')[0].dig('response')
}, },
{ {
name: evidence.dig('supporting_messages')[1].dig('name'), name: example_evidence.dig('supporting_messages')[1].dig('name'),
request: { request: {
headers: [ headers: [
{ {
name: evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('name'), name: example_evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('value') value: example_evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('value')
} }
], ],
url: evidence.dig('supporting_messages')[1].dig('request', 'url'), url: example_evidence.dig('supporting_messages')[1].dig('request', 'url'),
method: evidence.dig('supporting_messages')[1].dig('request', 'method'), method: example_evidence.dig('supporting_messages')[1].dig('request', 'method'),
body: evidence.dig('supporting_messages')[1].dig('request', 'body') body: example_evidence.dig('supporting_messages')[1].dig('request', 'body')
}, },
response: { response: {
headers: [ headers: [
{ {
name: evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('name'), name: example_evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('value') value: example_evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('value')
} }
], ],
reason_phrase: evidence.dig('supporting_messages')[1].dig('response', 'reason_phrase'), reason_phrase: example_evidence.dig('supporting_messages')[1].dig('response', 'reason_phrase'),
status_code: evidence.dig('supporting_messages')[1].dig('response', 'status_code'), status_code: example_evidence.dig('supporting_messages')[1].dig('response', 'status_code'),
body: evidence.dig('supporting_messages')[1].dig('response', 'body') body: example_evidence.dig('supporting_messages')[1].dig('response', 'body')
} }
} }
] ]
...@@ -855,17 +863,34 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -855,17 +863,34 @@ RSpec.describe Vulnerabilities::Finding do
end end
end end
context 'has no evidence summary when evidence is present, summary is not' do context 'without finding_evidence' do
let(:finding) { create(:vulnerabilities_finding, raw_metadata: { evidence: {} }) } context 'has an evidence fields' do
let(:finding) { create(:vulnerabilities_finding) }
let(:evidence) { finding.metadata['evidence'] }
it do include_examples 'evidence schema'
is_expected.to match a_hash_including(
summary: nil,
source: nil,
supporting_messages: [],
request: nil,
response: nil)
end end
context 'has no evidence summary when evidence is present, summary is not' do
let(:finding) { create(:vulnerabilities_finding, raw_metadata: { evidence: {} }) }
it do
is_expected.to match a_hash_including(
summary: nil,
source: nil,
supporting_messages: [],
request: nil,
response: nil)
end
end
end
context 'with finding_evidence' do
let(:finding_evidence) { build(:vulnerabilties_finding_evidence) }
let(:finding) { finding_evidence.finding }
let(:evidence) { finding_evidence.data }
include_examples 'evidence schema'
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