Commit 7bd96617 authored by Brian Williams's avatar Brian Williams

Add default_branch_image to container scanning location

Adds a a new field named default_branch_image, which indicates the name
of the scanned image as it appears on the default branch. On non-default
branches, we will use this in lieu of the image field when creating the
location fingerprint. This will allow vulnerabilities to generate
identical fingerprints when the image name differs between the default
branch and the non-default branch.
parent a5c05d69
---
name: improved_container_scan_matching
introduced_by_url: https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73486
rollout_issue_url: https://gitlab.com/gitlab-org/gitlab/-/issues/344534
milestone: '14.5'
type: development
group: group::container security
default_enabled: false
......@@ -12,7 +12,15 @@ module Gitlab
image: location_data['image'],
operating_system: location_data['operating_system'],
package_name: location_data.dig('dependency', 'package', 'name'),
package_version: location_data.dig('dependency', 'version'))
package_version: location_data.dig('dependency', 'version'),
default_branch_image: default_branch_image(location_data)
)
end
def default_branch_image(location_data)
return if @report.pipeline.default_branch?
location_data['default_branch_image']
end
end
end
......
......@@ -11,21 +11,27 @@ module Gitlab
attr_reader :package_name
attr_reader :package_version
def initialize(image:, operating_system:, package_name: nil, package_version: nil)
def initialize(image:, operating_system:, package_name: nil, package_version: nil, default_branch_image: nil)
@image = image
@operating_system = operating_system
@package_name = package_name
@package_version = package_version
@default_branch_image = default_branch_image
end
def fingerprint_data
"#{docker_image_name_without_tag}:#{package_name}"
end
def default_branch_image
return @default_branch_image if Feature.enabled?(:improved_container_scan_matching)
end
private
def docker_image_name_without_tag
base_name, version = image.split(':')
image_name = default_branch_image.presence || image
base_name, version = image_name.split(':')
return image if version_semver_like?(version)
......
......@@ -21,7 +21,8 @@
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -57,7 +58,8 @@
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -92,7 +94,8 @@
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -127,7 +130,8 @@
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -162,7 +166,8 @@
"version": "0.168-1"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -197,7 +202,8 @@
"version": "2.24-11+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -232,7 +238,8 @@
"version": "3.3-1"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -268,7 +275,8 @@
"version": "5.24.1-3+deb9u3"
},
"operating_system": "debian:9",
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e"
"image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e",
"default_branch_image": "registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest"
},
"identifiers": [
{
......@@ -299,4 +307,4 @@
"type": "container_scanning",
"status": "success"
}
}
\ No newline at end of file
}
......@@ -3,17 +3,21 @@
require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
let(:project) { artifact.project }
let(:pipeline) { artifact.job.pipeline }
let(:project) { create(:project, :repository) }
let(:current_branch) { project.default_branch }
let(:pipeline) { create(:ci_pipeline, ref: current_branch, project: project) }
let(:job) { create(:ci_build, pipeline: pipeline)}
let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, 2.weeks.ago) }
before do
artifact.each_blob { |blob| described_class.parse!(blob, report) }
stub_feature_flags(improved_container_scan_matching: false)
end
describe '#parse!' do
let(:artifact) { create(:ee_ci_job_artifact, :container_scanning) }
let(:artifact) { create(:ee_ci_job_artifact, :container_scanning, job: job) }
let(:image) { 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e' }
let(:default_branch_image) { 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0:latest' }
it "parses all identifiers and findings for unapproved vulnerabilities" do
expect(report.findings.length).to eq(8)
......@@ -30,7 +34,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
image: image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '2.24-11+deb9u3'
package_version: '2.24-11+deb9u3',
default_branch_image: nil
)
end
......@@ -41,5 +46,45 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
it "adds report image's name to raw_metadata" do
expect(Gitlab::Json.parse(report.findings.first.raw_metadata).dig('location', 'image')).to eq(image)
end
context 'with improved_container_scan_matching' do
before do
stub_feature_flags(improved_container_scan_matching: true)
end
context 'when on default branch' do
let(:current_branch) { project.default_branch }
it 'does not include default_branch_image in location' do
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes(
image: image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '2.24-11+deb9u3',
default_branch_image: nil
)
end
end
context 'when not on default branch' do
let(:current_branch) { 'not-default' }
it 'includes default_branch_image in location' do
location = report.findings.first.location
expect(location).to be_a(::Gitlab::Ci::Reports::Security::Locations::ContainerScanning)
expect(location).to have_attributes(
image: image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '2.24-11+deb9u3',
default_branch_image: default_branch_image
)
end
end
end
end
end
......@@ -23,39 +23,73 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
subject { described_class.new(**params) }
specify do
params[:image] = 'alpine:3.7.3'
expect(subject.fingerprint).to eq(sha1_of.call('alpine:3.7.3:glibc'))
end
context 'with feature enabled' do
before do
stub_feature_flags(improved_container_scan_matching: true)
end
specify do
params[:image] = 'alpine:3.7'
expect(subject.fingerprint).to eq(sha1_of.call('alpine:3.7:glibc'))
end
where(:image, :default_branch_image, :expected_fingerprint_input) do
[
['alpine:3.7.3', nil, 'alpine:3.7.3:glibc'],
['alpine:3.7', nil, 'alpine:3.7:glibc'],
['alpine:8101518288111119448185914762536722131810', nil, 'alpine:glibc'],
['alpine:1.0.0-beta', nil, 'alpine:1.0.0-beta:glibc'],
[
'registry.gitlab.com/group/project/tmp:af864bd61230d3d694eb01d6205b268b4ad63ac0',
nil,
'registry.gitlab.com/group/project/tmp:glibc'
],
[
'registry.gitlab.com/group/project/feature:5b1a4a921d7a50c3757aae3f7df2221878775af4',
'registry.gitlab.com/group/project/master:ec301f43f14a2b477806875e49cfc4d3fa0d22c3',
'registry.gitlab.com/group/project/master:glibc'
],
[
'registry.gitlab.com/group/project/feature:d6704dc0b8e33fb550a86f7847d6a3036d4f8bd5',
'registry.gitlab.com/group/project:latest',
'registry.gitlab.com/group/project:glibc'
],
[
'registry.gitlab.com/group/project@sha256:a418bbb80b9411f9a08025baa4681e192aaafd16505039bdcb113ccdb90a88fd',
'registry.gitlab.com/group/project:latest',
'registry.gitlab.com/group/project:glibc'
]
]
end
specify do
params[:image] = 'alpine:8101518288111119448185914762536722131810'
expect(subject.fingerprint).to eq(sha1_of.call('alpine:glibc'))
end
with_them do
let(:params) do
{
image: image,
default_branch_image: default_branch_image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '1.2.3'
}
end
specify do
params[:image] = 'alpine:1.0.0-beta'
expect(subject.fingerprint).to eq(sha1_of.call('alpine:1.0.0-beta:glibc'))
specify { expect(subject.fingerprint).to eq(sha1_of.call(expected_fingerprint_input)) }
end
end
specify do
params[:image] = 'registry.gitlab.com/gitlab-org/security-products/analyzers/container-scanning/tmp:af864bd61230d3d694eb01d6205b268b4ad63ac0'
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/gitlab-org/security-products/analyzers/container-scanning/tmp:glibc'))
end
context 'with feature disabled' do
before do
stub_feature_flags(improved_container_scan_matching: false)
end
specify do
params[:image] = 'registry.gitlab.com/gitlab-org/security-products/tests/container-scanning/master:ec301f43f14a2b477806875e49cfc4d3fa0d22c3'
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/gitlab-org/security-products/tests/container-scanning/master:glibc'))
end
let(:params) do
{
image: 'registry.gitlab.com/group/project/feature:ec301f43f14a2b477806875e49cfc4d3fa0d22c3',
default_branch_image: 'registry.gitlab.com/group/project/master:ec301f43f14a2b477806875e49cfc4d3fa0d22c3',
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '1.2.3'
}
end
specify do
params[:image] = 'registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:bc09fe2e0721dfaeee79364115aeedf2174cce0947b9ae5fe7c33312ee019a4e'
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:glibc'))
it 'ignores default_branch_image' do
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/group/project/feature:glibc'))
end
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