Commit f4e84c57 authored by Michael Eddington's avatar Michael Eddington Committed by James Lopez

Add new DAST schema fields to backend for API Fuzzing

parent a5d51a14
...@@ -65,7 +65,10 @@ module VulnerabilitiesHelper ...@@ -65,7 +65,10 @@ module VulnerabilitiesHelper
:scanner, :scanner,
:solution, :solution,
:request, :request,
:response :response,
:evidence_source,
:supporting_messages,
:assets
) )
if data[:location]['file'] if data[:location]['file']
......
...@@ -263,19 +263,67 @@ module Vulnerabilities ...@@ -263,19 +263,67 @@ module Vulnerabilities
metadata.dig('remediations') metadata.dig('remediations')
end end
def build_evidence_request(data)
return if data.nil?
{
headers: data.fetch('headers', []).map do |request_header|
{
name: request_header['name'],
value: request_header['value']
}
end,
method: data['method'],
url: data['url'],
body: data['body']
}
end
def build_evidence_response(data)
return if data.nil?
{
headers: data.fetch('headers', []).map do |header_data|
{
name: header_data['name'],
value: header_data['value']
}
end,
status_code: data['status_code'],
reason_phrase: data['reason_phrase'],
body: data['body']
}
end
def build_evidence_supporting_messages(data)
return [] if data.nil?
data.map do |message|
{
name: message['name'],
request: build_evidence_request(message['request']),
response: build_evidence_response(message['response'])
}
end
end
def build_evidence_source(data)
return if data.nil?
{
id: data['id'],
name: data['name'],
url: data['url']
}
end
def evidence def evidence
{ {
summary: metadata.dig('evidence', 'summary'), summary: metadata.dig('evidence', 'summary'),
request: { request: build_evidence_request(metadata.dig('evidence', 'request')),
headers: metadata.dig('evidence', 'request', 'headers') || [], response: build_evidence_response(metadata.dig('evidence', 'response')),
method: metadata.dig('evidence', 'request', 'method'), source: build_evidence_source(metadata.dig('evidence', 'source')),
url: metadata.dig('evidence', 'request', 'url') supporting_messages: build_evidence_supporting_messages(metadata.dig('evidence', 'supporting_messages'))
},
response: {
headers: metadata.dig('evidence', 'response', 'headers') || [],
status_code: metadata.dig('evidence', 'response', 'status_code'),
reason_phrase: metadata.dig('evidence', 'response', 'reason_phrase')
}
} }
end end
...@@ -295,6 +343,16 @@ module Vulnerabilities ...@@ -295,6 +343,16 @@ module Vulnerabilities
identifiers.select(&:other?).map(&:name) identifiers.select(&:other?).map(&:name)
end end
def assets
metadata.fetch('assets', []).map do |asset_data|
{
name: asset_data['name'],
type: asset_data['type'],
url: asset_data['url']
}
end
end
alias_method :==, :eql? # eql? is necessary in some cases like array intersection alias_method :==, :eql? # eql? is necessary in some cases like array intersection
def eql?(other) def eql?(other)
......
...@@ -31,6 +31,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity ...@@ -31,6 +31,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity
expose(:evidence) { |model, _| model.evidence[:summary] } expose(:evidence) { |model, _| model.evidence[:summary] }
expose(:request, using: Vulnerabilities::RequestEntity) { |model, _| model.evidence[:request] } expose(:request, using: Vulnerabilities::RequestEntity) { |model, _| model.evidence[:request] }
expose(:response, using: Vulnerabilities::ResponseEntity) { |model, _| model.evidence[:response] } expose(:response, using: Vulnerabilities::ResponseEntity) { |model, _| model.evidence[:response] }
expose(:evidence_source) { |model, _| model.evidence[:source] }
expose(:supporting_messages) { |model, _| model.evidence[:supporting_messages]}
expose(:assets) { |model, _| model.assets }
end end
expose :state expose :state
......
...@@ -4,4 +4,5 @@ class Vulnerabilities::RequestEntity < Grape::Entity ...@@ -4,4 +4,5 @@ class Vulnerabilities::RequestEntity < Grape::Entity
expose :headers expose :headers
expose :method expose :method
expose :url expose :url
expose :body
end end
...@@ -4,4 +4,5 @@ class Vulnerabilities::ResponseEntity < Grape::Entity ...@@ -4,4 +4,5 @@ class Vulnerabilities::ResponseEntity < Grape::Entity
expose :headers expose :headers
expose :reason_phrase expose :reason_phrase
expose :status_code expose :status_code
expose :body
end end
---
title: Add new security report schema fields to backend for API Fuzzing
merge_request: 44800
author:
type: changed
...@@ -56,18 +56,58 @@ FactoryBot.define do ...@@ -56,18 +56,58 @@ FactoryBot.define do
url: 'https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first' url: 'https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first'
} }
], ],
assets: [
{
type: "postman",
name: "Test Postman Collection",
url: "http://localhost/test.collection"
}
],
evidence: { evidence: {
summary: 'Credit card detected', summary: 'Credit card detected',
request: { request: {
headers: [{ name: 'Accept', value: '*/*' }], headers: [{ name: 'Accept', value: '*/*' }],
method: 'GET', method: 'GET',
url: 'http://goat:8080/WebGoat/logout' url: 'http://goat:8080/WebGoat/logout',
body: nil
}, },
response: { response: {
headers: [{ name: 'Content-Length', value: '0' }], headers: [{ name: 'Content-Length', value: '0' }],
reason_phrase: 'OK', reason_phrase: 'OK',
status_code: 200 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: ''
}
}
]
} }
}.to_json }.to_json
end end
......
...@@ -161,7 +161,10 @@ RSpec.describe VulnerabilitiesHelper do ...@@ -161,7 +161,10 @@ RSpec.describe VulnerabilitiesHelper do
evidence: kind_of(String), evidence: kind_of(String),
scanner: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder), scanner: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
request: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder), request: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
response: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder) response: kind_of(Grape::Entity::Exposure::NestingExposure::OutputBuilder),
evidence_source: anything,
assets: kind_of(Array),
supporting_messages: kind_of(Array)
) )
expect(subject[:location]['blob_path']).to match(kind_of(String)) expect(subject[:location]['blob_path']).to match(kind_of(String))
......
...@@ -628,15 +628,75 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -628,15 +628,75 @@ RSpec.describe Vulnerabilities::Finding do
is_expected.to match a_hash_including( is_expected.to match a_hash_including(
summary: evidence['summary'], summary: evidence['summary'],
request: { request: {
headers: evidence['request']['headers'], headers: [
{
name: evidence['request']['headers'][0]['name'],
value: evidence['request']['headers'][0]['value']
}
],
url: evidence['request']['url'], url: evidence['request']['url'],
method: evidence['request']['method'] method: evidence['request']['method'],
body: evidence['request']['body']
}, },
response: { response: {
headers: evidence['response']['headers'], headers: [
{
name: evidence['response']['headers'][0]['name'],
value: evidence['response']['headers'][0]['value']
}
],
reason_phrase: evidence['response']['reason_phrase'], reason_phrase: evidence['response']['reason_phrase'],
status_code: evidence['response']['status_code'] status_code: evidence['response']['status_code'],
}) body: evidence['request']['body']
},
source: {
id: evidence.dig('source', 'id'),
name: evidence.dig('source', 'name'),
url: evidence.dig('source', 'url')
},
supporting_messages: [
{
name: evidence.dig('supporting_messages')[0].dig('name'),
request: {
headers: [
{
name: evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[0].dig('request', 'headers')[0].dig('value')
}
],
url: evidence.dig('supporting_messages')[0].dig('request', 'url'),
method: evidence.dig('supporting_messages')[0].dig('request', 'method'),
body: evidence.dig('supporting_messages')[0].dig('request', 'body')
},
response: evidence.dig('supporting_messages')[0].dig('response')
},
{
name: evidence.dig('supporting_messages')[1].dig('name'),
request: {
headers: [
{
name: evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[1].dig('request', 'headers')[0].dig('value')
}
],
url: evidence.dig('supporting_messages')[1].dig('request', 'url'),
method: evidence.dig('supporting_messages')[1].dig('request', 'method'),
body: evidence.dig('supporting_messages')[1].dig('request', 'body')
},
response: {
headers: [
{
name: evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('name'),
value: evidence.dig('supporting_messages')[1].dig('response', 'headers')[0].dig('value')
}
],
reason_phrase: evidence.dig('supporting_messages')[1].dig('response', 'reason_phrase'),
status_code: evidence.dig('supporting_messages')[1].dig('response', 'status_code'),
body: evidence.dig('supporting_messages')[1].dig('response', 'body')
}
}
]
)
end end
end end
...@@ -646,16 +706,10 @@ RSpec.describe Vulnerabilities::Finding do ...@@ -646,16 +706,10 @@ RSpec.describe Vulnerabilities::Finding do
it do it do
is_expected.to match a_hash_including( is_expected.to match a_hash_including(
summary: nil, summary: nil,
request: { source: nil,
headers: [], supporting_messages: [],
url: nil, request: nil,
method: nil response: nil)
},
response: {
headers: [],
reason_phrase: nil,
status_code: nil
})
end end
end end
end end
......
...@@ -58,6 +58,7 @@ RSpec.describe Vulnerabilities::FindingEntity do ...@@ -58,6 +58,7 @@ RSpec.describe Vulnerabilities::FindingEntity do
expect(subject).to include(:description, :links, :location, :remediations, :solution, :evidence) expect(subject).to include(:description, :links, :location, :remediations, :solution, :evidence)
expect(subject).to include(:blob_path, :request, :response) expect(subject).to include(:blob_path, :request, :response)
expect(subject).to include(:scan) expect(subject).to include(:scan)
expect(subject).to include(:assets, :evidence_source, :supporting_messages)
end end
context 'when not allowed to admin vulnerability feedback' do context 'when not allowed to admin vulnerability feedback' do
......
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