Commit 695389c6 authored by Cameron Swords's avatar Cameron Swords Committed by Sean McGivern

Parse analyzer information from Secure reports

parent b9bd0a86
......@@ -25,6 +25,8 @@ module Gitlab
create_scanner
create_scan
create_analyzer
set_report_version
collate_remediations.each { |vulnerability| create_vulnerability(vulnerability) }
report_data
......@@ -67,6 +69,10 @@ module Gitlab
@scan_data ||= report_data.dig('scan')
end
def analyzer_data
@analyzer_data ||= report_data.dig('scan', 'analyzer')
end
# map remediations to relevant vulnerabilities
def collate_remediations
return report_data["vulnerabilities"] || [] unless report_data["remediations"]
......@@ -166,6 +172,25 @@ module Gitlab
report.scan = ::Gitlab::Ci::Reports::Security::Scan.new(scan_data)
end
def set_report_version
report.version = report_version
end
def create_analyzer
return unless analyzer_data.is_a?(Hash)
params = {
id: analyzer_data.dig('id'),
name: analyzer_data.dig('name'),
version: analyzer_data.dig('version'),
vendor: analyzer_data.dig('vendor', 'name')
}
return unless params.values.all?
report.analyzer = ::Gitlab::Ci::Reports::Security::Analyzer.new(**params)
end
def create_scanner(scanner_data = top_level_scanner)
return unless scanner_data.is_a?(Hash)
......@@ -173,7 +198,8 @@ module Gitlab
::Gitlab::Ci::Reports::Security::Scanner.new(
external_id: scanner_data['id'],
name: scanner_data['name'],
vendor: scanner_data.dig('vendor', 'name')))
vendor: scanner_data.dig('vendor', 'name'),
version: scanner_data.dig('version')))
end
def create_identifiers(identifiers)
......
# frozen_string_literal: true
module Gitlab
module Ci
module Reports
module Security
class Analyzer
attr_reader :id, :name, :version, :vendor
def initialize(id:, name:, version:, vendor:)
@id = id
@name = name
@version = version
@vendor = vendor
end
end
end
end
end
end
......@@ -6,7 +6,7 @@ module Gitlab
module Security
class Report
attr_reader :created_at, :type, :pipeline, :findings, :scanners, :identifiers
attr_accessor :scan, :scanned_resources, :errors
attr_accessor :scan, :scanned_resources, :errors, :analyzer, :version
delegate :project_id, to: :pipeline
......
......@@ -15,14 +15,15 @@ module Gitlab
"semgrep" => 2
}.freeze
attr_accessor :external_id, :name, :vendor
attr_accessor :external_id, :name, :vendor, :version
alias_method :key, :external_id
def initialize(external_id:, name:, vendor:)
def initialize(external_id:, name:, vendor:, version:)
@external_id = external_id
@name = name
@vendor = vendor
@version = version
end
def to_hash
......
......@@ -5,6 +5,7 @@ FactoryBot.define do
external_id { 'find_sec_bugs' }
name { 'Find Security Bugs' }
vendor { 'Security Scanner Vendor' }
version { '1.0.0' }
skip_create
......
......@@ -133,6 +133,15 @@
],
"dependency_files": [],
"scan": {
"analyzer": {
"id": "common-analyzer",
"name": "Common Analyzer",
"url": "https://site.com/analyzer/common",
"version": "2.0.1",
"vendor": {
"name": "Common"
}
},
"scanner": {
"id": "gemnasium",
"name": "Gemnasium",
......@@ -146,5 +155,6 @@
"start_time": "placeholder-value",
"end_time": "placeholder-value",
"status": "success"
}
},
"version": "14.0.2"
}
......@@ -195,6 +195,22 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
end
end
describe 'top-level scanner' do
it 'is the primary scanner' do
expect(report.primary_scanner.external_id).to eq('gemnasium')
expect(report.primary_scanner.name).to eq('Gemnasium')
expect(report.primary_scanner.vendor).to eq('GitLab')
expect(report.primary_scanner.version).to eq('2.18.0')
end
it 'returns nil report has no scanner' do
empty_report = Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, 2.weeks.ago)
described_class.parse!({}.to_json, empty_report)
expect(empty_report.primary_scanner).to be_nil
end
end
describe 'parsing scanners' do
subject(:scanner) { report.findings.first.scanner }
......@@ -225,6 +241,35 @@ RSpec.describe Gitlab::Ci::Parsers::Security::Common do
end
end
describe 'parsing schema version' do
it 'parses the version' do
expect(report.version).to eq('14.0.2')
end
it 'returns nil when there is no version' do
empty_report = Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, 2.weeks.ago)
described_class.parse!({}.to_json, empty_report)
expect(empty_report.version).to be_nil
end
end
describe 'parsing analyzer' do
it 'associates analyzer with report' do
expect(report.analyzer.id).to eq('common-analyzer')
expect(report.analyzer.name).to eq('Common Analyzer')
expect(report.analyzer.version).to eq('2.0.1')
expect(report.analyzer.vendor).to eq('Common')
end
it 'returns nil when analyzer data is not available' do
empty_report = Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, 2.weeks.ago)
described_class.parse!({}.to_json, empty_report)
expect(empty_report.analyzer).to be_nil
end
end
describe 'parsing links' do
it 'returns links object for each finding', :aggregate_failures do
links = report.findings.flat_map(&:links)
......
......@@ -10,7 +10,8 @@ RSpec.describe Gitlab::Ci::Reports::Security::Scanner do
{
external_id: 'brakeman',
name: 'Brakeman',
vendor: 'GitLab'
vendor: 'GitLab',
version: '1.0.1'
}
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