Commit 55924293 authored by Adam Cohen's avatar Adam Cohen Committed by Imre Farkas

Handle new Container Scanning report format

Deprecate old `clair-scanner` container scanning report format and
handle the new report format based on the Security Products Common
Format

Add conditional to switch container scanning formatting behaviour
based on report type

Update container scanning fixtures to include deprecated `clair-scanner`
report and Security Products Common Format
parent 348d09b1
---
title: Handle new Container Scanning report format
merge_request: 19123
author:
type: changed
...@@ -9,32 +9,35 @@ module Gitlab ...@@ -9,32 +9,35 @@ module Gitlab
DEPRECATED_REPORT_VERSION = "1.3".freeze DEPRECATED_REPORT_VERSION = "1.3".freeze
def parse!(json_data, report) def parse_report(json_data)
vulnerabilities = format_report(JSON.parse!(json_data)) report = super
vulnerabilities.each do |vulnerability| return format_deprecated_report(report) if deprecated?(report)
create_vulnerability(report, vulnerability, DEPRECATED_REPORT_VERSION)
end report
rescue JSON::ParserError
raise SecurityReportParserError, 'JSON parsing failed'
rescue
raise SecurityReportParserError, "#{report.type} security report parsing failed"
end end
private private
# Transforms the Clair JSON report into the expected format # Transforms the clair-scanner JSON report into the expected format
def format_report(data) # TODO: remove the following block when we no longer need to support legacy
vulnerabilities = data['vulnerabilities'] # clair-scanner data. See https://gitlab.com/gitlab-org/gitlab/issues/35442
def format_deprecated_report(data)
unapproved = data['unapproved'] unapproved = data['unapproved']
formatter = Formatters::ContainerScanning.new(data['image']) formatter = Formatters::DeprecatedContainerScanning.new(data['image'])
vulnerabilities.map do |vulnerability| vulnerabilities = data['vulnerabilities'].map do |vulnerability|
# We only report unapproved vulnerabilities # We only report unapproved vulnerabilities
next unless unapproved.include?(vulnerability['vulnerability']) next unless unapproved.include?(vulnerability['vulnerability'])
formatter.format(vulnerability) formatter.format(vulnerability)
end.compact end.compact
{ "vulnerabilities" => vulnerabilities, "version" => DEPRECATED_REPORT_VERSION }
end
def deprecated?(data)
data['image']
end end
def create_location(location_data) def create_location(location_data)
......
# frozen_string_literal: true # frozen_string_literal: true
# TODO: remove this class when we no longer need to support legacy
# clair-scanner data. See https://gitlab.com/gitlab-org/gitlab/issues/35442
module Gitlab module Gitlab
module Ci module Ci
module Parsers module Parsers
module Security module Security
module Formatters module Formatters
class ContainerScanning class DeprecatedContainerScanning
def initialize(image) def initialize(image)
@image = image @image = image
end end
def format(vulnerability) def format(vulnerability)
formatted_vulnerability = FormattedContainerScanningVulnerability.new(vulnerability) formatted_vulnerability = DeprecatedFormattedContainerScanningVulnerability.new(vulnerability)
{ {
'category' => 'container_scanning', 'category' => 'container_scanning',
......
# frozen_string_literal: true # frozen_string_literal: true
# TODO: remove this class when we no longer need to support legacy
# clair-scanner data. See https://gitlab.com/gitlab-org/gitlab/issues/35442
module Gitlab module Gitlab
module Ci module Ci
module Parsers module Parsers
module Security module Security
module Formatters module Formatters
class FormattedContainerScanningVulnerability class DeprecatedFormattedContainerScanningVulnerability
def initialize(vulnerability) def initialize(vulnerability)
@vulnerability = vulnerability @vulnerability = vulnerability
end end
......
...@@ -72,6 +72,12 @@ FactoryBot.define do ...@@ -72,6 +72,12 @@ FactoryBot.define do
end end
end end
trait :deprecated_container_scanning_report do
after(:build) do |build|
build.job_artifacts << create(:ee_ci_job_artifact, :deprecated_container_scanning_report, job: build)
end
end
trait :dependency_scanning_feature_branch do trait :dependency_scanning_feature_branch do
after(:build) do |build| after(:build) do |build|
build.job_artifacts << create(:ee_ci_job_artifact, :dependency_scanning_feature_branch, job: build) build.job_artifacts << create(:ee_ci_job_artifact, :dependency_scanning_feature_branch, job: build)
......
...@@ -232,6 +232,16 @@ FactoryBot.define do ...@@ -232,6 +232,16 @@ FactoryBot.define do
end end
end end
trait :deprecated_container_scanning_report do
file_format { :raw }
file_type { :container_scanning }
after(:build) do |artifact, _|
artifact.file = fixture_file_upload(
Rails.root.join('ee/spec/fixtures/security_reports/deprecated/gl-container-scanning-report.json'), 'text/plain')
end
end
trait :metrics do trait :metrics do
file_format { :gzip } file_format { :gzip }
file_type { :metrics } file_type { :metrics }
......
...@@ -19,292 +19,309 @@ describe Security::PipelineVulnerabilitiesFinder do ...@@ -19,292 +19,309 @@ describe Security::PipelineVulnerabilitiesFinder do
end end
end end
describe '#execute' do let_it_be(:project) { create(:project, :repository) }
set(:project) { create(:project, :repository) } let_it_be(:pipeline) { create(:ci_pipeline, :success, project: project) }
set(:pipeline) { create(:ci_pipeline, :success, project: project) } let_it_be(:build_cs) { create(:ci_build, :success, name: 'cs_job', pipeline: pipeline, project: project) }
let(:params) { {} }
shared_examples_for 'a pipeline vulnerabilities finder' do
set(:build_cs) { create(:ci_build, :success, name: 'cs_job', pipeline: pipeline, project: project) } describe '#execute' do
set(:build_dast) { create(:ci_build, :success, name: 'dast_job', pipeline: pipeline, project: project) } let(:params) { {} }
set(:build_ds) { create(:ci_build, :success, name: 'ds_job', pipeline: pipeline, project: project) }
set(:build_sast) { create(:ci_build, :success, name: 'sast_job', pipeline: pipeline, project: project) } let_it_be(:build_dast) { create(:ci_build, :success, name: 'dast_job', pipeline: pipeline, project: project) }
let_it_be(:build_ds) { create(:ci_build, :success, name: 'ds_job', pipeline: pipeline, project: project) }
set(:artifact_cs) { create(:ee_ci_job_artifact, :container_scanning, job: build_cs, project: project) } let_it_be(:build_sast) { create(:ci_build, :success, name: 'sast_job', pipeline: pipeline, project: project) }
set(:artifact_dast) { create(:ee_ci_job_artifact, :dast, job: build_dast, project: project) }
set(:artifact_ds) { create(:ee_ci_job_artifact, :dependency_scanning, job: build_ds, project: project) } let_it_be(:artifact_dast) { create(:ee_ci_job_artifact, :dast, job: build_dast, project: project) }
set(:artifact_sast) { create(:ee_ci_job_artifact, :sast, job: build_sast, project: project) } let_it_be(:artifact_ds) { create(:ee_ci_job_artifact, :dependency_scanning, job: build_ds, project: project) }
let_it_be(:artifact_sast) { create(:ee_ci_job_artifact, :sast, job: build_sast, project: project) }
let(:cs_count) { read_fixture(artifact_cs)['unapproved'].count }
let(:ds_count) { read_fixture(artifact_ds)['vulnerabilities'].count } let(:ds_count) { read_fixture(artifact_ds)['vulnerabilities'].count }
let(:sast_count) { read_fixture(artifact_sast)['vulnerabilities'].count } let(:sast_count) { read_fixture(artifact_sast)['vulnerabilities'].count }
let(:dast_count) do let(:dast_count) do
read_fixture(artifact_dast)['site'].sum do |site| read_fixture(artifact_dast)['site'].sum do |site|
site['alerts'].sum do |alert| site['alerts'].sum do |alert|
alert['instances'].size alert['instances'].size
end
end end
end end
end
before do
stub_licensed_features(sast: true, dependency_scanning: true, container_scanning: true, dast: true)
# Stub out deduplication, if not done the expectations will vary based on the fixtures (which may/may not have duplicates)
disable_deduplication
end
subject { described_class.new(pipeline: pipeline, params: params).execute } before do
stub_licensed_features(sast: true, dependency_scanning: true, container_scanning: true, dast: true)
# Stub out deduplication, if not done the expectations will vary based on the fixtures (which may/may not have duplicates)
disable_deduplication
end
it 'assigns commit sha to findings' do subject { described_class.new(pipeline: pipeline, params: params).execute }
expect(subject.map(&:sha).uniq).to eq [pipeline.sha]
end
context 'by order' do it 'assigns commit sha to findings' do
let(:params) { { report_type: %w[sast] } } expect(subject.map(&:sha).uniq).to eq [pipeline.sha]
let!(:high_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :high) }
let!(:critical_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :critical) }
let!(:critical_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :critical) }
let!(:unknown_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :unknown) }
let!(:unknown_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :unknown) }
let!(:unknown_low) { build(:vulnerabilities_occurrence, confidence: :low, severity: :unknown) }
it 'orders by severity and confidence' do
allow_any_instance_of(described_class).to receive(:filter).and_return([
unknown_low,
unknown_medium,
critical_high,
unknown_high,
critical_medium,
high_high
])
expect(subject).to eq([critical_high, critical_medium, high_high, unknown_high, unknown_medium, unknown_low])
end end
end
context 'by report type' do context 'by order' do
context 'when sast' do
let(:params) { { report_type: %w[sast] } } let(:params) { { report_type: %w[sast] } }
let(:sast_report_fingerprints) {pipeline.security_reports.reports['sast'].occurrences.map(&:location).map(&:fingerprint) } let!(:high_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :high) }
let!(:critical_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :critical) }
it 'includes only sast' do let!(:critical_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :critical) }
expect(subject.map(&:location_fingerprint)).to match_array(sast_report_fingerprints) let!(:unknown_high) { build(:vulnerabilities_occurrence, confidence: :high, severity: :unknown) }
expect(subject.count).to eq sast_count let!(:unknown_medium) { build(:vulnerabilities_occurrence, confidence: :medium, severity: :unknown) }
let!(:unknown_low) { build(:vulnerabilities_occurrence, confidence: :low, severity: :unknown) }
it 'orders by severity and confidence' do
allow_next_instance_of(described_class) do |pipeline_vulnerabilities_finder|
allow(pipeline_vulnerabilities_finder).to receive(:filter).and_return([
unknown_low,
unknown_medium,
critical_high,
unknown_high,
critical_medium,
high_high
])
expect(subject).to eq([critical_high, critical_medium, high_high, unknown_high, unknown_medium, unknown_low])
end
end end
end end
context 'when dependency_scanning' do context 'by report type' do
let(:params) { { report_type: %w[dependency_scanning] } } context 'when sast' do
let(:ds_report_fingerprints) {pipeline.security_reports.reports['dependency_scanning'].occurrences.map(&:location).map(&:fingerprint) } let(:params) { { report_type: %w[sast] } }
let(:sast_report_fingerprints) {pipeline.security_reports.reports['sast'].occurrences.map(&:location).map(&:fingerprint) }
it 'includes only dependency_scanning' do it 'includes only sast' do
expect(subject.map(&:location_fingerprint)).to match_array(ds_report_fingerprints) expect(subject.map(&:location_fingerprint)).to match_array(sast_report_fingerprints)
expect(subject.count).to eq ds_count expect(subject.count).to eq sast_count
end
end end
end
context 'when dast' do context 'when dependency_scanning' do
let(:params) { { report_type: %w[dast] } } let(:params) { { report_type: %w[dependency_scanning] } }
let(:dast_report_fingerprints) {pipeline.security_reports.reports['dast'].occurrences.map(&:location).map(&:fingerprint) } let(:ds_report_fingerprints) {pipeline.security_reports.reports['dependency_scanning'].occurrences.map(&:location).map(&:fingerprint) }
it 'includes only dast' do it 'includes only dependency_scanning' do
expect(subject.map(&:location_fingerprint)).to match_array(dast_report_fingerprints) expect(subject.map(&:location_fingerprint)).to match_array(ds_report_fingerprints)
expect(subject.count).to eq dast_count expect(subject.count).to eq ds_count
end
end end
end
context 'when container_scanning' do context 'when dast' do
let(:params) { { report_type: %w[container_scanning] } } let(:params) { { report_type: %w[dast] } }
let(:dast_report_fingerprints) {pipeline.security_reports.reports['dast'].occurrences.map(&:location).map(&:fingerprint) }
it 'includes only container_scanning' do it 'includes only dast' do
fingerprints = pipeline.security_reports.reports['container_scanning'].occurrences.map(&:location).map(&:fingerprint) expect(subject.map(&:location_fingerprint)).to match_array(dast_report_fingerprints)
expect(subject.map(&:location_fingerprint)).to match_array(fingerprints) expect(subject.count).to eq dast_count
expect(subject.count).to eq cs_count end
end end
end
end
context 'by scope' do context 'when container_scanning' do
let(:ds_occurrence) { pipeline.security_reports.reports["dependency_scanning"].occurrences.first } let(:params) { { report_type: %w[container_scanning] } }
let(:sast_occurrence) { pipeline.security_reports.reports["sast"].occurrences.first }
it 'includes only container_scanning' do
let!(:feedback) do fingerprints = pipeline.security_reports.reports['container_scanning'].occurrences.map(&:location).map(&:fingerprint)
[ expect(subject.map(&:location_fingerprint)).to match_array(fingerprints)
create( expect(subject.count).to eq cs_count
:vulnerability_feedback, end
:dismissal, end
:dependency_scanning,
project: project,
pipeline: pipeline,
project_fingerprint: ds_occurrence.project_fingerprint,
vulnerability_data: ds_occurrence.raw_metadata
),
create(
:vulnerability_feedback,
:dismissal,
:sast,
project: project,
pipeline: pipeline,
project_fingerprint: sast_occurrence.project_fingerprint,
vulnerability_data: sast_occurrence.raw_metadata
)
]
end end
context 'when unscoped' do context 'by scope' do
subject { described_class.new(pipeline: pipeline).execute } let(:ds_occurrence) { pipeline.security_reports.reports["dependency_scanning"].occurrences.first }
let(:sast_occurrence) { pipeline.security_reports.reports["sast"].occurrences.first }
let!(:feedback) do
[
create(
:vulnerability_feedback,
:dismissal,
:dependency_scanning,
project: project,
pipeline: pipeline,
project_fingerprint: ds_occurrence.project_fingerprint,
vulnerability_data: ds_occurrence.raw_metadata
),
create(
:vulnerability_feedback,
:dismissal,
:sast,
project: project,
pipeline: pipeline,
project_fingerprint: sast_occurrence.project_fingerprint,
vulnerability_data: sast_occurrence.raw_metadata
)
]
end
context 'when unscoped' do
subject { described_class.new(pipeline: pipeline).execute }
it 'returns non-dismissed vulnerabilities' do it 'returns non-dismissed vulnerabilities' do
expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count - feedback.count expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count - feedback.count
expect(subject.map(&:project_fingerprint)).not_to include(*feedback.map(&:project_fingerprint)) expect(subject.map(&:project_fingerprint)).not_to include(*feedback.map(&:project_fingerprint))
end
end end
end
context 'when `dismissed`' do context 'when `dismissed`' do
subject { described_class.new(pipeline: pipeline, params: { report_type: %w[dependency_scanning], scope: 'dismissed' } ).execute } subject { described_class.new(pipeline: pipeline, params: { report_type: %w[dependency_scanning], scope: 'dismissed' } ).execute }
it 'returns non-dismissed vulnerabilities' do it 'returns non-dismissed vulnerabilities' do
expect(subject.count).to eq(ds_count - 1) expect(subject.count).to eq(ds_count - 1)
expect(subject.map(&:project_fingerprint)).not_to include(ds_occurrence.project_fingerprint) expect(subject.map(&:project_fingerprint)).not_to include(ds_occurrence.project_fingerprint)
end
end end
end
context 'when `all`' do context 'when `all`' do
let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scope: 'all' } } let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scope: 'all' } }
it 'returns all vulnerabilities' do it 'returns all vulnerabilities' do
expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count
end
end end
end end
end
context 'by severity' do context 'by severity' do
context 'when unscoped' do context 'when unscoped' do
subject { described_class.new(pipeline: pipeline).execute } subject { described_class.new(pipeline: pipeline).execute }
it 'returns all vulnerability severity levels' do it 'returns all vulnerability severity levels' do
expect(subject.map(&:severity).uniq).to match_array %w[undefined unknown low medium high critical info] expect(subject.map(&:severity).uniq).to match_array %w[undefined unknown low medium high critical info]
end
end end
end
context 'when `low`' do context 'when `low`' do
subject { described_class.new(pipeline: pipeline, params: { severity: 'low' } ).execute } subject { described_class.new(pipeline: pipeline, params: { severity: 'low' } ).execute }
it 'returns only low-severity vulnerabilities' do it 'returns only low-severity vulnerabilities' do
expect(subject.map(&:severity).uniq).to match_array %w[low] expect(subject.map(&:severity).uniq).to match_array %w[low]
end
end end
end end
end
context 'by confidence' do context 'by confidence' do
context 'when unscoped' do context 'when unscoped' do
subject { described_class.new(pipeline: pipeline).execute } subject { described_class.new(pipeline: pipeline).execute }
it 'returns all vulnerability confidence levels' do it 'returns all vulnerability confidence levels' do
expect(subject.map(&:confidence).uniq).to match_array %w[undefined unknown low medium high] expect(subject.map(&:confidence).uniq).to match_array %w[undefined unknown low medium high]
end
end end
end
context 'when `medium`' do context 'when `medium`' do
subject { described_class.new(pipeline: pipeline, params: { confidence: 'medium' } ).execute } subject { described_class.new(pipeline: pipeline, params: { confidence: 'medium' } ).execute }
it 'returns only medium-confidence vulnerabilities' do it 'returns only medium-confidence vulnerabilities' do
expect(subject.map(&:confidence).uniq).to match_array %w[medium] expect(subject.map(&:confidence).uniq).to match_array %w[medium]
end
end end
end end
end
context 'by all filters' do context 'by all filters' do
context 'with found entity' do context 'with found entity' do
let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scope: 'all' } } let(:params) { { report_type: %w[sast dast container_scanning dependency_scanning], scope: 'all' } }
it 'filters by all params' do it 'filters by all params' do
expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count
expect(subject.map(&:confidence).uniq).to match_array %w[undefined unknown low medium high] expect(subject.map(&:confidence).uniq).to match_array %w[undefined unknown low medium high]
expect(subject.map(&:severity).uniq).to match_array %w[undefined unknown low medium high critical info] expect(subject.map(&:severity).uniq).to match_array %w[undefined unknown low medium high critical info]
end
end end
end
context 'without found entity' do context 'without found entity' do
let(:params) { { report_type: %w[code_quality] } } let(:params) { { report_type: %w[code_quality] } }
it 'did not find anything' do it 'did not find anything' do
is_expected.to be_empty is_expected.to be_empty
end
end end
end end
end
context 'without params' do context 'without params' do
subject { described_class.new(pipeline: pipeline).execute } subject { described_class.new(pipeline: pipeline).execute }
it 'returns all report_types' do it 'returns all report_types' do
expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count expect(subject.count).to eq cs_count + dast_count + ds_count + sast_count
end
end end
end
context 'when matching vulnerability records exist' do context 'when matching vulnerability records exist' do
before do before do
create(:vulnerabilities_finding, create(:vulnerabilities_finding,
:confirmed, :confirmed,
project: project, project: project,
report_type: 'sast', report_type: 'sast',
project_fingerprint: confirmed_fingerprint) project_fingerprint: confirmed_fingerprint)
create(:vulnerabilities_finding, create(:vulnerabilities_finding,
:resolved, :resolved,
project: project, project: project,
report_type: 'sast', report_type: 'sast',
project_fingerprint: resolved_fingerprint) project_fingerprint: resolved_fingerprint)
create(:vulnerabilities_finding, create(:vulnerabilities_finding,
:dismissed, :dismissed,
project: project, project: project,
report_type: 'sast', report_type: 'sast',
project_fingerprint: dismissed_fingerprint) project_fingerprint: dismissed_fingerprint)
end end
let(:confirmed_fingerprint) do let(:confirmed_fingerprint) do
Digest::SHA1.hexdigest( Digest::SHA1.hexdigest(
'python/hardcoded/hardcoded-tmp.py:52865813c884a507be1f152d654245af34aba8a391626d01f1ab6d3f52ec8779:B108') 'python/hardcoded/hardcoded-tmp.py:52865813c884a507be1f152d654245af34aba8a391626d01f1ab6d3f52ec8779:B108')
end end
let(:resolved_fingerprint) do let(:resolved_fingerprint) do
Digest::SHA1.hexdigest( Digest::SHA1.hexdigest(
'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM') 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:47:PREDICTABLE_RANDOM')
end end
let(:dismissed_fingerprint) do let(:dismissed_fingerprint) do
Digest::SHA1.hexdigest( Digest::SHA1.hexdigest(
'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM') 'groovy/src/main/java/com/gitlab/security_products/tests/App.groovy:41:PREDICTABLE_RANDOM')
end end
subject { described_class.new(pipeline: pipeline, params: { report_type: %w[sast], scope: 'all' }).execute } subject { described_class.new(pipeline: pipeline, params: { report_type: %w[sast], scope: 'all' }).execute }
it 'assigns vulnerability records to findings providing them with computed state' do it 'assigns vulnerability records to findings providing them with computed state' do
confirmed = subject.find { |f| f.project_fingerprint == confirmed_fingerprint } confirmed = subject.find { |f| f.project_fingerprint == confirmed_fingerprint }
resolved = subject.find { |f| f.project_fingerprint == resolved_fingerprint } resolved = subject.find { |f| f.project_fingerprint == resolved_fingerprint }
dismissed = subject.find { |f| f.project_fingerprint == dismissed_fingerprint } dismissed = subject.find { |f| f.project_fingerprint == dismissed_fingerprint }
expect(confirmed.state).to eq 'confirmed' expect(confirmed.state).to eq 'confirmed'
expect(resolved.state).to eq 'resolved' expect(resolved.state).to eq 'resolved'
expect(dismissed.state).to eq 'dismissed' expect(dismissed.state).to eq 'dismissed'
expect(subject - [confirmed, resolved, dismissed]).to all have_attributes(state: 'new') expect(subject - [confirmed, resolved, dismissed]).to all have_attributes(state: 'new')
end
end end
end
context 'when being tested for sort stability' do context 'when being tested for sort stability' do
let(:params) { { report_type: %w[sast] } } let(:params) { { report_type: %w[sast] } }
it 'maintains the order of the occurrences having the same severity and confidence' do it 'maintains the order of the occurrences having the same severity and confidence' do
select_proc = proc { |o| o.severity == 'medium' && o.confidence == 'high' } select_proc = proc { |o| o.severity == 'medium' && o.confidence == 'high' }
report_occurrences = pipeline.security_reports.reports['sast'].occurrences.select(&select_proc) report_occurrences = pipeline.security_reports.reports['sast'].occurrences.select(&select_proc)
found_occurrences = subject.select(&select_proc) found_occurrences = subject.select(&select_proc)
found_occurrences.each_with_index do |found, i| found_occurrences.each_with_index do |found, i|
expect(found.metadata['cve']).to eq(report_occurrences[i].compare_key) expect(found.metadata['cve']).to eq(report_occurrences[i].compare_key)
end
end end
end end
end
def read_fixture(fixture) def read_fixture(fixture)
JSON.parse(File.read(fixture.file.path)) JSON.parse(File.read(fixture.file.path))
end
end end
end end
context 'container_scanning' do
let_it_be(:artifact_cs) { create(:ee_ci_job_artifact, :container_scanning, job: build_cs, project: project) }
let(:cs_count) { read_fixture(artifact_cs)['vulnerabilities'].count }
it_behaves_like 'a pipeline vulnerabilities finder'
end
context 'deprecated container_scanning' do
let_it_be(:artifact_cs) { create(:ee_ci_job_artifact, :deprecated_container_scanning_report, job: build_cs, project: project) }
let(:cs_count) { read_fixture(artifact_cs)['unapproved'].count }
it_behaves_like 'a pipeline vulnerabilities finder'
end
end end
{
"image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff",
"unapproved": [
"CVE-2017-18269",
"CVE-2017-16997",
"CVE-2018-1000001",
"CVE-2016-10228",
"CVE-2018-18520",
"CVE-2010-4052",
"CVE-2018-16869",
"CVE-2018-18311"
],
"vulnerabilities": [
{
"featurename": "glibc",
"featureversion": "2.24-11+deb9u3",
"vulnerability": "CVE-2017-18269",
"namespace": "debian:9",
"description": "SSE2-optimized memmove implementation problem.",
"link": "https://security-tracker.debian.org/tracker/CVE-2017-18269",
"severity": "Defcon1",
"fixedby": "2.24-11+deb9u4"
},
{
"featurename": "glibc",
"featureversion": "2.24-11+deb9u3",
"vulnerability": "CVE-2017-16997",
"namespace": "debian:9",
"description": "elf/dl-load.c in the GNU C Library (aka glibc or libc6) 2.19 through 2.26 mishandles RPATH and RUNPATH containing $ORIGIN for a privileged (setuid or AT_SECURE) program, which allows local users to gain privileges via a Trojan horse library in the current working directory, related to the fillin_rpath and decompose_rpath functions. This is associated with misinterpretion of an empty RPATH/RUNPATH token as the \"./\" directory. NOTE: this configuration of RPATH/RUNPATH for a privileged program is apparently very uncommon; most likely, no such program is shipped with any common Linux distribution.",
"link": "https://security-tracker.debian.org/tracker/CVE-2017-16997",
"severity": "Critical",
"fixedby": ""
},
{
"featurename": "glibc",
"featureversion": "2.24-11+deb9u3",
"vulnerability": "CVE-2018-1000001",
"namespace": "debian:9",
"description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-1000001",
"severity": "High",
"fixedby": ""
},
{
"featurename": "glibc",
"featureversion": "2.24-11+deb9u3",
"vulnerability": "CVE-2016-10228",
"namespace": "debian:9",
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.25 and earlier, when invoked with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.",
"link": "https://security-tracker.debian.org/tracker/CVE-2016-10228",
"severity": "Medium",
"fixedby": ""
},
{
"featurename": "elfutils",
"featureversion": "0.168-1",
"vulnerability": "CVE-2018-18520",
"namespace": "debian:9",
"description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-18520",
"severity": "Low",
"fixedby": ""
},
{
"featurename": "glibc",
"featureversion": "2.24-11+deb9u3",
"vulnerability": "CVE-2010-4052",
"namespace": "debian:9",
"description": "Stack consumption vulnerability in the regcomp implementation in the GNU C Library (aka glibc or libc6) through 2.11.3, and 2.12.x through 2.12.2, allows context-dependent attackers to cause a denial of service (resource exhaustion) via a regular expression containing adjacent repetition operators, as demonstrated by a {10,}{10,}{10,}{10,} sequence in the proftpd.gnu.c exploit for ProFTPD.",
"link": "https://security-tracker.debian.org/tracker/CVE-2010-4052",
"severity": "Negligible",
"fixedby": ""
},
{
"featurename": "nettle",
"featureversion": "3.3-1",
"vulnerability": "CVE-2018-16869",
"namespace": "debian:9",
"description": "A Bleichenbacher type side-channel based padding oracle attack was found in the way nettle handles endian conversion of RSA decrypted PKCS#1 v1.5 data. An attacker who is able to run a process on the same physical core as the victim process, could use this flaw extract plaintext or in some cases downgrade any TLS connections to a vulnerable server.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-16869",
"severity": "Unknown",
"fixedby": ""
},
{
"featurename": "perl",
"featureversion": "5.24.1-3+deb9u4",
"vulnerability": "CVE-2018-18311",
"namespace": "debian:9",
"description": "Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-18311",
"severity": "Unknown",
"fixedby": "5.24.1-3+deb9u5"
},
{
"featurename": "foo",
"featureversion": "1.3",
"vulnerability": "CVE-2018-666",
"namespace": "debian:9",
"description": "Foo has a vulnerability nobody cares about and whitelist.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-666",
"severity": "Unknown",
"fixedby": "1.4"
}
]
}
{ {
"image": "registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff", "version": "2.3",
"unapproved": [
"CVE-2017-18269",
"CVE-2017-16997",
"CVE-2018-1000001",
"CVE-2016-10228",
"CVE-2018-18520",
"CVE-2010-4052",
"CVE-2018-16869",
"CVE-2018-18311"
],
"vulnerabilities": [ "vulnerabilities": [
{ {
"featurename": "glibc", "category": "container_scanning",
"featureversion": "2.24-11+deb9u3", "message": "CVE-2017-18269 in glibc",
"vulnerability": "CVE-2017-18269", "description": "An SSE2-optimized memmove implementation for i386 in sysdeps/i386/i686/multiarch/memcpy-sse2-unaligned.S in the GNU C Library (aka glibc or libc6) 2.21 through 2.27 does not correctly perform the overlapping memory check if the source memory range spans the middle of the address space, resulting in corrupt data being produced by the copy operation. This may disclose information to context-dependent attackers, or result in a denial of service, or, possibly, code execution.",
"namespace": "debian:9", "cve": "debian:9:glibc:CVE-2017-18269",
"description": "SSE2-optimized memmove implementation problem.", "severity": "Critical",
"link": "https://security-tracker.debian.org/tracker/CVE-2017-18269", "confidence": "Unknown",
"severity": "Defcon1", "solution": "Upgrade glibc from 2.24-11+deb9u3 to 2.24-11+deb9u4",
"fixedby": "2.24-11+deb9u4" "scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2017-18269",
"value": "CVE-2017-18269",
"url": "https://security-tracker.debian.org/tracker/CVE-2017-18269"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2017-18269"
}
]
}, },
{ {
"featurename": "glibc", "category": "container_scanning",
"featureversion": "2.24-11+deb9u3", "message": "CVE-2017-16997 in glibc",
"vulnerability": "CVE-2017-16997",
"namespace": "debian:9",
"description": "elf/dl-load.c in the GNU C Library (aka glibc or libc6) 2.19 through 2.26 mishandles RPATH and RUNPATH containing $ORIGIN for a privileged (setuid or AT_SECURE) program, which allows local users to gain privileges via a Trojan horse library in the current working directory, related to the fillin_rpath and decompose_rpath functions. This is associated with misinterpretion of an empty RPATH/RUNPATH token as the \"./\" directory. NOTE: this configuration of RPATH/RUNPATH for a privileged program is apparently very uncommon; most likely, no such program is shipped with any common Linux distribution.", "description": "elf/dl-load.c in the GNU C Library (aka glibc or libc6) 2.19 through 2.26 mishandles RPATH and RUNPATH containing $ORIGIN for a privileged (setuid or AT_SECURE) program, which allows local users to gain privileges via a Trojan horse library in the current working directory, related to the fillin_rpath and decompose_rpath functions. This is associated with misinterpretion of an empty RPATH/RUNPATH token as the \"./\" directory. NOTE: this configuration of RPATH/RUNPATH for a privileged program is apparently very uncommon; most likely, no such program is shipped with any common Linux distribution.",
"link": "https://security-tracker.debian.org/tracker/CVE-2017-16997", "cve": "debian:9:glibc:CVE-2017-16997",
"severity": "Critical", "severity": "Critical",
"fixedby": "" "confidence": "Unknown",
"solution": "Upgrade glibc from 2.24-11+deb9u3 to 2.24-11+deb9u4",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2017-16997",
"value": "CVE-2017-16997",
"url": "https://security-tracker.debian.org/tracker/CVE-2017-16997"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2017-16997"
}
]
}, },
{ {
"featurename": "glibc", "category": "container_scanning",
"featureversion": "2.24-11+deb9u3", "message": "CVE-2018-1000001 in glibc",
"vulnerability": "CVE-2018-1000001",
"namespace": "debian:9",
"description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.", "description": "In glibc 2.26 and earlier there is confusion in the usage of getcwd() by realpath() which can be used to write before the destination buffer leading to a buffer underflow and potential code execution.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-1000001", "cve": "debian:9:glibc:CVE-2018-1000001",
"severity": "High", "severity": "High",
"fixedby": "" "confidence": "Unknown",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-1000001",
"value": "CVE-2018-1000001",
"url": "https://security-tracker.debian.org/tracker/CVE-2018-1000001"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2018-1000001"
}
]
}, },
{ {
"featurename": "glibc", "category": "container_scanning",
"featureversion": "2.24-11+deb9u3", "message": "CVE-2016-10228 in glibc",
"vulnerability": "CVE-2016-10228",
"namespace": "debian:9",
"description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.25 and earlier, when invoked with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.", "description": "The iconv program in the GNU C Library (aka glibc or libc6) 2.25 and earlier, when invoked with the -c option, enters an infinite loop when processing invalid multi-byte input sequences, leading to a denial of service.",
"link": "https://security-tracker.debian.org/tracker/CVE-2016-10228", "cve": "debian:9:glibc:CVE-2016-10228",
"severity": "Medium", "severity": "Medium",
"fixedby": "" "confidence": "Unknown",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2016-10228",
"value": "CVE-2016-10228",
"url": "https://security-tracker.debian.org/tracker/CVE-2016-10228"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2016-10228"
}
]
}, },
{ {
"featurename": "elfutils", "category": "container_scanning",
"featureversion": "0.168-1", "message": "CVE-2018-18520 in elfutils",
"vulnerability": "CVE-2018-18520",
"namespace": "debian:9",
"description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.", "description": "An Invalid Memory Address Dereference exists in the function elf_end in libelf in elfutils through v0.174. Although eu-size is intended to support ar files inside ar files, handle_ar in size.c closes the outer ar file before handling all inner entries. The vulnerability allows attackers to cause a denial of service (application crash) with a crafted ELF file.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-18520", "cve": "debian:9:elfutils:CVE-2018-18520",
"severity": "Low", "severity": "Low",
"fixedby": "" "confidence": "Unknown",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "elfutils"
},
"version": "0.168-1"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-18520",
"value": "CVE-2018-18520",
"url": "https://security-tracker.debian.org/tracker/CVE-2018-18520"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2018-18520"
}
]
}, },
{ {
"featurename": "glibc", "category": "container_scanning",
"featureversion": "2.24-11+deb9u3", "message": "CVE-2010-4052 in glibc",
"vulnerability": "CVE-2010-4052",
"namespace": "debian:9",
"description": "Stack consumption vulnerability in the regcomp implementation in the GNU C Library (aka glibc or libc6) through 2.11.3, and 2.12.x through 2.12.2, allows context-dependent attackers to cause a denial of service (resource exhaustion) via a regular expression containing adjacent repetition operators, as demonstrated by a {10,}{10,}{10,}{10,} sequence in the proftpd.gnu.c exploit for ProFTPD.", "description": "Stack consumption vulnerability in the regcomp implementation in the GNU C Library (aka glibc or libc6) through 2.11.3, and 2.12.x through 2.12.2, allows context-dependent attackers to cause a denial of service (resource exhaustion) via a regular expression containing adjacent repetition operators, as demonstrated by a {10,}{10,}{10,}{10,} sequence in the proftpd.gnu.c exploit for ProFTPD.",
"link": "https://security-tracker.debian.org/tracker/CVE-2010-4052", "cve": "debian:9:glibc:CVE-2010-4052",
"severity": "Negligible", "severity": "Low",
"fixedby": "" "confidence": "Unknown",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "glibc"
},
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2010-4052",
"value": "CVE-2010-4052",
"url": "https://security-tracker.debian.org/tracker/CVE-2010-4052"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2010-4052"
}
]
}, },
{ {
"featurename": "nettle", "category": "container_scanning",
"featureversion": "3.3-1", "message": "CVE-2018-16869 in nettle",
"vulnerability": "CVE-2018-16869",
"namespace": "debian:9",
"description": "A Bleichenbacher type side-channel based padding oracle attack was found in the way nettle handles endian conversion of RSA decrypted PKCS#1 v1.5 data. An attacker who is able to run a process on the same physical core as the victim process, could use this flaw extract plaintext or in some cases downgrade any TLS connections to a vulnerable server.", "description": "A Bleichenbacher type side-channel based padding oracle attack was found in the way nettle handles endian conversion of RSA decrypted PKCS#1 v1.5 data. An attacker who is able to run a process on the same physical core as the victim process, could use this flaw extract plaintext or in some cases downgrade any TLS connections to a vulnerable server.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-16869", "cve": "debian:9:nettle:CVE-2018-16869",
"severity": "Unknown", "severity": "Unknown",
"fixedby": "" "confidence": "Unknown",
"scanner": {
"id": "klar",
"name": "klar"
},
"location": {
"dependency": {
"package": {
"name": "nettle"
},
"version": "3.3-1"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-16869",
"value": "CVE-2018-16869",
"url": "https://security-tracker.debian.org/tracker/CVE-2018-16869"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2018-16869"
}
]
}, },
{ {
"featurename": "perl", "category": "container_scanning",
"featureversion": "5.24.1-3+deb9u4", "message": "CVE-2018-18311 in perl",
"vulnerability": "CVE-2018-18311",
"namespace": "debian:9",
"description": "Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.", "description": "Perl before 5.26.3 and 5.28.x before 5.28.1 has a buffer overflow via a crafted regular expression that triggers invalid write operations.",
"link": "https://security-tracker.debian.org/tracker/CVE-2018-18311", "cve": "debian:9:perl:CVE-2018-18311",
"severity": "Unknown", "severity": "Unknown",
"fixedby": "5.24.1-3+deb9u5" "confidence": "Unknown",
}, "solution": "Upgrade perl from 5.24.1-3+deb9u3 to 5.24.1-3+deb9u5",
{ "scanner": {
"featurename": "foo", "id": "klar",
"featureversion": "1.3", "name": "klar"
"vulnerability": "CVE-2018-666", },
"namespace": "debian:9", "location": {
"description": "Foo has a vulnerability nobody cares about and whitelist.", "dependency": {
"link": "https://security-tracker.debian.org/tracker/CVE-2018-666", "package": {
"severity": "Unknown", "name": "perl"
"fixedby": "1.4" },
"version": "5.24.1-3+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
},
"identifiers": [
{
"type": "cve",
"name": "CVE-2018-18311",
"value": "CVE-2018-18311",
"url": "https://security-tracker.debian.org/tracker/CVE-2018-18311"
}
],
"links": [
{
"url": "https://security-tracker.debian.org/tracker/CVE-2018-18311"
}
]
} }
] ],
"remediations": []
} }
...@@ -4,52 +4,52 @@ require 'spec_helper' ...@@ -4,52 +4,52 @@ require 'spec_helper'
describe Gitlab::Ci::Parsers::Security::ContainerScanning do describe Gitlab::Ci::Parsers::Security::ContainerScanning do
let(:parser) { described_class.new } let(:parser) { described_class.new }
let(:project) { artifact.project }
let(:pipeline) { artifact.job.pipeline }
let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline.sha) }
let(:clair_vulnerabilities) do before do
JSON.parse!( artifact.each_blob do |blob|
File.read( parser.parse!(blob, report)
Rails.root.join('ee/spec/fixtures/security_reports/master/gl-container-scanning-report.json') end
)
)['vulnerabilities']
end end
describe '#parse!' do describe '#parse!' do
let(:project) { artifact.project } using RSpec::Parameterized::TableSyntax
let(:pipeline) { artifact.job.pipeline }
let(:artifact) { create(:ee_ci_job_artifact, :container_scanning) }
let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline.sha) }
before do
artifact.each_blob do |blob|
parser.parse!(blob, report)
end
end
it "parses all identifiers and occurrences for unapproved vulnerabilities" do where(:report_type, :image, :version) do
expect(report.occurrences.length).to eq(8) :deprecated_container_scanning_report | 'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff' | '1.3'
expect(report.identifiers.length).to eq(8) :container_scanning | 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e' | '2.3'
expect(report.scanners.length).to eq(1)
end end
it 'generates expected location' do with_them do
location = report.occurrences.first.location let(:artifact) { create(:ee_ci_job_artifact, report_type) }
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning) it "parses all identifiers and occurrences for unapproved vulnerabilities" do
expect(location).to have_attributes( expect(report.occurrences.length).to eq(8)
image: 'registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff', expect(report.identifiers.length).to eq(8)
operating_system: 'debian:9', expect(report.scanners.length).to eq(1)
package_name: 'glibc', end
package_version: '2.24-11+deb9u3'
)
end
it "generates expected metadata_version" do it 'generates expected location' do
expect(report.occurrences.first.metadata_version).to eq('1.3') location = report.occurrences.first.location
end
it "adds report image's name to raw_metadata" do expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(JSON.parse(report.occurrences.first.raw_metadata).dig('location', 'image')) expect(location).to have_attributes(
.to eq('registry.gitlab.com/groulot/container-scanning-test/master:5f21de6956aee99ddb68ae49498662d9872f50ff') image: image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '2.24-11+deb9u3'
)
end
it "generates expected metadata_version" do
expect(report.occurrences.first.metadata_version).to eq(version)
end
it "adds report image's name to raw_metadata" do
expect(JSON.parse(report.occurrences.first.raw_metadata).dig('location', 'image')).to eq(image)
end
end end
end end
end end
...@@ -2,19 +2,19 @@ ...@@ -2,19 +2,19 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Parsers::Security::Formatters::ContainerScanning do describe Gitlab::Ci::Parsers::Security::Formatters::DeprecatedContainerScanning do
let(:raw_report) do
JSON.parse!(
File.read(
Rails.root.join('ee/spec/fixtures/security_reports/master/gl-container-scanning-report.json')
)
)
end
let(:vulnerability) { raw_report['vulnerabilities'].first } let(:vulnerability) { raw_report['vulnerabilities'].first }
describe '#format' do describe '#format' do
it 'format ZAP vulnerability into the 1.3 format' do let(:raw_report) do
JSON.parse!(
File.read(
Rails.root.join('ee/spec/fixtures/security_reports/deprecated/gl-container-scanning-report.json')
)
)
end
it 'formats the vulnerability into the 1.3 format' do
formatter = described_class.new('image_name') formatter = described_class.new('image_name')
expect(formatter.format(vulnerability)).to eq( { expect(formatter.format(vulnerability)).to eq( {
......
...@@ -2,11 +2,11 @@ ...@@ -2,11 +2,11 @@
require 'spec_helper' require 'spec_helper'
describe Gitlab::Ci::Parsers::Security::Formatters::FormattedContainerScanningVulnerability do describe Gitlab::Ci::Parsers::Security::Formatters::DeprecatedFormattedContainerScanningVulnerability do
let(:raw_report) do let(:raw_report) do
JSON.parse!( JSON.parse!(
File.read( File.read(
Rails.root.join('ee/spec/fixtures/security_reports/master/gl-container-scanning-report.json') Rails.root.join('ee/spec/fixtures/security_reports/deprecated/gl-container-scanning-report.json')
) )
) )
end end
......
...@@ -18,7 +18,7 @@ describe Ci::CompareContainerScanningReportsService do ...@@ -18,7 +18,7 @@ describe Ci::CompareContainerScanningReportsService do
let!(:base_pipeline) { create(:ee_ci_pipeline) } let!(:base_pipeline) { create(:ee_ci_pipeline) }
let!(:head_pipeline) { create(:ee_ci_pipeline, :with_container_scanning_report, project: project) } let!(:head_pipeline) { create(:ee_ci_pipeline, :with_container_scanning_report, project: project) }
it 'reports new licenses' do it 'reports new, existing and fixed vulnerabilities' do
expect(subject[:status]).to eq(:parsed) expect(subject[:status]).to eq(:parsed)
expect(subject[:data]['added'].count).to eq(8) expect(subject[:data]['added'].count).to eq(8)
expect(subject[:data]['existing'].count).to eq(0) expect(subject[:data]['existing'].count).to eq(0)
......
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