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
end
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
def message
......
......@@ -28,7 +28,9 @@ class Vulnerabilities::FindingEntity < Grape::Entity
expose :location
expose :remediations
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
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
after(:build) do |finding, evaluator|
if evaluator.summary
raw_metadata = Gitlab::Json.parse(finding.raw_metadata)
raw_metadata.delete("solution")
raw_metadata["remediations"] = [
raw_metadata.delete('solution')
raw_metadata['remediations'] = [
{
summary: evaluator.summary
}
......@@ -38,25 +38,35 @@ FactoryBot.define do
metadata_version { 'sast:1.0' }
raw_metadata do
{
description: "The cipher does not provide data integrity update 1",
message: "The cipher does not provide data integrity",
cve: "818bf5dacb291e15d9e6dc3c5ac32178:CIPHER",
solution: "GCM mode introduces an HMAC into the resulting encrypted data, providing integrity of the result.",
description: 'The cipher does not provide data integrity update 1',
message: 'The cipher does not provide data integrity',
cve: '818bf5dacb291e15d9e6dc3c5ac32178:CIPHER',
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",
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"
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"
name: 'Cipher does not check for integrity first?',
url: 'https://crypto.stackexchange.com/questions/31428/pbewithmd5anddes-cipher-does-not-check-for-integrity-first'
}
],
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
end
......@@ -97,7 +107,7 @@ FactoryBot.define do
raw_metadata.delete(:solution)
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
......
......@@ -1017,6 +1017,93 @@
"confidence": "medium",
"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.",
"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": [
{
"name": "Cookie No HttpOnly Flag",
......
......@@ -596,22 +596,45 @@ describe Vulnerabilities::Occurrence do
end
describe '#evidence' do
it 'has an evidence summary when present' do
occurrence = create(:vulnerabilities_occurrence)
subject { occurrence.evidence }
expect(occurrence.evidence).to eq(occurrence.metadata['evidence']['summary'])
end
it 'has no evidence summary when evidence is present, summary is not' do
occurrence = create(:vulnerabilities_occurrence, raw_metadata: { evidence: {} })
expect(occurrence.evidence).to be_nil
context 'has an evidence fields' do
let(:occurrence) { create(:vulnerabilities_occurrence) }
let(:evidence) { occurrence.metadata['evidence'] }
it do
is_expected.to match a_hash_including(
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
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
......
......@@ -55,7 +55,7 @@ describe Vulnerabilities::FindingEntity do
expect(subject).to include(:scanner, :project, :identifiers)
expect(subject).to include(:dismissal_feedback, :issue_feedback)
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
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