Commit 9c93f8ad authored by Cameron Swords's avatar Cameron Swords Committed by Heinrich Lee Yu

Expose request and response evidence

Expose evidence on occurrence
Add example to DAST report
parent 503343b8
...@@ -255,7 +255,19 @@ module Vulnerabilities ...@@ -255,7 +255,19 @@ module Vulnerabilities
end end
def evidence def evidence
metadata.dig('evidence', 'summary') {
summary: metadata.dig('evidence', 'summary'),
request: {
headers: metadata.dig('evidence', 'request', 'headers') || [],
method: metadata.dig('evidence', 'request', 'method'),
url: metadata.dig('evidence', 'request', 'url')
},
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
def message def message
......
...@@ -28,7 +28,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity ...@@ -28,7 +28,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity
expose :location expose :location
expose :remediations expose :remediations
expose :solution expose :solution
expose :evidence expose(:evidence) { |model, _| model.evidence[:summary] }
expose(:request, using: Vulnerabilities::RequestEntity) { |model, _| model.evidence[:request] }
expose(:response, using: Vulnerabilities::ResponseEntity) { |model, _| model.evidence[:response] }
end end
expose :state expose :state
......
# frozen_string_literal: true
class Vulnerabilities::RequestEntity < Grape::Entity
expose :headers
expose :method
expose :url
end
# frozen_string_literal: true
class Vulnerabilities::ResponseEntity < Grape::Entity
expose :headers
expose :reason_phrase
expose :status_code
end
...@@ -13,8 +13,8 @@ FactoryBot.define do ...@@ -13,8 +13,8 @@ FactoryBot.define do
after(:build) do |finding, evaluator| after(:build) do |finding, evaluator|
if evaluator.summary if evaluator.summary
raw_metadata = Gitlab::Json.parse(finding.raw_metadata) raw_metadata = Gitlab::Json.parse(finding.raw_metadata)
raw_metadata.delete("solution") raw_metadata.delete('solution')
raw_metadata["remediations"] = [ raw_metadata['remediations'] = [
{ {
summary: evaluator.summary summary: evaluator.summary
} }
...@@ -38,25 +38,35 @@ FactoryBot.define do ...@@ -38,25 +38,35 @@ FactoryBot.define do
metadata_version { 'sast:1.0' } metadata_version { 'sast:1.0' }
raw_metadata do raw_metadata do
{ {
description: "The cipher does not provide data integrity update 1", description: 'The cipher does not provide data integrity update 1',
message: "The cipher does not provide data integrity", message: 'The cipher does not provide data integrity',
cve: "818bf5dacb291e15d9e6dc3c5ac32178:CIPHER", cve: '818bf5dacb291e15d9e6dc3c5ac32178:CIPHER',
solution: "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.", solution: 'GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.',
location: { location: {
file: "maven/src/main/java/com/gitlab/security_products/tests/App.java", file: 'maven/src/main/java/com/gitlab/security_products/tests/App.java',
start_line: 29, start_line: 29,
end_line: 29, end_line: 29,
class: "com.gitlab.security_products.tests.App", class: 'com.gitlab.security_products.tests.App',
method: "insecureCypher" method: 'insecureCypher'
}, },
links: [ links: [
{ {
name: "Cipher does not check for integrity first?", name: 'Cipher does not check for integrity first?',
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'
} }
], ],
evidence: { evidence: {
summary: 'Credit card detected' summary: 'Credit card detected',
request: {
headers: [{ name: 'Accept', value: '*/*' }],
method: 'GET',
url: 'http://goat:8080/WebGoat/logout'
},
response: {
headers: [{ name: 'Content-Length', value: '0' }],
reason_phrase: 'OK',
status_code: 200
}
} }
}.to_json }.to_json
end end
...@@ -97,7 +107,7 @@ FactoryBot.define do ...@@ -97,7 +107,7 @@ FactoryBot.define do
raw_metadata.delete(:solution) raw_metadata.delete(:solution)
raw_metadata[:remediations] = [ raw_metadata[:remediations] = [
{ {
summary: "Use GCM mode which includes HMAC in the resulting encrypted data, providing integrity of the result." summary: 'Use GCM mode which includes HMAC in the resulting encrypted data, providing integrity of the result.'
} }
] ]
finding.raw_metadata = raw_metadata.to_json finding.raw_metadata = raw_metadata.to_json
......
...@@ -1017,6 +1017,93 @@ ...@@ -1017,6 +1017,93 @@
"confidence": "medium", "confidence": "medium",
"cve": "10010", "cve": "10010",
"description": "A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible.", "description": "A cookie has been set without the HttpOnly flag, which means that the cookie can be accessed by JavaScript. If a malicious script can be run on this page then the cookie will be accessible and can be transmitted to another site. If this is a session cookie then session hijacking may be possible.",
"evidence": {
"request": {
"headers": [
{
"name": "Accept",
"value": "text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8"
},
{
"name": "Accept-Language",
"value": "en-US,en;q=0.5"
},
{
"name": "Connection",
"value": "keep-alive"
},
{
"name": "Cookie",
"value": "JSESSIONID=BB38BE6D77E83FA7522D1429AA5F2EAF"
},
{
"name": "Host",
"value": "goat:8080"
},
{
"name": "Referer",
"value": "http://goat:8080/WebGoat/logout"
},
{
"name": "Upgrade-Insecure-Requests",
"value": "1"
},
{
"name": "User-Agent",
"value": "Mozilla/5.0 (X11; Linux x86_64; rv:59.0) Gecko/20100101 Firefox/59.0"
}
],
"method": "GET",
"url": "http://goat:8080/WebGoat/logout"
},
"response": {
"headers": [
{
"name": "Connection",
"value": "keep-alive"
},
{
"name": "Content-Length",
"value": "0"
},
{
"name": "Date",
"value": "Thu, 07 May 2020 07:18:06 GMT"
},
{
"name": "Gitlab-DAST-Permission",
"value": "allow"
},
{
"name": "Location",
"value": "http://goat:8080/WebGoat/login"
},
{
"name": "Server",
"value": "nginx/1.17.6"
},
{
"name": "Set-Cookie",
"value": "JSESSIONID=1624E8274D30BB5A029F5A18BB7E4400; Path=/WebGoat"
},
{
"name": "X-Content-Type-Options",
"value": "nosniff"
},
{
"name": "X-Frame-Options",
"value": "DENY"
},
{
"name": "X-XSS-Protection",
"value": "1; mode=block"
}
],
"reason_phrase": "Found",
"status_code": 302
},
"summary": "Set-Cookie: JSESSIONID"
},
"identifiers": [ "identifiers": [
{ {
"name": "Cookie No HttpOnly Flag", "name": "Cookie No HttpOnly Flag",
......
...@@ -596,22 +596,45 @@ describe Vulnerabilities::Occurrence do ...@@ -596,22 +596,45 @@ describe Vulnerabilities::Occurrence do
end end
describe '#evidence' do describe '#evidence' do
it 'has an evidence summary when present' do subject { occurrence.evidence }
occurrence = create(:vulnerabilities_occurrence)
expect(occurrence.evidence).to eq(occurrence.metadata['evidence']['summary']) context 'has an evidence fields' do
end let(:occurrence) { create(:vulnerabilities_occurrence) }
let(:evidence) { occurrence.metadata['evidence'] }
it 'has no evidence summary when evidence is present, summary is not' do
occurrence = create(:vulnerabilities_occurrence, raw_metadata: { evidence: {} }) it do
is_expected.to match a_hash_including(
expect(occurrence.evidence).to be_nil summary: evidence['summary'],
request: {
headers: evidence['request']['headers'],
url: evidence['request']['url'],
method: evidence['request']['method']
},
response: {
headers: evidence['response']['headers'],
reason_phrase: evidence['response']['reason_phrase'],
status_code: evidence['response']['status_code']
})
end
end
context 'has no evidence summary when evidence is present, summary is not' do
let(:occurrence) { create(:vulnerabilities_occurrence, raw_metadata: { evidence: {} }) }
it do
is_expected.to match a_hash_including(
summary: nil,
request: {
headers: [],
url: nil,
method: nil
},
response: {
headers: [],
reason_phrase: nil,
status_code: nil
})
end end
it 'has no evidence summary when evidence is not present' do
occurrence = create(:vulnerabilities_occurrence, raw_metadata: {})
expect(occurrence.evidence).to be_nil
end end
end end
......
...@@ -55,7 +55,7 @@ describe Vulnerabilities::FindingEntity do ...@@ -55,7 +55,7 @@ describe Vulnerabilities::FindingEntity do
expect(subject).to include(:scanner, :project, :identifiers) expect(subject).to include(:scanner, :project, :identifiers)
expect(subject).to include(:dismissal_feedback, :issue_feedback) expect(subject).to include(:dismissal_feedback, :issue_feedback)
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) expect(subject).to include(:blob_path, :request, :response)
end end
context 'when not allowed to admin vulnerability feedback' do context 'when not allowed to admin vulnerability feedback' do
......
# frozen_string_literal: true
require 'spec_helper'
describe Vulnerabilities::RequestEntity do
let(:request) { create(:vulnerabilities_occurrence).evidence[:request] }
let(:entity) do
described_class.represent(request)
end
describe '#as_json' do
subject { entity.as_json }
it 'contains required fields' do
expect(subject).to include(:headers, :method, :url)
end
end
end
# frozen_string_literal: true
require 'spec_helper'
describe Vulnerabilities::ResponseEntity do
let(:response) { create(:vulnerabilities_occurrence).evidence[:response] }
let(:entity) do
described_class.represent(response)
end
describe '#as_json' do
subject { entity.as_json }
it 'contains required fields' do
expect(subject).to include(:headers, :reason_phrase, :status_code)
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