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 ...@@ -12,7 +12,15 @@ module Gitlab
image: location_data['image'], image: location_data['image'],
operating_system: location_data['operating_system'], operating_system: location_data['operating_system'],
package_name: location_data.dig('dependency', 'package', 'name'), 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 end
end end
......
...@@ -11,21 +11,27 @@ module Gitlab ...@@ -11,21 +11,27 @@ module Gitlab
attr_reader :package_name attr_reader :package_name
attr_reader :package_version 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 @image = image
@operating_system = operating_system @operating_system = operating_system
@package_name = package_name @package_name = package_name
@package_version = package_version @package_version = package_version
@default_branch_image = default_branch_image
end end
def fingerprint_data def fingerprint_data
"#{docker_image_name_without_tag}:#{package_name}" "#{docker_image_name_without_tag}:#{package_name}"
end end
def default_branch_image
return @default_branch_image if Feature.enabled?(:improved_container_scan_matching)
end
private private
def docker_image_name_without_tag 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) return image if version_semver_like?(version)
......
...@@ -21,7 +21,8 @@ ...@@ -21,7 +21,8 @@
"version": "2.24-11+deb9u3" "version": "2.24-11+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -57,7 +58,8 @@ ...@@ -57,7 +58,8 @@
"version": "2.24-11+deb9u3" "version": "2.24-11+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -92,7 +94,8 @@ ...@@ -92,7 +94,8 @@
"version": "2.24-11+deb9u3" "version": "2.24-11+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -127,7 +130,8 @@ ...@@ -127,7 +130,8 @@
"version": "2.24-11+deb9u3" "version": "2.24-11+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -162,7 +166,8 @@ ...@@ -162,7 +166,8 @@
"version": "0.168-1" "version": "0.168-1"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -197,7 +202,8 @@ ...@@ -197,7 +202,8 @@
"version": "2.24-11+deb9u3" "version": "2.24-11+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -232,7 +238,8 @@ ...@@ -232,7 +238,8 @@
"version": "3.3-1" "version": "3.3-1"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -268,7 +275,8 @@ ...@@ -268,7 +275,8 @@
"version": "5.24.1-3+deb9u3" "version": "5.24.1-3+deb9u3"
}, },
"operating_system": "debian:9", "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": [ "identifiers": [
{ {
...@@ -299,4 +307,4 @@ ...@@ -299,4 +307,4 @@
"type": "container_scanning", "type": "container_scanning",
"status": "success" "status": "success"
} }
} }
\ No newline at end of file
...@@ -3,17 +3,21 @@ ...@@ -3,17 +3,21 @@
require 'spec_helper' require 'spec_helper'
RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
let(:project) { artifact.project } let(:project) { create(:project, :repository) }
let(:pipeline) { artifact.job.pipeline } 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) } let(:report) { Gitlab::Ci::Reports::Security::Report.new(artifact.file_type, pipeline, 2.weeks.ago) }
before do before do
artifact.each_blob { |blob| described_class.parse!(blob, report) } artifact.each_blob { |blob| described_class.parse!(blob, report) }
stub_feature_flags(improved_container_scan_matching: false)
end end
describe '#parse!' do 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(: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 it "parses all identifiers and findings for unapproved vulnerabilities" do
expect(report.findings.length).to eq(8) expect(report.findings.length).to eq(8)
...@@ -30,7 +34,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -30,7 +34,8 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
image: image, image: image,
operating_system: 'debian:9', operating_system: 'debian:9',
package_name: 'glibc', package_name: 'glibc',
package_version: '2.24-11+deb9u3' package_version: '2.24-11+deb9u3',
default_branch_image: nil
) )
end end
...@@ -41,5 +46,45 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do ...@@ -41,5 +46,45 @@ RSpec.describe Gitlab::Ci::Parsers::Security::ContainerScanning do
it "adds report image's name to raw_metadata" 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) expect(Gitlab::Json.parse(report.findings.first.raw_metadata).dig('location', 'image')).to eq(image)
end 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
end end
...@@ -23,39 +23,73 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do ...@@ -23,39 +23,73 @@ RSpec.describe Gitlab::Ci::Reports::Security::Locations::ContainerScanning do
subject { described_class.new(**params) } subject { described_class.new(**params) }
specify do context 'with feature enabled' do
params[:image] = 'alpine:3.7.3' before do
expect(subject.fingerprint).to eq(sha1_of.call('alpine:3.7.3:glibc')) stub_feature_flags(improved_container_scan_matching: true)
end end
specify do where(:image, :default_branch_image, :expected_fingerprint_input) do
params[:image] = 'alpine:3.7' [
expect(subject.fingerprint).to eq(sha1_of.call('alpine:3.7:glibc')) ['alpine:3.7.3', nil, 'alpine:3.7.3:glibc'],
end ['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 with_them do
params[:image] = 'alpine:8101518288111119448185914762536722131810' let(:params) do
expect(subject.fingerprint).to eq(sha1_of.call('alpine:glibc')) {
end image: image,
default_branch_image: default_branch_image,
operating_system: 'debian:9',
package_name: 'glibc',
package_version: '1.2.3'
}
end
specify do specify { expect(subject.fingerprint).to eq(sha1_of.call(expected_fingerprint_input)) }
params[:image] = 'alpine:1.0.0-beta' end
expect(subject.fingerprint).to eq(sha1_of.call('alpine:1.0.0-beta:glibc'))
end end
specify do context 'with feature disabled' do
params[:image] = 'registry.gitlab.com/gitlab-org/security-products/analyzers/container-scanning/tmp:af864bd61230d3d694eb01d6205b268b4ad63ac0' before do
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/gitlab-org/security-products/analyzers/container-scanning/tmp:glibc')) stub_feature_flags(improved_container_scan_matching: false)
end end
specify do let(:params) 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')) image: 'registry.gitlab.com/group/project/feature:ec301f43f14a2b477806875e49cfc4d3fa0d22c3',
end 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 it 'ignores default_branch_image' 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/group/project/feature:glibc'))
expect(subject.fingerprint).to eq(sha1_of.call('registry.gitlab.com/gitlab-org/security-products/dast/webgoat-8.0@sha256:glibc')) end
end end
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