Commit ce4e4d0f authored by Brian Williams's avatar Brian Williams

Make starboard_vulnerability API consistent with security reports

The current implemetation of the starboard_vulnerability API uses data
structures with some differences from the vulnerability report
schema. This change makes the two implementations more consistent.

- `location` now has the same required fields
- `vendor` has been changed to `vendor.name`
parent 6bed0cb4
......@@ -30,18 +30,26 @@ module Vulnerabilities
GENERIC_REPORT_TYPE
end
def initialize_vulnerability(vulnerability_hash)
attributes = vulnerability_hash
def sanitize_enums(vulnerability_hash)
vulnerability_hash
.slice(*%i[
description
state
severity
confidence
])
.transform_values(&:downcase)
end
def initialize_vulnerability(vulnerability_hash)
attributes = vulnerability_hash
.slice(*%i[
detected_at
confirmed_at
resolved_at
dismissed_at
])
.merge(sanitize_enums(vulnerability_hash))
.merge(
project: @project,
author: @author,
......@@ -97,6 +105,7 @@ module Vulnerabilities
Vulnerabilities::Scanner.find_or_initialize_by(name: name) do |s|
s.project = @project
s.external_id = scanner_hash[:id]
s.vendor = scanner_hash.dig(:vendor, :name)
end
end
# rubocop: enable CodeReuse/ActiveRecord
......
......@@ -18,9 +18,7 @@ module Vulnerabilities
raise Gitlab::Access::AccessDeniedError unless authorized?
vulnerability_hash = @params[:vulnerability]
vulnerability_hash[:state] = :detected
vulnerability = initialize_vulnerability(vulnerability_hash)
vulnerability.title = vulnerability_hash[:name]&.truncate(::Issuable::TITLE_LENGTH_MAX)
identifiers = initialize_identifiers(@params.dig(:vulnerability, :identifiers))
scanner = initialize_scanner(@params[:scanner])
finding = initialize_finding(
......@@ -71,5 +69,11 @@ module Vulnerabilities
Digest::SHA1.hexdigest(fingerprint_data)
end
def initialize_vulnerability(vulnerability_hash)
vulnerability_hash[:state] = :detected
vulnerability_hash[:title] = vulnerability_hash[:name]
super(vulnerability_hash)
end
end
end
......@@ -64,7 +64,29 @@ module EE
requires :name, type: String
requires :severity, type: String
requires :confidence, type: String
requires :location, type: Hash
requires :location, type: Hash do
requires :image, type: String
requires :dependency, type: Hash do
requires :package, type: Hash do
requires :name, type: String
end
optional :version, type: String
end
requires :kubernetes_resource, type: Hash do
requires :namespace, type: String
requires :name, type: String
requires :kind, type: String
requires :container_name, type: String
requires :agent_id, type: String
end
optional :operating_system, type: String
end
requires :identifiers, type: Array do
requires :type, type: String
requires :name, type: String
......@@ -77,11 +99,13 @@ module EE
optional :solution, type: String
optional :links, type: Array
end
requires :scanner, type: Hash, desc: 'Scanner details matching the `.scan.scanner` field on the security report schema' do
requires :id, type: String
requires :name, type: String
optional :vendor, type: String
requires :vendor, type: Hash do
requires :name, type: String
end
end
end
......
......@@ -235,11 +235,19 @@ RSpec.describe API::Internal::Kubernetes do
severity: 'high',
confidence: 'unknown',
location: {
image: 'index.docker.io/library/nginx:latest',
kubernetes_resource: {
namespace: 'production',
kind: 'deployment',
name: 'nginx',
container: 'nginx'
name: 'nginx-ingress',
container_name: 'nginx',
agent_id: '1'
},
dependency: {
package: {
name: 'libc'
},
version: 'v1.2.3'
}
},
identifiers: [
......@@ -253,7 +261,9 @@ RSpec.describe API::Internal::Kubernetes do
scanner: {
id: 'starboard_trivy',
name: 'Trivy (via Starboard Operator)',
vendor: 'GitLab'
vendor: {
name: 'GitLab'
}
}
}
end
......@@ -276,6 +286,18 @@ RSpec.describe API::Internal::Kubernetes do
end
end
context 'when required parameters are missing' do
where(:missing_param) { %i[vulnerability scanner] }
with_them do
it 'returns bad request' do
send_request(params: payload.delete(missing_param))
expect(response).to have_gitlab_http_status(:bad_request)
end
end
end
context 'when feature is not available' do
before do
stub_licensed_features(security_dashboard: false)
......
......@@ -252,6 +252,27 @@ RSpec.describe Vulnerabilities::ManuallyCreateService do
expect(subject.message).to match(/confirmed_at can only be set/)
end
end
context 'with capitalized enum fields' do
let(:params) do
{
vulnerability: {
name: "Test vulnerability",
state: "Detected",
severity: "Unknown",
confidence: "Unknown",
identifiers: [identifier_attributes],
scanner: scanner_attributes
}
}
end
it 'does not raise an exception' do
expect { subject }.not_to raise_error
expect(subject.success?).to be_truthy
end
end
end
context 'with invalid parameters' do
......
......@@ -6,14 +6,16 @@ RSpec.describe Vulnerabilities::StarboardVulnerabilityCreateService do
let(:agent) { create(:cluster_agent) }
let(:project) { agent.project }
let(:user) { agent.created_by_user }
let(:severity) { 'high' }
let(:confidence) { 'unknown' }
let(:params) do
{
vulnerability: {
name: 'CVE-123-4567 in libc',
message: 'Vulnerability message',
description: 'Vulnerability description',
severity: 'high',
confidence: 'unknown',
severity: severity,
confidence: confidence,
location: {
kubernetes_resource: {
namespace: 'production',
......@@ -33,7 +35,9 @@ RSpec.describe Vulnerabilities::StarboardVulnerabilityCreateService do
scanner: {
id: 'starboard_trivy',
name: 'Trivy (via Starboard Operator)',
vendor: 'GitLab'
vendor: {
name: 'GitLab'
}
}
}
end
......@@ -69,6 +73,17 @@ RSpec.describe Vulnerabilities::StarboardVulnerabilityCreateService do
scanner = finding.scanner
expect(scanner.external_id).to eq(params.dig(:scanner, :id))
expect(scanner.name).to eq(params.dig(:scanner, :name))
expect(scanner.vendor).to eq(params.dig(:scanner, :vendor, :name))
end
context 'with capitalized enums' do
let(:severity) { 'High' }
let(:confidence) { 'Unknown' }
it 'does not raise exception' do
expect { subject }.not_to raise_error
expect(subject.success?).to be_truthy
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